================ @@ -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(), + "use starts_with " + "instead of %select{find()|rfind()}0 %select{==|!=}1 0") + << Rev << Neg + << FixItHint::CreateReplacement(ComparisonExpr->getSourceRange(), ---------------- PiotrZSL wrote:
may cause problem with comparison ends with macro https://github.com/llvm/llvm-project/pull/72385 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits