10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
18 const auto SupportedContainers = hasType(
19 hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
20 hasAnyName(
"::std::set",
"::std::unordered_set",
"::std::map",
21 "::std::unordered_map",
"::std::multiset",
22 "::std::unordered_multiset",
"::std::multimap",
23 "::std::unordered_multimap"))))));
25 const auto CountCall =
26 cxxMemberCallExpr(on(SupportedContainers),
27 callee(cxxMethodDecl(hasName(
"count"))),
32 cxxMemberCallExpr(on(SupportedContainers),
33 callee(cxxMethodDecl(hasName(
"find"))),
37 const auto EndCall = cxxMemberCallExpr(on(SupportedContainers),
38 callee(cxxMethodDecl(hasName(
"end"))),
41 const auto Literal0 = integerLiteral(equals(0));
42 const auto Literal1 = integerLiteral(equals(1));
44 auto AddSimpleMatcher = [&](
auto Matcher) {
46 traverse(TK_IgnoreUnlessSpelledInSource, std::move(Matcher)),
this);
50 Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(booleanType()),
51 hasSourceExpression(CountCall))
52 .bind(
"positiveComparison"),
55 binaryOperator(hasLHS(CountCall), hasOperatorName(
"!="), hasRHS(Literal0))
56 .bind(
"positiveComparison"));
58 binaryOperator(hasLHS(Literal0), hasOperatorName(
"!="), hasRHS(CountCall))
59 .bind(
"positiveComparison"));
61 binaryOperator(hasLHS(CountCall), hasOperatorName(
">"), hasRHS(Literal0))
62 .bind(
"positiveComparison"));
64 binaryOperator(hasLHS(Literal0), hasOperatorName(
"<"), hasRHS(CountCall))
65 .bind(
"positiveComparison"));
67 binaryOperator(hasLHS(CountCall), hasOperatorName(
">="), hasRHS(Literal1))
68 .bind(
"positiveComparison"));
70 binaryOperator(hasLHS(Literal1), hasOperatorName(
"<="), hasRHS(CountCall))
71 .bind(
"positiveComparison"));
75 binaryOperator(hasLHS(CountCall), hasOperatorName(
"=="), hasRHS(Literal0))
76 .bind(
"negativeComparison"));
78 binaryOperator(hasLHS(Literal0), hasOperatorName(
"=="), hasRHS(CountCall))
79 .bind(
"negativeComparison"));
81 binaryOperator(hasLHS(CountCall), hasOperatorName(
"<="), hasRHS(Literal0))
82 .bind(
"negativeComparison"));
84 binaryOperator(hasLHS(Literal0), hasOperatorName(
">="), hasRHS(CountCall))
85 .bind(
"negativeComparison"));
87 binaryOperator(hasLHS(CountCall), hasOperatorName(
"<"), hasRHS(Literal1))
88 .bind(
"negativeComparison"));
90 binaryOperator(hasLHS(Literal1), hasOperatorName(
">"), hasRHS(CountCall))
91 .bind(
"negativeComparison"));
95 binaryOperator(hasLHS(FindCall), hasOperatorName(
"!="), hasRHS(EndCall))
96 .bind(
"positiveComparison"));
98 binaryOperator(hasLHS(FindCall), hasOperatorName(
"=="), hasRHS(EndCall))
99 .bind(
"negativeComparison"));
104 const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"call");
105 const auto *PositiveComparison =
106 Result.Nodes.getNodeAs<Expr>(
"positiveComparison");
107 const auto *NegativeComparison =
108 Result.Nodes.getNodeAs<Expr>(
"negativeComparison");
109 assert((!PositiveComparison || !NegativeComparison) &&
110 "only one of PositiveComparison or NegativeComparison should be set");
111 bool Negated = NegativeComparison !=
nullptr;
112 const auto *Comparison = Negated ? NegativeComparison : PositiveComparison;
116 diag(Call->getExprLoc(),
"use 'contains' to check for membership");
119 SourceLocation FuncCallLoc = Comparison->getEndLoc();
120 if (!FuncCallLoc.isValid() || FuncCallLoc.isMacroID())
124 const auto *Member = cast<MemberExpr>(Call->getCallee());
125 Diag << FixItHint::CreateReplacement(
126 Member->getMemberNameInfo().getSourceRange(),
"contains");
127 SourceLocation ComparisonBegin = Comparison->getSourceRange().getBegin();
128 SourceLocation ComparisonEnd = Comparison->getSourceRange().getEnd();
129 SourceLocation CallBegin = Call->getSourceRange().getBegin();
130 SourceLocation CallEnd = Call->getSourceRange().getEnd();
131 Diag << FixItHint::CreateReplacement(
132 CharSourceRange::getCharRange(ComparisonBegin, CallBegin),
134 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
135 CallEnd.getLocWithOffset(1), ComparisonEnd));
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Override this to register AST matchers with Finder.