11#include "../utils/ASTUtils.h"
12#include "../utils/Matchers.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/Lex/Lexer.h"
23 if (
const auto *Op = llvm::dyn_cast<BinaryOperator>(ComparisonExpr))
24 return Op->getOpcode() == BO_NE;
26 if (
const auto *Op = llvm::dyn_cast<CXXOperatorCallExpr>(ComparisonExpr))
27 return Op->getOperator() == OO_ExclaimEqual;
30 llvm::dyn_cast<CXXRewrittenBinaryOperator>(ComparisonExpr))
31 return Op->getOperator() == BO_NE;
39 :
ID(std::move(
ID)),
Node(std::move(
Node)), Context(Context) {}
40 bool operator()(
const internal::BoundNodesMap &Nodes)
const {
42 if (
const auto *StringLiteralNode = Nodes.getNodeAs<StringLiteral>(ID)) {
43 if (
const auto *IntegerLiteralSizeNode = Node.get<IntegerLiteral>()) {
44 return StringLiteralNode->getLength() !=
45 IntegerLiteralSizeNode->getValue().getZExtValue();
48 if (
const auto *StrlenNode = Node.get<CallExpr>()) {
49 if (StrlenNode->getDirectCallee()->getName() !=
"strlen" ||
50 StrlenNode->getNumArgs() != 1) {
54 if (
const auto *StrlenArgNode = dyn_cast<StringLiteral>(
55 StrlenNode->getArg(0)->IgnoreParenImpCasts())) {
56 return StrlenArgNode->getLength() != StringLiteralNode->getLength();
62 if (
const auto *ExprNode = Nodes.getNodeAs<Expr>(ID)) {
63 if (
const auto *MemberCallNode = Node.get<CXXMemberCallExpr>()) {
64 const CXXMethodDecl *MethodDeclNode = MemberCallNode->getMethodDecl();
65 const StringRef
Name = MethodDeclNode->getName();
66 if (!MethodDeclNode->isConst() || MethodDeclNode->getNumParams() != 0 ||
67 (
Name !=
"size" &&
Name !=
"length")) {
71 if (
const auto *OnNode =
72 dyn_cast<Expr>(MemberCallNode->getImplicitObjectArgument())) {
74 ExprNode->IgnoreParenImpCasts(),
91 ID, DynTypedNode::create(
Node), &(Finder->getASTContext())));
99 const auto ZeroLiteral = integerLiteral(equals(0));
101 const auto ClassTypeWithMethod = [](
const StringRef MethodBoundName,
102 const auto... Methods) {
103 return cxxRecordDecl(anyOf(
104 hasMethod(cxxMethodDecl(isConst(), parameterCountIs(1),
105 returns(booleanType()), hasAnyName(Methods))
106 .bind(MethodBoundName))...));
109 const auto OnClassWithStartsWithFunction =
110 ClassTypeWithMethod(
"starts_with_fun",
"starts_with",
"startsWith",
111 "startswith",
"StartsWith");
113 const auto OnClassWithEndsWithFunction = ClassTypeWithMethod(
114 "ends_with_fun",
"ends_with",
"endsWith",
"endswith",
"EndsWith");
117 const auto FindExpr = cxxMemberCallExpr(
118 anyOf(argumentCountIs(1), hasArgument(1, ZeroLiteral)),
120 cxxMethodDecl(hasName(
"find"), ofClass(OnClassWithStartsWithFunction))
122 hasArgument(0, expr().bind(
"needle")));
125 const auto RFindExpr = cxxMemberCallExpr(
126 hasArgument(1, ZeroLiteral),
127 callee(cxxMethodDecl(hasName(
"rfind"),
128 ofClass(OnClassWithStartsWithFunction))
130 hasArgument(0, expr().bind(
"needle")));
133 const auto CompareExpr = cxxMemberCallExpr(
134 argumentCountIs(3), hasArgument(0, ZeroLiteral),
135 callee(cxxMethodDecl(hasName(
"compare"),
136 ofClass(OnClassWithStartsWithFunction))
138 hasArgument(2, expr().bind(
"needle")),
139 hasArgument(1, lengthExprForStringNode(
"needle")));
142 const auto CompareEndsWithExpr = cxxMemberCallExpr(
144 callee(cxxMethodDecl(hasName(
"compare"),
145 ofClass(OnClassWithEndsWithFunction))
147 on(expr().bind(
"haystack")), hasArgument(2, expr().bind(
"needle")),
148 hasArgument(1, lengthExprForStringNode(
"needle")),
150 binaryOperator(hasOperatorName(
"-"),
151 hasLHS(lengthExprForStringNode(
"haystack")),
152 hasRHS(lengthExprForStringNode(
"needle")))));
157 matchers::isEqualityOperator(),
158 hasOperands(cxxMemberCallExpr(anyOf(FindExpr, RFindExpr, CompareExpr,
159 CompareEndsWithExpr))
168 matchers::isEqualityOperator(),
173 allOf(argumentCountIs(2),
176 anyOf(declRefExpr(to(varDecl(hasName(
"npos")))),
177 memberExpr(member(hasName(
"npos"))))))),
178 callee(cxxMethodDecl(hasName(
"rfind"),
179 ofClass(OnClassWithEndsWithFunction))
181 on(expr().bind(
"haystack")),
182 hasArgument(0, expr().bind(
"needle")))
184 binaryOperator(hasOperatorName(
"-"),
185 hasLHS(lengthExprForStringNode(
"haystack")),
186 hasRHS(lengthExprForStringNode(
"needle")))))
193 hasAnyOperatorName(
"==",
"!="),
195 expr().bind(
"needle"),
197 argumentCountIs(2), hasArgument(0, ZeroLiteral),
198 hasArgument(1, lengthExprForStringNode(
"needle")),
199 callee(cxxMethodDecl(hasName(
"substr"),
200 ofClass(OnClassWithStartsWithFunction))
208 const auto *ComparisonExpr = Result.Nodes.getNodeAs<Expr>(
"expr");
209 const auto *FindExpr = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"find_expr");
210 const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>(
"find_fun");
211 const auto *SearchExpr = Result.Nodes.getNodeAs<Expr>(
"needle");
212 const auto *StartsWithFunction =
213 Result.Nodes.getNodeAs<CXXMethodDecl>(
"starts_with_fun");
214 const auto *EndsWithFunction =
215 Result.Nodes.getNodeAs<CXXMethodDecl>(
"ends_with_fun");
216 assert(
bool(StartsWithFunction) !=
bool(EndsWithFunction));
218 const CXXMethodDecl *ReplacementFunction =
219 StartsWithFunction ? StartsWithFunction : EndsWithFunction;
221 if (ComparisonExpr->getBeginLoc().isMacroID() ||
222 FindExpr->getBeginLoc().isMacroID())
226 if (FindExpr->getNumArgs() == 0)
230 const auto SearchExprText = Lexer::getSourceText(
231 CharSourceRange::getTokenRange(SearchExpr->getSourceRange()),
232 *Result.SourceManager, Result.Context->getLangOpts());
234 auto Diagnostic =
diag(FindExpr->getExprLoc(),
"use %0 instead of %1")
235 << ReplacementFunction->getName() << FindFun->getName();
238 Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
239 ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc()));
242 Diagnostic << FixItHint::CreateReplacement(FindExpr->getExprLoc(),
243 ReplacementFunction->getName());
247 CharSourceRange::getTokenRange(FindExpr->getArg(0)->getBeginLoc(),
248 ComparisonExpr->getEndLoc()),
249 (SearchExprText +
")").str());
253 Diagnostic << FixItHint::CreateInsertion(FindExpr->getBeginLoc(),
"!");
llvm::SmallString< 256U > Name
CodeCompletionBuilder Builder
DiagnosticCallback Diagnostic
::clang::DynTypedNode Node
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.
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)
static bool isNegativeComparison(const Expr *ComparisonExpr)
AST_MATCHER_P(Expr, lengthExprForStringNode, std::string, ID)
bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt, const ASTContext &Context, bool Canonical)
bool operator()(const internal::BoundNodesMap &Nodes) const
NotLengthExprForStringNode(std::string ID, DynTypedNode Node, ASTContext *Context)