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"
16 using namespace clang::ast_matchers;
20 namespace readability {
22 ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef
Name,
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");
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()),
47 hasParent(unaryOperator(hasOperatorName(
"!")).bind(
"NegOnSize")),
49 hasParent(explicitCastExpr(hasDestinationType(booleanType()))));
53 on(expr(anyOf(hasType(namedDecl(stlContainer)),
54 hasType(pointsTo(namedDecl(stlContainer))),
55 hasType(references(namedDecl(stlContainer)))))
57 callee(cxxMethodDecl(hasName(
"size"))), WrongUse)
58 .bind(
"SizeCallExpr"),
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");
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()";
74 ReplacementText +=
".empty()";
77 bool Negation =
false;
78 const bool ContainerIsLHS =
79 !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
80 const auto OpCode = BinaryOp->getOpcode();
83 if (
const auto *Literal = llvm::dyn_cast<IntegerLiteral>(
84 BinaryOp->getRHS()->IgnoreImpCasts()))
85 Value = Literal->getValue().getLimitedValue();
90 llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts())
99 if (Value == 1 && (OpCode == BinaryOperatorKind::BO_EQ ||
100 OpCode == BinaryOperatorKind::BO_NE))
104 if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
105 (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
110 if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
111 (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
113 if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
114 (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
118 if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
120 if ((OpCode == BinaryOperatorKind::BO_GT ||
121 OpCode == BinaryOperatorKind::BO_GE) &&
124 if ((OpCode == BinaryOperatorKind::BO_LT ||
125 OpCode == BinaryOperatorKind::BO_LE) &&
130 ReplacementText =
"!" + ReplacementText;
131 Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
137 if (
const auto *UnaryOp =
138 Result.Nodes.getNodeAs<UnaryOperator>(
"NegOnSize"))
139 Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
142 Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
143 "!" + ReplacementText);
145 diag(MemberCall->getLocStart(),
"the 'empty' method should be used to check "
146 "for emptiness instead of 'size'")
LangOptions getLangOpts() const
Returns the language options from the context.
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
Base class for all clang-tidy checks.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
ClangTidyContext & Context
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.