diff --git a/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp b/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp --- a/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp +++ b/clang-tools-extra/clang-tidy/abseil/StringFindStartswithCheck.cpp @@ -40,12 +40,13 @@ auto StringFind = cxxMemberCallExpr( // .find()-call on a string... - callee(cxxMethodDecl(hasName("find"))), - on(hasType(StringType)), + callee(cxxMethodDecl(hasName("find"))), 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), hasArgument(1, cxxDefaultArgExpr()))); + anyOf(hasArgument(1, ZeroLiteral), hasArgument(1, cxxDefaultArgExpr())), + // ... and optionally a third argument for 'search string length'. + optionally(hasArgument(2, expr().bind("needle_length")))); Finder->addMatcher( // Match [=!]= with a zero on one side and a string.find on the other. @@ -69,6 +70,7 @@ const Expr *Haystack = Result.Nodes.getNodeAs("findexpr") ->getImplicitObjectArgument(); assert(Haystack != nullptr); + const auto *NeedleLength = Result.Nodes.getNodeAs("needle_length"); if (ComparisonExpr->getBeginLoc().isMacroID()) return; @@ -82,6 +84,20 @@ CharSourceRange::getTokenRange(Haystack->getSourceRange()), Source, Context.getLangOpts()); + // If there is a third argument (search string length), create an + // absl::string_view(search string, search string length) and use that + // as the search string in StartsWith. + std::string NewNeedleStr; + if (NeedleLength != nullptr) { + const StringRef NeedleLengthCode = Lexer::getSourceText( + CharSourceRange::getTokenRange(NeedleLength->getSourceRange()), Source, + Context.getLangOpts()); + NewNeedleStr = std::string("absl::string_view(") + NeedleExprCode.str() + + ", " + NeedleLengthCode.str() + ")"; + } else { + NewNeedleStr = NeedleExprCode.str(); + } + // Create the StartsWith string, negating if comparison was "!=". bool Neg = ComparisonExpr->getOpcodeStr() == "!="; StringRef StartswithStr; @@ -100,7 +116,7 @@ Diagnostic << FixItHint::CreateReplacement( ComparisonExpr->getSourceRange(), - (StartswithStr + "(" + HaystackExprCode + ", " + NeedleExprCode + ")") + (StartswithStr + "(" + HaystackExprCode + ", " + NewNeedleStr + ")") .str()); // Create a preprocessor #include FixIt hint (CreateIncludeInsertion checks diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-startswith.cpp b/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-startswith.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-startswith.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-startswith.cpp @@ -13,6 +13,7 @@ ~basic_string(); int find(basic_string s, int pos = 0); int find(const char *s, int pos = 0); + int find(const char *s, int pos, int n); }; typedef basic_string string; typedef basic_string wstring; @@ -41,6 +42,10 @@ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith // CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(s, s);{{$}} + s.find(s, 0) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith + // CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(s, s);{{$}} + s.find("aaa") != 0; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StartsWith // CHECK-FIXES: {{^[[:space:]]*}}!absl::StartsWith(s, "aaa");{{$}} @@ -61,9 +66,14 @@ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith // CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(s2, "a");{{$}} + s.find("a", 0, 1) == 0; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith + // CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(s, absl::string_view("a", 1));{{$}} + // expressions that don't trigger the check are here. A_MACRO(s.find("a"), 0); s.find("a", 1) == 0; s.find("a", 1) == 1; s.find("a") == 1; + s.find("a", 1, 1) == 0; }