101 const auto ZeroLiteral = integerLiteral(equals(0));
103 const auto ClassTypeWithMethod = [](
const StringRef MethodBoundName,
104 const auto... Methods) {
105 return cxxRecordDecl(anyOf(
106 hasMethod(cxxMethodDecl(isConst(), parameterCountIs(1),
107 returns(booleanType()), hasAnyName(Methods))
108 .bind(MethodBoundName))...));
111 const auto OnClassWithStartsWithFunction =
112 ClassTypeWithMethod(
"starts_with_fun",
"starts_with",
"startsWith",
113 "startswith",
"StartsWith");
115 const auto OnClassWithEndsWithFunction = ClassTypeWithMethod(
116 "ends_with_fun",
"ends_with",
"endsWith",
"endswith",
"EndsWith");
119 const auto FindExpr = cxxMemberCallExpr(
121 cxxMethodDecl(hasName(
"find"), ofClass(OnClassWithStartsWithFunction))
123 hasArgument(0, expr().bind(
"needle")),
128 allOf(argumentCountIs(2), hasArgument(1, ZeroLiteral)),
130 allOf(argumentCountIs(3), hasArgument(1, ZeroLiteral),
131 hasArgument(2, lengthExprForStringNode(
"needle")))));
134 const auto RFindExpr = cxxMemberCallExpr(
135 callee(cxxMethodDecl(hasName(
"rfind"),
136 ofClass(OnClassWithStartsWithFunction))
138 hasArgument(0, expr().bind(
"needle")),
141 allOf(argumentCountIs(2), hasArgument(1, ZeroLiteral)),
143 allOf(argumentCountIs(3), hasArgument(1, ZeroLiteral),
144 hasArgument(2, lengthExprForStringNode(
"needle")))));
147 const auto CompareExpr = cxxMemberCallExpr(
148 argumentCountIs(3), hasArgument(0, ZeroLiteral),
149 callee(cxxMethodDecl(hasName(
"compare"),
150 ofClass(OnClassWithStartsWithFunction))
152 hasArgument(2, expr().bind(
"needle")),
153 hasArgument(1, lengthExprForStringNode(
"needle")));
156 const auto CompareEndsWithExpr = cxxMemberCallExpr(
158 callee(cxxMethodDecl(hasName(
"compare"),
159 ofClass(OnClassWithEndsWithFunction))
161 on(expr().bind(
"haystack")), hasArgument(2, expr().bind(
"needle")),
162 hasArgument(1, lengthExprForStringNode(
"needle")),
164 binaryOperator(hasOperatorName(
"-"),
165 hasLHS(lengthExprForStringNode(
"haystack")),
166 hasRHS(lengthExprForStringNode(
"needle")))));
171 matchers::isEqualityOperator(),
172 hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr,
173 CompareEndsWithExpr))
182 matchers::isEqualityOperator(),
187 allOf(argumentCountIs(2),
190 anyOf(declRefExpr(to(varDecl(hasName(
"npos")))),
191 memberExpr(member(hasName(
"npos"))))))),
192 callee(cxxMethodDecl(hasName(
"rfind"),
193 ofClass(OnClassWithEndsWithFunction))
195 on(expr().bind(
"haystack")),
196 hasArgument(0, expr().bind(
"needle")))
198 binaryOperator(hasOperatorName(
"-"),
199 hasLHS(lengthExprForStringNode(
"haystack")),
200 hasRHS(lengthExprForStringNode(
"needle")))))
207 hasAnyOperatorName(
"==",
"!="),
209 expr().bind(
"needle"),
211 argumentCountIs(2), hasArgument(0, ZeroLiteral),
212 hasArgument(1, lengthExprForStringNode(
"needle")),
213 callee(cxxMethodDecl(hasName(
"substr"),
214 ofClass(OnClassWithStartsWithFunction))
222 const auto *ComparisonExpr = Result.Nodes.getNodeAs<Expr>(
"expr");
223 const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"find_expr");
224 const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>(
"find_fun");
225 const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>(
"needle");
226 const auto *StartsWithFunction =
227 Result.Nodes.getNodeAs<CXXMethodDecl>(
"starts_with_fun");
228 const auto *EndsWithFunction =
229 Result.Nodes.getNodeAs<CXXMethodDecl>(
"ends_with_fun");
230 assert(
bool(StartsWithFunction) !=
bool(EndsWithFunction));
232 const CXXMethodDecl *ReplacementFunction =
233 StartsWithFunction ? StartsWithFunction : EndsWithFunction;
235 if (ComparisonExpr->getBeginLoc().isMacroID() ||
236 FindExpr->getBeginLoc().isMacroID())
240 if (FindExpr->getNumArgs() == 0)
244 const auto SearchExprText = Lexer::getSourceText(
245 CharSourceRange::getTokenRange(SearchExpr->getSourceRange()),
246 *Result.SourceManager, Result.Context->getLangOpts());
248 const auto Diagnostic = diag(FindExpr->getExprLoc(),
"use %0 instead of %1")
249 << ReplacementFunction->getName()
250 << FindFun->getName();
253 Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
254 ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc()));
257 Diagnostic << FixItHint::CreateReplacement(FindExpr->getExprLoc(),
258 ReplacementFunction->getName());
261 Diagnostic << FixItHint::CreateReplacement(
262 CharSourceRange::getTokenRange(FindExpr->getArg(0)->getBeginLoc(),
263 ComparisonExpr->getEndLoc()),
264 (SearchExprText +
")").str());
268 Diagnostic << FixItHint::CreateInsertion(FindExpr->getBeginLoc(),
"!");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.