clang-tools  4.0.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 "../utils/Matchers.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Lex/Lexer.h"
14 #include "llvm/ADT/StringRef.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 ValidContainer = cxxRecordDecl(isSameOrDerivedFrom(
33  namedDecl(
34  has(cxxMethodDecl(
35  isConst(), parameterCountIs(0), isPublic(), hasName("size"),
36  returns(qualType(isInteger(), unless(booleanType()))))
37  .bind("size")),
38  has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
39  hasName("empty"), returns(booleanType()))
40  .bind("empty")))
41  .bind("container")));
42 
43  const auto WrongUse = anyOf(
44  hasParent(binaryOperator(
45  matchers::isComparisonOperator(),
46  hasEitherOperand(ignoringImpCasts(anyOf(
47  integerLiteral(equals(1)), integerLiteral(equals(0))))))
48  .bind("SizeBinaryOp")),
49  hasParent(implicitCastExpr(
50  hasImplicitDestinationType(booleanType()),
51  anyOf(
52  hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
53  anything()))),
54  hasParent(explicitCastExpr(hasDestinationType(booleanType()))));
55 
56  Finder->addMatcher(
57  cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer),
58  hasType(pointsTo(ValidContainer)),
59  hasType(references(ValidContainer))))
60  .bind("STLObject")),
61  callee(cxxMethodDecl(hasName("size"))), WrongUse,
62  unless(hasAncestor(cxxMethodDecl(
63  ofClass(equalsBoundNode("container"))))))
64  .bind("SizeCallExpr"),
65  this);
66 }
67 
68 void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
69  const auto *MemberCall =
70  Result.Nodes.getNodeAs<CXXMemberCallExpr>("SizeCallExpr");
71  const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("SizeBinaryOp");
72  const auto *E = Result.Nodes.getNodeAs<Expr>("STLObject");
73  FixItHint Hint;
74  std::string ReplacementText =
75  Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()),
76  *Result.SourceManager, getLangOpts());
77  if (E->getType()->isPointerType())
78  ReplacementText += "->empty()";
79  else
80  ReplacementText += ".empty()";
81 
82  if (BinaryOp) { // Determine the correct transformation.
83  bool Negation = false;
84  const bool ContainerIsLHS =
85  !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
86  const auto OpCode = BinaryOp->getOpcode();
87  uint64_t Value = 0;
88  if (ContainerIsLHS) {
89  if (const auto *Literal = llvm::dyn_cast<IntegerLiteral>(
90  BinaryOp->getRHS()->IgnoreImpCasts()))
91  Value = Literal->getValue().getLimitedValue();
92  else
93  return;
94  } else {
95  Value =
96  llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts())
97  ->getValue()
98  .getLimitedValue();
99  }
100 
101  // Constant that is not handled.
102  if (Value > 1)
103  return;
104 
105  if (Value == 1 && (OpCode == BinaryOperatorKind::BO_EQ ||
106  OpCode == BinaryOperatorKind::BO_NE))
107  return;
108 
109  // Always true, no warnings for that.
110  if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
111  (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
112  return;
113 
114  // Do not warn for size > 1, 1 < size, size <= 1, 1 >= size.
115  if (Value == 1) {
116  if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
117  (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
118  return;
119  if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
120  (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
121  return;
122  }
123 
124  if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
125  Negation = true;
126  if ((OpCode == BinaryOperatorKind::BO_GT ||
127  OpCode == BinaryOperatorKind::BO_GE) &&
128  ContainerIsLHS)
129  Negation = true;
130  if ((OpCode == BinaryOperatorKind::BO_LT ||
131  OpCode == BinaryOperatorKind::BO_LE) &&
132  !ContainerIsLHS)
133  Negation = true;
134 
135  if (Negation)
136  ReplacementText = "!" + ReplacementText;
137  Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
138  ReplacementText);
139 
140  } else {
141  // If there is a conversion above the size call to bool, it is safe to just
142  // replace size with empty.
143  if (const auto *UnaryOp =
144  Result.Nodes.getNodeAs<UnaryOperator>("NegOnSize"))
145  Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
146  ReplacementText);
147  else
148  Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
149  "!" + ReplacementText);
150  }
151 
152  diag(MemberCall->getLocStart(), "the 'empty' method should be used to check "
153  "for emptiness instead of 'size'")
154  << Hint;
155 
156  const auto *Container = Result.Nodes.getNodeAs<NamedDecl>("container");
157  const auto *Empty = Result.Nodes.getNodeAs<FunctionDecl>("empty");
158 
159  diag(Empty->getLocation(), "method %0::empty() defined here",
160  DiagnosticIDs::Note)
161  << Container;
162 }
163 
164 } // namespace readability
165 } // namespace tidy
166 } // namespace clang
const std::string Name
Definition: USRFinder.cpp:164
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
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:262
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
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:403
const NamedDecl * Result
Definition: USRFinder.cpp:162