Skip to content

Conversation

@HerrCai0907
Copy link
Contributor

Fixed: #97243

@llvmbot
Copy link
Member

llvmbot commented Jan 14, 2025

@llvm/pr-subscribers-clang-tidy

Author: Congcong Cai (HerrCai0907)

Changes

Fixed: #97243


Full diff: https://github.com/llvm/llvm-project/pull/122901.diff

4 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp (+21-7)
  • (modified) clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h (+1-1)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+4)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp (+13)
diff --git a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
index 7ec62f41aec019..bd9830278facb7 100644
--- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
@@ -10,6 +10,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
 
 using namespace clang::ast_matchers;
 
@@ -136,13 +137,26 @@ void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
 
 void RawStringLiteralCheck::replaceWithRawStringLiteral(
     const MatchFinder::MatchResult &Result, const StringLiteral *Literal,
-    StringRef Replacement) {
-  CharSourceRange CharRange = Lexer::makeFileCharRange(
-      CharSourceRange::getTokenRange(Literal->getSourceRange()),
-      *Result.SourceManager, getLangOpts());
-  diag(Literal->getBeginLoc(),
-       "escaped string literal can be written as a raw string literal")
-      << FixItHint::CreateReplacement(CharRange, Replacement);
+    std::string Replacement) {
+  DiagnosticBuilder Builder =
+      diag(Literal->getBeginLoc(),
+           "escaped string literal can be written as a raw string literal");
+  const SourceManager &SM = *Result.SourceManager;
+  const CharSourceRange TokenRange =
+      CharSourceRange::getTokenRange(Literal->getSourceRange());
+  Token T;
+  if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, getLangOpts()))
+    return;
+  const CharSourceRange CharRange =
+      Lexer::makeFileCharRange(TokenRange, SM, getLangOpts());
+  if (T.hasUDSuffix()) {
+    StringRef Text = Lexer::getSourceText(CharRange, SM, getLangOpts());
+    const size_t UDSuffixPos = Text.find_last_of('"');
+    if (UDSuffixPos == StringRef::npos)
+      return;
+    Replacement += Text.slice(UDSuffixPos + 1, Text.size());
+  }
+  Builder << FixItHint::CreateReplacement(CharRange, Replacement);
 }
 
 } // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
index aae58ca0e98d9a..6898e0624d1eb8 100644
--- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
@@ -35,7 +35,7 @@ class RawStringLiteralCheck : public ClangTidyCheck {
 private:
   void replaceWithRawStringLiteral(
       const ast_matchers::MatchFinder::MatchResult &Result,
-      const StringLiteral *Literal, StringRef Replacement);
+      const StringLiteral *Literal, std::string Replacement);
 
   std::string DelimiterStem;
   CharsBitSet DisallowedChars;
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3fe2f0ce01bccd..503d16bae021a8 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -317,6 +317,10 @@ Changes in existing checks
   a false positive when only an implicit conversion happened inside an
   initializer list.
 
+- Improved :doc:`modernize-raw-string-literal
+  <clang-tidy/checks/modernize/raw-string-literal>` check to fix incorrect auto
+  fix when the string contains a user-defined suffix.
+
 - Improved :doc:`modernize-use-designated-initializers
   <clang-tidy/checks/modernize/use-designated-initializers>` check to fix a
   crash when a class is declared but not defined.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp
index ad5d450036f2fd..5856b8882574a7 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp
@@ -129,3 +129,16 @@ void callFn() {
   // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} can be written as a raw string literal
   // CHECK-FIXES: {{^}}  fn<double>(R"(foo\bar)");{{$}}
 }
+
+namespace std {
+using size_t = decltype(sizeof(0));
+namespace ud {
+int operator""_abc(const char *str, std::size_t len);
+} // namespace ud
+} // namespace std
+namespace gh97243 {
+using namespace std::ud;
+auto UserDefinedLiteral = "foo\\bar"_abc;
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}auto UserDefinedLiteral = R"(foo\bar)"_abc;
+} // namespace gh97243

@llvmbot
Copy link
Member

llvmbot commented Jan 14, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Congcong Cai (HerrCai0907)

Changes

Fixed: #97243


Full diff: https://github.com/llvm/llvm-project/pull/122901.diff

4 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp (+21-7)
  • (modified) clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h (+1-1)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+4)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp (+13)
diff --git a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
index 7ec62f41aec019..bd9830278facb7 100644
--- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
@@ -10,6 +10,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
 
 using namespace clang::ast_matchers;
 
@@ -136,13 +137,26 @@ void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
 
 void RawStringLiteralCheck::replaceWithRawStringLiteral(
     const MatchFinder::MatchResult &Result, const StringLiteral *Literal,
-    StringRef Replacement) {
-  CharSourceRange CharRange = Lexer::makeFileCharRange(
-      CharSourceRange::getTokenRange(Literal->getSourceRange()),
-      *Result.SourceManager, getLangOpts());
-  diag(Literal->getBeginLoc(),
-       "escaped string literal can be written as a raw string literal")
-      << FixItHint::CreateReplacement(CharRange, Replacement);
+    std::string Replacement) {
+  DiagnosticBuilder Builder =
+      diag(Literal->getBeginLoc(),
+           "escaped string literal can be written as a raw string literal");
+  const SourceManager &SM = *Result.SourceManager;
+  const CharSourceRange TokenRange =
+      CharSourceRange::getTokenRange(Literal->getSourceRange());
+  Token T;
+  if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, getLangOpts()))
+    return;
+  const CharSourceRange CharRange =
+      Lexer::makeFileCharRange(TokenRange, SM, getLangOpts());
+  if (T.hasUDSuffix()) {
+    StringRef Text = Lexer::getSourceText(CharRange, SM, getLangOpts());
+    const size_t UDSuffixPos = Text.find_last_of('"');
+    if (UDSuffixPos == StringRef::npos)
+      return;
+    Replacement += Text.slice(UDSuffixPos + 1, Text.size());
+  }
+  Builder << FixItHint::CreateReplacement(CharRange, Replacement);
 }
 
 } // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
index aae58ca0e98d9a..6898e0624d1eb8 100644
--- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
@@ -35,7 +35,7 @@ class RawStringLiteralCheck : public ClangTidyCheck {
 private:
   void replaceWithRawStringLiteral(
       const ast_matchers::MatchFinder::MatchResult &Result,
-      const StringLiteral *Literal, StringRef Replacement);
+      const StringLiteral *Literal, std::string Replacement);
 
   std::string DelimiterStem;
   CharsBitSet DisallowedChars;
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3fe2f0ce01bccd..503d16bae021a8 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -317,6 +317,10 @@ Changes in existing checks
   a false positive when only an implicit conversion happened inside an
   initializer list.
 
+- Improved :doc:`modernize-raw-string-literal
+  <clang-tidy/checks/modernize/raw-string-literal>` check to fix incorrect auto
+  fix when the string contains a user-defined suffix.
+
 - Improved :doc:`modernize-use-designated-initializers
   <clang-tidy/checks/modernize/use-designated-initializers>` check to fix a
   crash when a class is declared but not defined.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp
index ad5d450036f2fd..5856b8882574a7 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/raw-string-literal.cpp
@@ -129,3 +129,16 @@ void callFn() {
   // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} can be written as a raw string literal
   // CHECK-FIXES: {{^}}  fn<double>(R"(foo\bar)");{{$}}
 }
+
+namespace std {
+using size_t = decltype(sizeof(0));
+namespace ud {
+int operator""_abc(const char *str, std::size_t len);
+} // namespace ud
+} // namespace std
+namespace gh97243 {
+using namespace std::ud;
+auto UserDefinedLiteral = "foo\\bar"_abc;
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}auto UserDefinedLiteral = R"(foo\bar)"_abc;
+} // namespace gh97243

Copy link
Contributor Author

@HerrCai0907 HerrCai0907 force-pushed the users/ccc01-14-_clang-tidy_fix_incorrect_auto-fix_for_the_string_contains_a_user-defined_suffix branch from 6cabc9d to 6cec0eb Compare January 14, 2025 14:53
@HerrCai0907 HerrCai0907 changed the title [clang-tidy]fix incorrect auto-fix for the string contains a user-defined suffix [clang-tidy]fix incorrect fix-it for the string contains a user-defined suffix Jan 15, 2025
Copy link
Contributor Author

HerrCai0907 commented Jan 17, 2025

Merge activity

  • Jan 17, 8:39 AM EST: A user started a stack merge that includes this pull request via Graphite.
  • Jan 17, 8:41 AM EST: A user merged this pull request with Graphite.

@HerrCai0907 HerrCai0907 merged commit 361f363 into main Jan 17, 2025
9 checks passed
@HerrCai0907 HerrCai0907 deleted the users/ccc01-14-_clang-tidy_fix_incorrect_auto-fix_for_the_string_contains_a_user-defined_suffix branch January 17, 2025 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

clang-tidy: modernize-raw-string-literal wrongly removes suffix, e.g. s (std::string_literals)

4 participants