10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "clang/Lex/Lexer.h"
13 #include "llvm/ADT/StringRef.h"
15 using namespace clang::ast_matchers;
18 static const char *
const ContainerNames[] = {
"std::array",
25 "std::priority_queue",
30 "std::unordered_multimap",
31 "std::unordered_multiset",
34 return std::binary_search(std::begin(ContainerNames),
35 std::end(ContainerNames), ClassName);
40 AST_MATCHER(QualType, isBoolType) {
return Node->isBooleanType(); }
43 return isContainer(Node.getQualifiedNameAsString());
48 namespace readability {
50 ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef
Name,
60 const auto WrongUse = anyOf(
63 anyOf(has(integerLiteral(equals(0))),
64 allOf(anyOf(hasOperatorName(
"<"), hasOperatorName(
">="),
65 hasOperatorName(
">"), hasOperatorName(
"<=")),
67 ignoringImpCasts(integerLiteral(equals(1)))))))
68 .bind(
"SizeBinaryOp")),
69 hasParent(implicitCastExpr(
70 hasImplicitDestinationType(isBoolType()),
72 hasParent(unaryOperator(hasOperatorName(
"!")).bind(
"NegOnSize")),
74 hasParent(explicitCastExpr(hasDestinationType(isBoolType()))));
78 on(expr(anyOf(hasType(namedDecl(stlContainer())),
79 hasType(pointsTo(namedDecl(stlContainer()))),
80 hasType(references(namedDecl(stlContainer())))))
82 callee(cxxMethodDecl(hasName(
"size"))), WrongUse)
83 .bind(
"SizeCallExpr"),
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");
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()";
99 ReplacementText +=
".empty()";
102 bool Negation =
false;
103 const bool ContainerIsLHS =
104 !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
105 const auto OpCode = BinaryOp->getOpcode();
107 if (ContainerIsLHS) {
108 if (
const auto *Literal = llvm::dyn_cast<IntegerLiteral>(
109 BinaryOp->getRHS()->IgnoreImpCasts()))
110 Value = Literal->getValue().getLimitedValue();
115 llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts())
125 if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
126 (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
131 if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
132 (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
134 if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
135 (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
139 if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
141 if ((OpCode == BinaryOperatorKind::BO_GT ||
142 OpCode == BinaryOperatorKind::BO_GE) &&
145 if ((OpCode == BinaryOperatorKind::BO_LT ||
146 OpCode == BinaryOperatorKind::BO_LE) &&
151 ReplacementText =
"!" + ReplacementText;
152 Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
158 if (
const auto *UnaryOp =
159 Result.Nodes.getNodeAs<UnaryOperator>(
"NegOnSize"))
160 Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
163 Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
164 "!" + ReplacementText);
166 diag(MemberCall->getLocStart(),
"the 'empty' method should be used to check "
167 "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.
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers 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.
static bool isContainer(llvm::StringRef ClassName)