clang-tools  3.9.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 #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,
24  : ClangTidyCheck(Name, Context) {}
25 
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
const std::string Name
Definition: USRFinder.cpp:140
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:170
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:210
Base class for all clang-tidy checks.
Definition: ClangTidy.h:110
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers 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:352
const NamedDecl * Result
Definition: USRFinder.cpp:137