102 const auto ZeroLiteral = integerLiteral(equals(0));
104 const auto ClassTypeWithMethod = [](
const StringRef MethodBoundName,
105 const auto... Methods) {
106 return cxxRecordDecl(anyOf(
107 hasMethod(cxxMethodDecl(isConst(), parameterCountIs(1),
108 returns(booleanType()), hasAnyName(Methods))
109 .bind(MethodBoundName))...));
112 const auto OnClassWithStartsWithFunction =
113 ClassTypeWithMethod(
"starts_with_fun",
"starts_with",
"startsWith",
114 "startswith",
"StartsWith");
116 const auto OnClassWithEndsWithFunction = ClassTypeWithMethod(
117 "ends_with_fun",
"ends_with",
"endsWith",
"endswith",
"EndsWith");
120 const auto FindExpr = cxxMemberCallExpr(
122 cxxMethodDecl(hasName(
"find"), ofClass(OnClassWithStartsWithFunction))
124 hasArgument(0, expr().bind(
"needle")),
129 allOf(argumentCountIs(2), hasArgument(1, ZeroLiteral)),
131 allOf(argumentCountIs(3), hasArgument(1, ZeroLiteral),
132 hasArgument(2, lengthExprForStringNode(
"needle")))));
135 const auto RFindExpr = cxxMemberCallExpr(
136 callee(cxxMethodDecl(hasName(
"rfind"),
137 ofClass(OnClassWithStartsWithFunction))
139 hasArgument(0, expr().bind(
"needle")),
142 allOf(argumentCountIs(2), hasArgument(1, ZeroLiteral)),
144 allOf(argumentCountIs(3), hasArgument(1, ZeroLiteral),
145 hasArgument(2, lengthExprForStringNode(
"needle")))));
148 const auto CompareExpr = cxxMemberCallExpr(
149 argumentCountIs(3), hasArgument(0, ZeroLiteral),
150 callee(cxxMethodDecl(hasName(
"compare"),
151 ofClass(OnClassWithStartsWithFunction))
153 hasArgument(2, expr().bind(
"needle")),
154 hasArgument(1, lengthExprForStringNode(
"needle")));
157 const auto CompareEndsWithExpr = cxxMemberCallExpr(
159 callee(cxxMethodDecl(hasName(
"compare"),
160 ofClass(OnClassWithEndsWithFunction))
162 on(expr().bind(
"haystack")), hasArgument(2, expr().bind(
"needle")),
163 hasArgument(1, lengthExprForStringNode(
"needle")),
165 binaryOperator(hasOperatorName(
"-"),
166 hasLHS(lengthExprForStringNode(
"haystack")),
167 hasRHS(lengthExprForStringNode(
"needle")))));
172 matchers::isEqualityOperator(),
173 hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr,
174 CompareEndsWithExpr))
183 matchers::isEqualityOperator(),
188 allOf(argumentCountIs(2),
191 anyOf(declRefExpr(to(varDecl(hasName(
"npos")))),
192 memberExpr(member(hasName(
"npos"))))))),
193 callee(cxxMethodDecl(hasName(
"rfind"),
194 ofClass(OnClassWithEndsWithFunction))
196 on(expr().bind(
"haystack")),
197 hasArgument(0, expr().bind(
"needle")))
199 binaryOperator(hasOperatorName(
"-"),
200 hasLHS(lengthExprForStringNode(
"haystack")),
201 hasRHS(lengthExprForStringNode(
"needle")))))
208 hasAnyOperatorName(
"==",
"!="),
210 expr().bind(
"needle"),
212 argumentCountIs(2), hasArgument(0, ZeroLiteral),
213 hasArgument(1, lengthExprForStringNode(
"needle")),
214 callee(cxxMethodDecl(hasName(
"substr"),
215 ofClass(OnClassWithStartsWithFunction))
223 const auto *ComparisonExpr = Result.Nodes.getNodeAs<Expr>(
"expr");
224 const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"find_expr");
225 const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>(
"find_fun");
226 const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>(
"needle");
227 const auto *StartsWithFunction =
228 Result.Nodes.getNodeAs<CXXMethodDecl>(
"starts_with_fun");
229 const auto *EndsWithFunction =
230 Result.Nodes.getNodeAs<CXXMethodDecl>(
"ends_with_fun");
231 assert(
bool(StartsWithFunction) !=
bool(EndsWithFunction));
233 const CXXMethodDecl *ReplacementFunction =
234 StartsWithFunction ? StartsWithFunction : EndsWithFunction;
236 if (ComparisonExpr->getBeginLoc().isMacroID() ||
237 FindExpr->getBeginLoc().isMacroID())
241 if (FindExpr->getNumArgs() == 0)
245 const auto SearchExprText = Lexer::getSourceText(
246 CharSourceRange::getTokenRange(SearchExpr->getSourceRange()),
247 *Result.SourceManager, Result.Context->getLangOpts());
249 auto Diagnostic = diag(FindExpr->getExprLoc(),
"use %0 instead of %1")
250 << ReplacementFunction->getName() << 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.