================
@@ -0,0 +1,144 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "StringViewConversionsCheck.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::performance {
+
+static auto getStringTypeMatcher(StringRef CharType) {
+  return hasCanonicalType(hasDeclaration(cxxRecordDecl(hasName(CharType))));
+}
+
+void StringViewConversionsCheck::registerMatchers(MatchFinder *Finder) {
+  // Matchers for std::basic_string[_view] families
+  // (includes std::string, std::wstring, std::u8string, etc.)
+  const auto IsStdString = getStringTypeMatcher("::std::basic_string");
+  const auto IsStdStringView = 
getStringTypeMatcher("::std::basic_string_view");
+
+  // Matches pointer to any character type (char*, const char*, wchar_t*, etc.)
+  // or array of any character type (char[], char[N], etc.)
+  const auto IsCharPointerOrArray =
+      anyOf(hasType(pointerType(pointee(isAnyCharacter()))),
+            hasType(arrayType(hasElementType(isAnyCharacter()))));
+
+  // Matches expressions that can be implicitly converted to string_view:
+  // - string_view itself
+  // - string literals ("hello", L"hello", u8"hello", etc.)
+  // - character pointers (const char*, char*)
+  // - character arrays (char[], char[N])
+  const auto ImplicitlyConvertibleToStringView =
+      expr(anyOf(hasType(IsStdStringView), stringLiteral(),
+                 IsCharPointerOrArray))
+          .bind("originalStringView");
+
+  // Matches std::string construction from an expression that is
+  // implicitly convertible to string_view.
+  // Excludes copy and move constructors to avoid false positives.
+  const auto RedundantStringConstruction = cxxConstructExpr(
+      hasType(IsStdString),
+      hasArgument(0, ignoringImplicit(ImplicitlyConvertibleToStringView)),
+      unless(hasDeclaration(cxxConstructorDecl(isCopyConstructor()))),
+      unless(hasDeclaration(cxxConstructorDecl(isMoveConstructor()))));
+
+  // Matches functional cast syntax: std::string(expr)
+  // This produces CXXFunctionalCastExpr in the AST.
+  const auto RedundantFunctionalCast =
+      cxxFunctionalCastExpr(hasType(IsStdString),
+                            hasDescendant(RedundantStringConstruction))
+          .bind("redundantExpr");
+
+  // Matches brace initialization syntax: std::string{expr}
+  // This produces CXXTemporaryObjectExpr in the AST.
+  const auto RedundantTemporaryObject =
+      cxxTemporaryObjectExpr(
+          hasType(IsStdString),
+          hasArgument(0, ignoringImplicit(ImplicitlyConvertibleToStringView)),
+          unless(hasDeclaration(cxxConstructorDecl(isCopyConstructor()))),
+          unless(hasDeclaration(cxxConstructorDecl(isMoveConstructor()))))
+          .bind("redundantExpr");
+
+  // Main matcher: finds function calls where an argument:
+  // 1. Has type string_view (after implicit conversions)
+  // 2. Contains a redundant std::string construction (either syntax)
+  // 3. Does NOT contain operator+ (which requires std::string operands,
+  //    so the conversion would not be redundant in that case)
+  //
+  // Example patterns detected:
+  //   foo(std::string(sv))     - string_view -> string -> string_view
+  //   foo(std::string{"lit"})  - literal -> string -> string_view
+  //
+  // Example patterns excluded:
+  //   foo(std::string(sv) + "bar")  - operator+ needs std::string
+  Finder->addMatcher(
+      callExpr(
+          forEachArgumentWithParam(
+              expr(hasType(IsStdStringView),
+                   hasDescendant(expr(anyOf(RedundantFunctionalCast,
+                                            RedundantTemporaryObject))),
+                   // Exclude cases where std::string is used with operator+
+                   // since string_view doesn't support concatenation.
+                   unless(hasDescendant(cxxOperatorCallExpr(
+                       hasOverloadedOperatorName("+"), hasType(IsStdString)))))
+                  .bind("expr"),
+              parmVarDecl(hasType(IsStdStringView))))
+          .bind("call"),
+      this);
+}
+
+void StringViewConversionsCheck::check(const MatchFinder::MatchResult &Result) 
{
+  // Get the full argument expression passed to the function.
+  // This has type string_view after implicit conversions.
+  const auto *ParamExpr = Result.Nodes.getNodeAs<Expr>("expr");
+  if (!ParamExpr)
+    return;
+
+  // Get the redundant std::string construction expression.
+  // This is either CXXFunctionalCastExpr for std::string(x) syntax
+  // or CXXTemporaryObjectExpr for std::string{x} syntax.
+  const auto *RedundantExpr = Result.Nodes.getNodeAs<Expr>("redundantExpr");
+  if (!RedundantExpr)
+    return;
+
+  // Get the original expression that was passed to std::string constructor.
+  // This is what we want to use as the replacement.
+  const auto *OriginalExpr = 
Result.Nodes.getNodeAs<Expr>("originalStringView");
+  if (!OriginalExpr)
+    return;
+
+  // Sanity check. Verify that the redundant expression is the direct source of
+  // the argument, not part of a larger expression (e.g., std::string(sv) +
+  // "bar"). If source ranges don't match, there's something between the string
+  // construction and the function argument, so we shouldn't transform.
+  if (ParamExpr->getSourceRange() != RedundantExpr->getSourceRange())
+    return;
----------------
localspook wrote:

If this isn't something we expect to ever happen, I think it makes more sense 
to `assert` it

https://github.com/llvm/llvm-project/pull/174288
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to