11#include "../utils/OptionsUtils.h"
12#include "clang/Lex/Lexer.h"
25 const auto ZeroLiteral = integerLiteral(equals(0));
26 const auto HasStartsWithMethodWithName = [](
const std::string &
Name) {
28 cxxMethodDecl(hasName(
Name), isConst(), parameterCountIs(1))
29 .bind(
"starts_with_fun"));
31 const auto HasStartsWithMethod =
32 anyOf(HasStartsWithMethodWithName(
"starts_with"),
33 HasStartsWithMethodWithName(
"startsWith"),
34 HasStartsWithMethodWithName(
"startswith"));
35 const auto ClassWithStartsWithFunction = cxxRecordDecl(anyOf(
36 HasStartsWithMethod, hasAnyBase(hasType(hasCanonicalType(hasDeclaration(
37 cxxRecordDecl(HasStartsWithMethod)))))));
39 const auto FindExpr = cxxMemberCallExpr(
41 anyOf(argumentCountIs(1), hasArgument(1, ZeroLiteral)),
43 callee(cxxMethodDecl(hasName(
"find")).bind(
"find_fun")),
46 hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
48 hasArgument(0, expr().bind(
"search_expr")));
50 const auto RFindExpr = cxxMemberCallExpr(
52 hasArgument(1, ZeroLiteral),
54 callee(cxxMethodDecl(hasName(
"rfind")).bind(
"find_fun")),
57 hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
59 hasArgument(0, expr().bind(
"search_expr")));
62 const auto HasStringLiteralAndLengthArgs = [](
const auto StringArgIndex,
63 const auto LengthArgIndex) {
65 hasArgument(StringArgIndex, stringLiteral().bind(
"string_literal_arg")),
66 hasArgument(LengthArgIndex,
67 anyOf(integerLiteral().bind(
"integer_literal_size_arg"),
68 callExpr(callee(functionDecl(parameterCountIs(1),
70 hasArgument(0, stringLiteral().bind(
75 const auto HasStringVariableAndSizeCallArgs = [](
const auto StringArgIndex,
76 const auto LengthArgIndex) {
78 hasArgument(StringArgIndex, declRefExpr(hasDeclaration(
79 decl().bind(
"string_var_decl")))),
80 hasArgument(LengthArgIndex,
82 callee(cxxMethodDecl(isConst(), parameterCountIs(0),
83 hasAnyName(
"size",
"length"))),
85 to(decl(equalsBoundNode(
"string_var_decl"))))))));
89 const auto HasStringAndLengthArgs =
90 [HasStringLiteralAndLengthArgs, HasStringVariableAndSizeCallArgs](
91 const auto StringArgIndex,
const auto LengthArgIndex) {
93 HasStringLiteralAndLengthArgs(StringArgIndex, LengthArgIndex),
94 HasStringVariableAndSizeCallArgs(StringArgIndex, LengthArgIndex));
97 const auto CompareExpr = cxxMemberCallExpr(
101 hasArgument(0, ZeroLiteral),
103 callee(cxxMethodDecl(hasName(
"compare")).bind(
"find_fun")),
106 hasCanonicalType(hasDeclaration(ClassWithStartsWithFunction)))),
108 HasStringAndLengthArgs(2, 1),
110 hasArgument(2, expr().bind(
"search_expr")));
115 hasAnyOperatorName(
"==",
"!="),
116 hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr))
124 const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>(
"expr");
125 const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"find_expr");
126 const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>(
"find_fun");
127 const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>(
"search_expr");
128 const auto *StartsWithFunction =
129 Result.Nodes.getNodeAs<CXXMethodDecl>(
"starts_with_fun");
131 const auto *StringLiteralArg =
132 Result.Nodes.getNodeAs<StringLiteral>(
"string_literal_arg");
133 const auto *IntegerLiteralSizeArg =
134 Result.Nodes.getNodeAs<IntegerLiteral>(
"integer_literal_size_arg");
135 const auto *StrlenArg = Result.Nodes.getNodeAs<StringLiteral>(
"strlen_arg");
138 if (StringLiteralArg && IntegerLiteralSizeArg &&
139 StringLiteralArg->getLength() !=
140 IntegerLiteralSizeArg->getValue().getZExtValue()) {
144 if (StringLiteralArg && StrlenArg &&
145 StringLiteralArg->getLength() != StrlenArg->getLength()) {
149 if (ComparisonExpr->getBeginLoc().isMacroID()) {
153 const bool Neg = ComparisonExpr->getOpcode() == BO_NE;
156 diag(FindExpr->getExprLoc(),
"use %0 instead of %1() %select{==|!=}2 0")
157 << StartsWithFunction->getName() << FindFun->getName() << Neg;
161 CharSourceRange::getTokenRange(
162 Lexer::getLocForEndOfToken(SearchExpr->getEndLoc(), 0,
164 ComparisonExpr->getEndLoc()),
168 Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
169 ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc()));
174 CharSourceRange::getCharRange(FindExpr->getExprLoc(),
175 SearchExpr->getBeginLoc()),
176 (StartsWithFunction->getName() +
"(").str());
180 Diagnostic << FixItHint::CreateInsertion(FindExpr->getBeginLoc(),
"!");
llvm::SmallString< 256U > Name
DiagnosticCallback Diagnostic
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
UseStartsEndsWithCheck(StringRef Name, ClangTidyContext *Context)