[clang-tools-extra] [clang-tidy] Enhance modernize-use-starts-ends-with to handle substr patterns (PR #116033)

Nicolas van Kempen via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 15 14:01:35 PST 2024


================
@@ -189,7 +203,54 @@ void UseStartsEndsWithCheck::check(const MatchFinder::MatchResult &Result) {
   if (ComparisonExpr->getBeginLoc().isMacroID())
     return;
 
-  const bool Neg = ComparisonExpr->getOpcode() == BO_NE;
+  bool Neg;
+  if (const auto *BO = llvm::dyn_cast<BinaryOperator>(ComparisonExpr)) {
+    Neg = BO->getOpcode() == BO_NE;
+  } else {
+    assert(llvm::isa<CXXOperatorCallExpr>(ComparisonExpr));
+    Neg = llvm::cast<CXXOperatorCallExpr>(ComparisonExpr)->getOperator() ==
+          OO_ExclaimEqual;
+  }
+
+  // Check if this is a substr case
+  bool IsSubstr = FindFun->getName() == "substr";
+
+  if (IsSubstr) {
+      const auto *SubstrCall = cast<CXXMemberCallExpr>(FindExpr);
+      const Expr *Object = SubstrCall->getImplicitObjectArgument();
+
+      std::string ObjectStr;
+      std::string SearchStr;
+      bool Invalid = false;
+
+      auto &SM = *Result.SourceManager;
+
+      CharSourceRange ObjectRange = CharSourceRange::getTokenRange(
+          Object->getBeginLoc(), Object->getEndLoc());
+      ObjectStr = Lexer::getSourceText(ObjectRange, SM, getLangOpts(), &Invalid).str();
+      if (Invalid)
+        return;
+
+      CharSourceRange SearchRange = CharSourceRange::getTokenRange(
+          SearchExpr->getBeginLoc(), SearchExpr->getEndLoc());
+      SearchStr = Lexer::getSourceText(SearchRange, SM, getLangOpts(), &Invalid).str();
+      if (Invalid)
+        return;
+
+      // Build the new expression: [!]Object.starts_with(SearchExpr)
+    std::string NewExpr =
+          (llvm::Twine(Neg ? "!" : "") + ObjectStr + "." +
+          ReplacementFunction->getName() + "(" + SearchStr + ")").str();
+    // Replace the entire comparison expression
+    auto Diag = diag(ComparisonExpr->getBeginLoc(),
+                    "use %0 instead of substr() %select{==|!=}1")
+                << ReplacementFunction->getName() << Neg;
+    Diag << FixItHint::CreateReplacement(
+        CharSourceRange::getTokenRange(ComparisonExpr->getSourceRange()),
+        NewExpr);
+      return;
+  }
+
----------------
nicovank wrote:

```cpp
// Remove everything before call.
Diagnostic << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
    ComparisonExpr->getBeginLoc(), FindExpr->getBeginLoc()));

// Rename function.
Diagnostic << FixItHint::CreateReplacement(FindExpr->getExprLoc(),
                                            ReplacementFunction->getName());

// Replace everything after the opening parenthesis with search expression.
const auto SearchExprText = Lexer::getSourceText(
    CharSourceRange::getTokenRange(SearchExpr->getSourceRange()),
    *Result.SourceManager, Result.Context->getLangOpts());
Diagnostic << FixItHint::CreateReplacement(
    CharSourceRange::getTokenRange(FindExpr->getArg(0)->getBeginLoc(),
                                    ComparisonExpr->getEndLoc()),
    (SearchExprText + ")").str());

// Add possible negation '!'.
if (Neg)
  Diagnostic << FixItHint::CreateInsertion(FindExpr->getBeginLoc(), "!");
```

I think this strategy might work, bit of a mix of both methods. Let me know what you think.

https://github.com/llvm/llvm-project/pull/116033


More information about the cfe-commits mailing list