Index: clang-tidy/readability/ContainerSizeEmptyCheck.cpp =================================================================== --- clang-tidy/readability/ContainerSizeEmptyCheck.cpp +++ clang-tidy/readability/ContainerSizeEmptyCheck.cpp @@ -29,11 +29,15 @@ if (!getLangOpts().CPlusPlus) return; - const auto stlContainer = hasAnyName( - "array", "basic_string", "deque", "forward_list", "list", "map", - "multimap", "multiset", "priority_queue", "queue", "set", "stack", - "unordered_map", "unordered_multimap", "unordered_multiset", - "unordered_set", "vector"); + const auto validContainer = + namedDecl(has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(), + hasName("size"), returns(isInteger()), + unless(returns(booleanType()))) + .bind("size")), + has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(), + hasName("empty"), returns(booleanType())) + .bind("empty"))) + .bind("container"); const auto WrongUse = anyOf( hasParent(binaryOperator( @@ -50,9 +54,9 @@ Finder->addMatcher( cxxMemberCallExpr( - on(expr(anyOf(hasType(namedDecl(stlContainer)), - hasType(pointsTo(namedDecl(stlContainer))), - hasType(references(namedDecl(stlContainer))))) + on(expr(anyOf(hasType(validContainer), + hasType(pointsTo(validContainer)), + hasType(references(validContainer)))) .bind("STLObject")), callee(cxxMethodDecl(hasName("size"))), WrongUse) .bind("SizeCallExpr"), @@ -142,9 +146,17 @@ Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(), "!" + ReplacementText); } + diag(MemberCall->getLocStart(), "the 'empty' method should be used to check " "for emptiness instead of 'size'") << Hint; + + const auto *Container = Result.Nodes.getNodeAs("container"); + const auto *Empty = Result.Nodes.getNodeAs("empty"); + + diag(Empty->getLocation(), "method %0::empty() defined here", + DiagnosticIDs::Note) + << Container; } } // namespace readability Index: docs/clang-tidy/checks/readability-container-size-empty.rst =================================================================== --- docs/clang-tidy/checks/readability-container-size-empty.rst +++ docs/clang-tidy/checks/readability-container-size-empty.rst @@ -11,6 +11,16 @@ instead of the ``size()`` method. It is not guaranteed that ``size()`` is a constant-time function, and it is generally more efficient and also shows clearer intent to use ``empty()``. Furthermore some containers may implement -the ``empty()`` method but not implement the ``size()`` method. Using ``empty()`` -whenever possible makes it easier to switch to another container in the -future. +the ``empty()`` method but not implement the ``size()`` method. Using +``empty()`` whenever possible makes it easier to switch to another container in +the future. + +The check issues warning if a container has ``size()`` and ``empty()`` methods +matching following signatures: + +code-block:: c++ + + size_type size() const; + bool empty() const; + +`size_type` can be any kind of integer type. Index: test/clang-tidy/clang-cl-driver.cpp =================================================================== --- /dev/null +++ test/clang-tidy/clang-cl-driver.cpp @@ -0,0 +1,17 @@ +// RUN: clang-tidy -checks=-*,modernize-use-nullptr %s -- --driver-mode=cl /DTEST1 /DFOO=foo /DBAR=bar | FileCheck -implicit-check-not="{{warning|error}}:" %s +int *a = 0; +// CHECK: :[[@LINE-1]]:10: warning: use nullptr +#ifdef TEST1 +int *b = 0; +// CHECK: :[[@LINE-1]]:10: warning: use nullptr +#endif +#define foo 1 +#define bar 1 +#if FOO +int *c = 0; +// CHECK: :[[@LINE-1]]:10: warning: use nullptr +#endif +#if BAR +int *d = 0; +// CHECK: :[[@LINE-1]]:10: warning: use nullptr +#endif Index: test/clang-tidy/readability-container-size-empty.cpp =================================================================== --- test/clang-tidy/readability-container-size-empty.cpp +++ test/clang-tidy/readability-container-size-empty.cpp @@ -24,9 +24,34 @@ }; } - } +template +class TemplatedContainer { +public: + int size() const; + bool empty() const; +}; + +template +class PrivateEmpty { +public: + int size() const; +private: + bool empty() const; +}; + +struct BoolSize { + bool size() const; + bool empty() const; +}; + +struct EnumSize { + enum E { one }; + enum E size() const; + bool empty() const; +}; + int main() { std::set intSet; std::string str; @@ -39,6 +64,7 @@ ; // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty] // CHECK-FIXES: {{^ }}if (intSet.empty()){{$}} + // CHECK-MESSAGES: :23:8: note: method 'set'::empty() defined here if (str.size() == 0) ; // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used @@ -127,6 +153,110 @@ ; // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used // CHECK-FIXES: {{^ }}if (vect4.empty()){{$}} + + TemplatedContainer templated_container; + if (templated_container.size() == 0) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}} + if (templated_container.size() != 0) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}} + if (0 == templated_container.size()) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}} + if (0 != templated_container.size()) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}} + if (templated_container.size() > 0) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}} + if (0 < templated_container.size()) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}} + if (templated_container.size() < 1) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}} + if (1 > templated_container.size()) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}} + if (templated_container.size() >= 1) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}} + if (1 <= templated_container.size()) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}} + if (templated_container.size() > 1) // no warning + ; + if (1 < templated_container.size()) // no warning + ; + if (templated_container.size() <= 1) // no warning + ; + if (1 >= templated_container.size()) // no warning + ; + if (!templated_container.size()) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}} + if (templated_container.size()) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}} + + if (templated_container.empty()) + ; + + // No warnings expected. + PrivateEmpty private_empty; + if (private_empty.size() == 0) + ; + if (private_empty.size() != 0) + ; + if (0 == private_empty.size()) + ; + if (0 != private_empty.size()) + ; + if (private_empty.size() > 0) + ; + if (0 < private_empty.size()) + ; + if (private_empty.size() < 1) + ; + if (1 > private_empty.size()) + ; + if (private_empty.size() >= 1) + ; + if (1 <= private_empty.size()) + ; + if (private_empty.size() > 1) + ; + if (1 < private_empty.size()) + ; + if (private_empty.size() <= 1) + ; + if (1 >= private_empty.size()) + ; + if (!private_empty.size()) + ; + if (private_empty.size()) + ; + + // Types with weird size() return type. + BoolSize bs; + if (bs.size() == 0) + ; + EnumSize es; + if (es.size() == 0) + ; } #define CHECKSIZE(x) if (x.size()) @@ -136,12 +266,22 @@ std::vector v; if (v.size()) ; - // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty] // CHECK-FIXES: {{^ }}if (!v.empty()){{$}} // CHECK-FIXES-NEXT: ; CHECKSIZE(v); // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: the 'empty' method should be used // CHECK-MESSAGES: CHECKSIZE(v); + + TemplatedContainer templated_container; + if (templated_container.size()) + ; + // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used + // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}} + // CHECK-FIXES-NEXT: ; + CHECKSIZE(templated_container); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: the 'empty' method should be used + // CHECK-MESSAGES: CHECKSIZE(templated_container); } void g() {