19 const auto Literal0 = integerLiteral(equals(0));
20 const auto Literal1 = integerLiteral(equals(1));
22 const auto ClassWithContains = cxxRecordDecl(
23 hasMethod(cxxMethodDecl(isConst(), parameterCountIs(1), isPublic(),
24 unless(isDeleted()), returns(booleanType()),
25 hasAnyName(
"contains",
"Contains"))
26 .bind(
"contains_fun")));
28 const auto CountCall =
29 cxxMemberCallExpr(argumentCountIs(1),
30 callee(cxxMethodDecl(hasAnyName(
"count",
"Count"),
31 ofClass(ClassWithContains))))
38 anyOf(argumentCountIs(1),
39 allOf(argumentCountIs(2), hasArgument(1, Literal0))),
40 callee(cxxMethodDecl(hasAnyName(
"find",
"Find"),
41 ofClass(ClassWithContains))))
44 const auto EndCall = cxxMemberCallExpr(
45 argumentCountIs(0), callee(cxxMethodDecl(hasAnyName(
"end",
"End"),
46 ofClass(ClassWithContains))));
48 const auto StringNpos = anyOf(declRefExpr(to(varDecl(hasName(
"npos")))),
49 memberExpr(member(hasName(
"npos"))));
53 implicitCastExpr(hasImplicitDestinationType(booleanType()),
54 hasSourceExpression(CountCall))
55 .bind(
"positiveComparison")),
58 const auto PositiveComparison =
59 anyOf(allOf(hasOperatorName(
"!="), hasOperands(CountCall, Literal0)),
60 allOf(hasLHS(CountCall), hasOperatorName(
">"), hasRHS(Literal0)),
61 allOf(hasLHS(Literal0), hasOperatorName(
"<"), hasRHS(CountCall)),
62 allOf(hasLHS(CountCall), hasOperatorName(
">="), hasRHS(Literal1)),
63 allOf(hasLHS(Literal1), hasOperatorName(
"<="), hasRHS(CountCall)),
64 allOf(hasOperatorName(
"!="),
65 hasOperands(FindCall, anyOf(EndCall, StringNpos))));
67 const auto NegativeComparison =
68 anyOf(allOf(hasOperatorName(
"=="), hasOperands(CountCall, Literal0)),
69 allOf(hasLHS(CountCall), hasOperatorName(
"<="), hasRHS(Literal0)),
70 allOf(hasLHS(Literal0), hasOperatorName(
">="), hasRHS(CountCall)),
71 allOf(hasLHS(CountCall), hasOperatorName(
"<"), hasRHS(Literal1)),
72 allOf(hasLHS(Literal1), hasOperatorName(
">"), hasRHS(CountCall)),
73 allOf(hasOperatorName(
"=="),
74 hasOperands(FindCall, anyOf(EndCall, StringNpos))));
78 anyOf(allOf(PositiveComparison, expr().bind(
"positiveComparison")),
79 allOf(NegativeComparison, expr().bind(
"negativeComparison")))),
85 const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"call");
86 const auto *PositiveComparison =
87 Result.Nodes.getNodeAs<Expr>(
"positiveComparison");
88 const auto *NegativeComparison =
89 Result.Nodes.getNodeAs<Expr>(
"negativeComparison");
90 assert((!PositiveComparison || !NegativeComparison) &&
91 "only one of PositiveComparison or NegativeComparison should be set");
92 const bool Negated = NegativeComparison !=
nullptr;
93 const auto *Comparison = Negated ? NegativeComparison : PositiveComparison;
94 const StringRef ContainsFunName =
95 Result.Nodes.getNodeAs<CXXMethodDecl>(
"contains_fun")->
getName();
96 const Expr *SearchExpr = Call->getArg(0)->IgnoreParenImpCasts();
99 auto Diag = diag(Call->getExprLoc(),
"use '%0' to check for membership")
103 const SourceLocation FuncCallLoc = Comparison->getEndLoc();
104 if (!FuncCallLoc.isValid() || FuncCallLoc.isMacroID())
107 const StringRef SearchExprText = Lexer::getSourceText(
108 CharSourceRange::getTokenRange(SearchExpr->getSourceRange()),
109 *Result.SourceManager, Result.Context->getLangOpts());
112 Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
113 Comparison->getBeginLoc(), Call->getBeginLoc()));
116 Diag << FixItHint::CreateReplacement(Call->getExprLoc(), ContainsFunName);
119 Diag << FixItHint::CreateReplacement(
120 CharSourceRange::getTokenRange(Call->getArg(0)->getBeginLoc(),
121 Comparison->getEndLoc()),
122 (SearchExprText +
")").str());
126 Diag << FixItHint::CreateInsertion(Call->getBeginLoc(),
"!");