clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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 //===----------------------------------------------------------------------===//
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
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,
51 ClangTidyContext *Context)
52 : ClangTidyCheck(Name, Context) {}
53
54 void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
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
clang::tidy::ClangTidyCheck::getLangOpts
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:162
clang::tidy::readability::ContainerSizeEmptyCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: ContainerSizeEmptyCheck.cpp:87
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:102
clang::tidy::AST_MATCHER
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt)
Definition: ProBoundsArrayToPointerDecayCheck.cpp:25
clang::tidy::readability::ContainerSizeEmptyCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
Definition: ContainerSizeEmptyCheck.cpp:54
Context
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:124
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:323
isContainer
static bool isContainer(llvm::StringRef ClassName)
Definition: ContainerSizeEmptyCheck.cpp:17
ContainerSizeEmptyCheck.h
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121