1 //===--- ContainerSizeEmptyCheck.cpp - clang-tidy -------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 #include "ContainerSizeEmptyCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchers.h" 12 #include "clang/Lex/Lexer.h" 13 #include "llvm/ADT/StringRef.h" 14 #include "../utils/Matchers.h" 15 16 using namespace clang::ast_matchers; 17 18 namespace clang { 19 namespace tidy { 20 namespace readability { 21 22 ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef Name, 23 ClangTidyContext *Context) 24 : ClangTidyCheck(Name, Context) {} 25 26 void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) { 27 // Only register the matchers for C++; the functionality currently does not 28 // provide any benefit to other languages, despite being benign. 29 if (!getLangOpts().CPlusPlus) 30 return; 31 32 const auto stlContainer = hasAnyName( 33 "array", "basic_string", "deque", "forward_list", "list", "map", 34 "multimap", "multiset", "priority_queue", "queue", "set", "stack", 35 "unordered_map", "unordered_multimap", "unordered_multiset", 36 "unordered_set", "vector"); 37 38 const auto WrongUse = anyOf( 39 hasParent(binaryOperator( 40 matchers::isComparisonOperator(), 41 hasEitherOperand(ignoringImpCasts(anyOf( 42 integerLiteral(equals(1)), integerLiteral(equals(0)))))) 43 .bind("SizeBinaryOp")), 44 hasParent(implicitCastExpr( 45 hasImplicitDestinationType(booleanType()), 46 anyOf( 47 hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")), 48 anything()))), 49 hasParent(explicitCastExpr(hasDestinationType(booleanType())))); 50 51 Finder->addMatcher( 52 cxxMemberCallExpr( 53 on(expr(anyOf(hasType(namedDecl(stlContainer)), 54 hasType(pointsTo(namedDecl(stlContainer))), 55 hasType(references(namedDecl(stlContainer))))) 56 .bind("STLObject")), 57 callee(cxxMethodDecl(hasName("size"))), WrongUse) 58 .bind("SizeCallExpr"), 59 this); 60 } 61 62 void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) { 63 const auto *MemberCall = 64 Result.Nodes.getNodeAs<CXXMemberCallExpr>("SizeCallExpr"); 65 const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("SizeBinaryOp"); 66 const auto *E = Result.Nodes.getNodeAs<Expr>("STLObject"); 67 FixItHint Hint; 68 std::string ReplacementText = Lexer::getSourceText( 69 CharSourceRange::getTokenRange(E->getSourceRange()), 70 *Result.SourceManager, Result.Context->getLangOpts()); 71 if (E->getType()->isPointerType()) 72 ReplacementText += "->empty()"; 73 else 74 ReplacementText += ".empty()"; 75 76 if (BinaryOp) { // Determine the correct transformation. 77 bool Negation = false; 78 const bool ContainerIsLHS = 79 !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts()); 80 const auto OpCode = BinaryOp->getOpcode(); 81 uint64_t Value = 0; 82 if (ContainerIsLHS) { 83 if (const auto *Literal = llvm::dyn_cast<IntegerLiteral>( 84 BinaryOp->getRHS()->IgnoreImpCasts())) 85 Value = Literal->getValue().getLimitedValue(); 86 else 87 return; 88 } else { 89 Value = 90 llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts()) 91 ->getValue() 92 .getLimitedValue(); 93 } 94 95 // Constant that is not handled. 96 if (Value > 1) 97 return; 98 99 if (Value == 1 && (OpCode == BinaryOperatorKind::BO_EQ || 100 OpCode == BinaryOperatorKind::BO_NE)) 101 return; 102 103 // Always true, no warnings for that. 104 if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) || 105 (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS)) 106 return; 107 108 // Do not warn for size > 1, 1 < size, size <= 1, 1 >= size. 109 if (Value == 1) { 110 if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) || 111 (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS)) 112 return; 113 if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) || 114 (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS)) 115 return; 116 } 117 118 if (OpCode == BinaryOperatorKind::BO_NE && Value == 0) 119 Negation = true; 120 if ((OpCode == BinaryOperatorKind::BO_GT || 121 OpCode == BinaryOperatorKind::BO_GE) && 122 ContainerIsLHS) 123 Negation = true; 124 if ((OpCode == BinaryOperatorKind::BO_LT || 125 OpCode == BinaryOperatorKind::BO_LE) && 126 !ContainerIsLHS) 127 Negation = true; 128 129 if (Negation) 130 ReplacementText = "!" + ReplacementText; 131 Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(), 132 ReplacementText); 133 134 } else { 135 // If there is a conversion above the size call to bool, it is safe to just 136 // replace size with empty. 137 if (const auto *UnaryOp = 138 Result.Nodes.getNodeAs<UnaryOperator>("NegOnSize")) 139 Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(), 140 ReplacementText); 141 else 142 Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(), 143 "!" + ReplacementText); 144 } 145 diag(MemberCall->getLocStart(), "the 'empty' method should be used to check " 146 "for emptiness instead of 'size'") 147 << Hint; 148 } 149 150 } // namespace readability 151 } // namespace tidy 152 } // namespace clang 153