[clang-tools-extra] [clang-tidy] Add new performance-use-starts-ends-with check (PR #72385)
Piotr Zegar via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 16 00:17:56 PST 2023
================
@@ -0,0 +1,106 @@
+//===--- UseStartsEndsWithCheck.cpp - clang-tidy --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseStartsEndsWithCheck.h"
+
+#include "../utils/OptionsUtils.h"
+#include "clang/Lex/Lexer.h"
+
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+
+const auto DefaultStringLikeClasses =
+ "::std::basic_string;::std::basic_string_view";
+
+UseStartsEndsWithCheck::UseStartsEndsWithCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ StringLikeClasses(utils::options::parseStringList(
+ Options.get("StringLikeClasses", DefaultStringLikeClasses))) {}
+
+void UseStartsEndsWithCheck::registerMatchers(MatchFinder *Finder) {
+ const auto ZeroLiteral = integerLiteral(equals(0));
+ const auto StringClassMatcher = cxxRecordDecl(hasAnyName(StringLikeClasses));
+ const auto StringType = hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(StringClassMatcher)));
+
+ const auto StringFind = cxxMemberCallExpr(
+ // .find()-call on a string...
+ callee(cxxMethodDecl(hasName("find")).bind("findfun")),
+ on(hasType(StringType)),
+ // ... with some search expression ...
+ hasArgument(0, expr().bind("needle")),
+ // ... and either "0" as second argument or the default argument (also 0).
+ anyOf(hasArgument(1, ZeroLiteral), argumentCountIs(1)));
+
+ const auto StringRFind = cxxMemberCallExpr(
+ // .rfind()-call on a string...
+ callee(cxxMethodDecl(hasName("rfind")).bind("rfindfun")),
+ on(hasType(StringType)),
+ // ... with some search expression ...
+ hasArgument(0, expr().bind("needle")),
+ // ... and "0" as second argument.
+ hasArgument(1, ZeroLiteral));
+
+ Finder->addMatcher(
+ // Match [=!]= with a zero on one side and a string.(r?)find on the other.
+ binaryOperator(
+ hasAnyOperatorName("==", "!="),
+ hasOperands(ZeroLiteral,
+ cxxMemberCallExpr(anyOf(StringFind, StringRFind))
+ .bind("findexpr")))
+ .bind("expr"),
+ this);
+}
+
+void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
+ const ASTContext &Context = *Result.Context;
+ const SourceManager &Source = Context.getSourceManager();
+
+ const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
+ const auto *Needle = Result.Nodes.getNodeAs<Expr>("needle");
+ const Expr *Haystack = Result.Nodes.getNodeAs<CXXMemberCallExpr>("findexpr")
+ ->getImplicitObjectArgument();
+ const auto *FindFun = Result.Nodes.getNodeAs<CXXMethodDecl>("findfun");
+ const auto *RFindFun = Result.Nodes.getNodeAs<CXXMethodDecl>("rfindfun");
+ assert(!FindFun != !RFindFun); // XOR.
+
+ if (ComparisonExpr->getBeginLoc().isMacroID()) {
+ return;
+ }
+
+ const StringRef NeedleExprCode = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Needle->getSourceRange()), Source,
+ Context.getLangOpts());
+ const StringRef HaystackExprCode = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Haystack->getSourceRange()), Source,
+ Context.getLangOpts());
+
+ const bool Rev = RFindFun != nullptr;
+ const bool Neg = ComparisonExpr->getOpcode() == BO_NE;
+ const std::string ReplacementCode = ((Neg ? "!" : "") + HaystackExprCode +
+ ".starts_with(" + NeedleExprCode + ")")
+ .str();
+
+ diag(ComparisonExpr->getBeginLoc(),
----------------
PiotrZSL wrote:
actually you could point here to place where call to find/rfind happen
https://github.com/llvm/llvm-project/pull/72385
More information about the cfe-commits
mailing list