clang-tools  3.8.0
ContainerSizeEmptyCheck.cpp
Go to the documentation of this file.
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 //===----------------------------------------------------------------------===//
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 
15 using namespace clang::ast_matchers;
16 
17 static bool isContainer(llvm::StringRef ClassName) {
18  static const char *const ContainerNames[] = {"std::array",
19  "std::deque",
20  "std::forward_list",
21  "std::list",
22  "std::map",
23  "std::multimap",
24  "std::multiset",
25  "std::priority_queue",
26  "std::queue",
27  "std::set",
28  "std::stack",
29  "std::unordered_map",
30  "std::unordered_multimap",
31  "std::unordered_multiset",
32  "std::unordered_set",
33  "std::vector"};
34  return std::binary_search(std::begin(ContainerNames),
35  std::end(ContainerNames), ClassName);
36 }
37 
38 namespace clang {
39 namespace {
40 AST_MATCHER(QualType, isBoolType) { return Node->isBooleanType(); }
41 
42 AST_MATCHER(NamedDecl, stlContainer) {
43  return isContainer(Node.getQualifiedNameAsString());
44 }
45 } // namespace
46 
47 namespace tidy {
48 namespace readability {
49 
50 ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef Name,
52  : ClangTidyCheck(Name, Context) {}
53 
55  // Only register the matchers for C++; the functionality currently does not
56  // provide any benefit to other languages, despite being benign.
57  if (!getLangOpts().CPlusPlus)
58  return;
59 
60  const auto WrongUse = anyOf(
61  hasParent(
62  binaryOperator(
63  anyOf(has(integerLiteral(equals(0))),
64  allOf(anyOf(hasOperatorName("<"), hasOperatorName(">="),
65  hasOperatorName(">"), hasOperatorName("<=")),
66  hasEitherOperand(
67  ignoringImpCasts(integerLiteral(equals(1)))))))
68  .bind("SizeBinaryOp")),
69  hasParent(implicitCastExpr(
70  hasImplicitDestinationType(isBoolType()),
71  anyOf(
72  hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
73  anything()))),
74  hasParent(explicitCastExpr(hasDestinationType(isBoolType()))));
75 
76  Finder->addMatcher(
77  cxxMemberCallExpr(
78  on(expr(anyOf(hasType(namedDecl(stlContainer())),
79  hasType(pointsTo(namedDecl(stlContainer()))),
80  hasType(references(namedDecl(stlContainer())))))
81  .bind("STLObject")),
82  callee(cxxMethodDecl(hasName("size"))), WrongUse)
83  .bind("SizeCallExpr"),
84  this);
85 }
86 
87 void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
88  const auto *MemberCall =
89  Result.Nodes.getNodeAs<CXXMemberCallExpr>("SizeCallExpr");
90  const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("SizeBinaryOp");
91  const auto *E = Result.Nodes.getNodeAs<Expr>("STLObject");
92  FixItHint Hint;
93  std::string ReplacementText = Lexer::getSourceText(
94  CharSourceRange::getTokenRange(E->getSourceRange()),
95  *Result.SourceManager, Result.Context->getLangOpts());
96  if (E->getType()->isPointerType())
97  ReplacementText += "->empty()";
98  else
99  ReplacementText += ".empty()";
100 
101  if (BinaryOp) { // Determine the correct transformation.
102  bool Negation = false;
103  const bool ContainerIsLHS =
104  !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
105  const auto OpCode = BinaryOp->getOpcode();
106  uint64_t Value = 0;
107  if (ContainerIsLHS) {
108  if (const auto *Literal = llvm::dyn_cast<IntegerLiteral>(
109  BinaryOp->getRHS()->IgnoreImpCasts()))
110  Value = Literal->getValue().getLimitedValue();
111  else
112  return;
113  } else {
114  Value =
115  llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts())
116  ->getValue()
117  .getLimitedValue();
118  }
119 
120  // Constant that is not handled.
121  if (Value > 1)
122  return;
123 
124  // Always true, no warnings for that.
125  if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
126  (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
127  return;
128 
129  // Do not warn for size > 1, 1 < size, size <= 1, 1 >= size.
130  if (Value == 1) {
131  if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
132  (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
133  return;
134  if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
135  (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
136  return;
137  }
138 
139  if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
140  Negation = true;
141  if ((OpCode == BinaryOperatorKind::BO_GT ||
142  OpCode == BinaryOperatorKind::BO_GE) &&
143  ContainerIsLHS)
144  Negation = true;
145  if ((OpCode == BinaryOperatorKind::BO_LT ||
146  OpCode == BinaryOperatorKind::BO_LE) &&
147  !ContainerIsLHS)
148  Negation = true;
149 
150  if (Negation)
151  ReplacementText = "!" + ReplacementText;
152  Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
153  ReplacementText);
154 
155  } else {
156  // If there is a conversion above the size call to bool, it is safe to just
157  // replace size with empty.
158  if (const auto *UnaryOp =
159  Result.Nodes.getNodeAs<UnaryOperator>("NegOnSize"))
160  Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
161  ReplacementText);
162  else
163  Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
164  "!" + ReplacementText);
165  }
166  diag(MemberCall->getLocStart(), "the 'empty' method should be used to check "
167  "for emptiness instead of 'size'")
168  << Hint;
169 }
170 
171 } // namespace readability
172 } // namespace tidy
173 } // namespace clang
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:162
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
StringHandle Name
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
Base class for all clang-tidy checks.
Definition: ClangTidy.h:102
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:323
static bool isContainer(llvm::StringRef ClassName)
const NamedDecl * Result
Definition: USRFinder.cpp:121