Skip to content

Conversation

@HerrCai0907
Copy link
Contributor

No description provided.

Copy link
Contributor Author

HerrCai0907 commented Jan 14, 2025

@llvmbot
Copy link
Member

llvmbot commented Jan 14, 2025

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

Author: Congcong Cai (HerrCai0907)

Changes

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

2 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp (+62-43)
  • (modified) clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h (-4)
diff --git a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
index bd9830278facb7..1618b5be7699d9 100644
--- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
@@ -9,8 +9,11 @@
 #include "RawStringLiteralCheck.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/StringRef.h"
+#include <optional>
 
 using namespace clang::ast_matchers;
 
@@ -67,20 +70,6 @@ bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
                         : (")" + Delimiter + R"(")")) != StringRef::npos;
 }
 
-std::string asRawStringLiteral(const StringLiteral *Literal,
-                               const std::string &DelimiterStem) {
-  const StringRef Bytes = Literal->getBytes();
-  std::string Delimiter;
-  for (int I = 0; containsDelimiter(Bytes, Delimiter); ++I) {
-    Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
-  }
-
-  if (Delimiter.empty())
-    return (R"(R"()" + Bytes + R"lit()")lit").str();
-
-  return (R"(R")" + Delimiter + "(" + Bytes + ")" + Delimiter + R"(")").str();
-}
-
 } // namespace
 
 RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name,
@@ -120,43 +109,73 @@ void RawStringLiteralCheck::registerMatchers(MatchFinder *Finder) {
       stringLiteral(unless(hasParent(predefinedExpr()))).bind("lit"), this);
 }
 
-void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
-  const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
-  if (Literal->getBeginLoc().isMacroID())
-    return;
-
-  if (containsEscapedCharacters(Result, Literal, DisallowedChars)) {
-    std::string Replacement = asRawStringLiteral(Literal, DelimiterStem);
-    if (ReplaceShorterLiterals ||
-        Replacement.length() <=
-            Lexer::MeasureTokenLength(Literal->getBeginLoc(),
-                                      *Result.SourceManager, getLangOpts()))
-      replaceWithRawStringLiteral(Result, Literal, Replacement);
-  }
-}
-
-void RawStringLiteralCheck::replaceWithRawStringLiteral(
-    const MatchFinder::MatchResult &Result, const StringLiteral *Literal,
-    std::string Replacement) {
-  DiagnosticBuilder Builder =
-      diag(Literal->getBeginLoc(),
-           "escaped string literal can be written as a raw string literal");
-  const SourceManager &SM = *Result.SourceManager;
+static std::optional<StringRef>
+createUserDefinedSuffix(const StringLiteral *Literal, const SourceManager &SM,
+                        const LangOptions &LangOpts) {
   const CharSourceRange TokenRange =
       CharSourceRange::getTokenRange(Literal->getSourceRange());
   Token T;
-  if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, getLangOpts()))
-    return;
+  if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, LangOpts))
+    return std::nullopt;
   const CharSourceRange CharRange =
-      Lexer::makeFileCharRange(TokenRange, SM, getLangOpts());
+      Lexer::makeFileCharRange(TokenRange, SM, LangOpts);
   if (T.hasUDSuffix()) {
-    StringRef Text = Lexer::getSourceText(CharRange, SM, getLangOpts());
+    StringRef Text = Lexer::getSourceText(CharRange, SM, LangOpts);
     const size_t UDSuffixPos = Text.find_last_of('"');
     if (UDSuffixPos == StringRef::npos)
-      return;
-    Replacement += Text.slice(UDSuffixPos + 1, Text.size());
+      return std::nullopt;
+    return Text.slice(UDSuffixPos + 1, Text.size());
+  }
+  return std::nullopt;
+}
+
+static std::string createRawStringLiteral(const StringLiteral *Literal,
+                                          const std::string &DelimiterStem,
+                                          const SourceManager &SM,
+                                          const LangOptions &LangOpts) {
+  const StringRef Bytes = Literal->getBytes();
+  std::string Delimiter;
+  for (int I = 0; containsDelimiter(Bytes, Delimiter); ++I) {
+    Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
+  }
+
+  std::optional<StringRef> UserDefinedSuffix =
+      createUserDefinedSuffix(Literal, SM, LangOpts);
+
+  if (Delimiter.empty())
+    return (R"(R"()" + Bytes + R"lit()")lit" + UserDefinedSuffix.value_or(""))
+        .str();
+
+  return (R"(R")" + Delimiter + "(" + Bytes + ")" + Delimiter + R"(")" +
+          UserDefinedSuffix.value_or(""))
+      .str();
+}
+
+static bool compareStringLength(StringRef Replacement,
+                                const StringLiteral *Literal,
+                                const SourceManager &SM,
+                                const LangOptions &LangOpts) {
+  return Replacement.size() <=
+         Lexer::MeasureTokenLength(Literal->getBeginLoc(), SM, LangOpts);
+}
+
+void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
+  if (Literal->getBeginLoc().isMacroID())
+    return;
+  SourceManager const &SM = *Result.SourceManager;
+  LangOptions const &LangOpts = getLangOpts();
+  if (containsEscapedCharacters(Result, Literal, DisallowedChars)) {
+    std::string Replacement =
+        createRawStringLiteral(Literal, DelimiterStem, SM, LangOpts);
+    if (ReplaceShorterLiterals ||
+        compareStringLength(Replacement, Literal, SM, LangOpts)) {
+      diag(Literal->getBeginLoc(),
+           "escaped string literal can be written as a raw string literal")
+          << FixItHint::CreateReplacement(Literal->getSourceRange(),
+                                          Replacement);
+    }
   }
-  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 6898e0624d1eb8..879255550dd5b6 100644
--- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
@@ -33,10 +33,6 @@ class RawStringLiteralCheck : public ClangTidyCheck {
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
 
 private:
-  void replaceWithRawStringLiteral(
-      const ast_matchers::MatchFinder::MatchResult &Result,
-      const StringLiteral *Literal, std::string Replacement);
-
   std::string DelimiterStem;
   CharsBitSet DisallowedChars;
   const bool ReplaceShorterLiterals;

@llvmbot
Copy link
Member

llvmbot commented Jan 14, 2025

@llvm/pr-subscribers-clang-tidy

Author: Congcong Cai (HerrCai0907)

Changes

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

2 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp (+62-43)
  • (modified) clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h (-4)
diff --git a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
index bd9830278facb7..1618b5be7699d9 100644
--- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp
@@ -9,8 +9,11 @@
 #include "RawStringLiteralCheck.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/StringRef.h"
+#include <optional>
 
 using namespace clang::ast_matchers;
 
@@ -67,20 +70,6 @@ bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
                         : (")" + Delimiter + R"(")")) != StringRef::npos;
 }
 
-std::string asRawStringLiteral(const StringLiteral *Literal,
-                               const std::string &DelimiterStem) {
-  const StringRef Bytes = Literal->getBytes();
-  std::string Delimiter;
-  for (int I = 0; containsDelimiter(Bytes, Delimiter); ++I) {
-    Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
-  }
-
-  if (Delimiter.empty())
-    return (R"(R"()" + Bytes + R"lit()")lit").str();
-
-  return (R"(R")" + Delimiter + "(" + Bytes + ")" + Delimiter + R"(")").str();
-}
-
 } // namespace
 
 RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name,
@@ -120,43 +109,73 @@ void RawStringLiteralCheck::registerMatchers(MatchFinder *Finder) {
       stringLiteral(unless(hasParent(predefinedExpr()))).bind("lit"), this);
 }
 
-void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
-  const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
-  if (Literal->getBeginLoc().isMacroID())
-    return;
-
-  if (containsEscapedCharacters(Result, Literal, DisallowedChars)) {
-    std::string Replacement = asRawStringLiteral(Literal, DelimiterStem);
-    if (ReplaceShorterLiterals ||
-        Replacement.length() <=
-            Lexer::MeasureTokenLength(Literal->getBeginLoc(),
-                                      *Result.SourceManager, getLangOpts()))
-      replaceWithRawStringLiteral(Result, Literal, Replacement);
-  }
-}
-
-void RawStringLiteralCheck::replaceWithRawStringLiteral(
-    const MatchFinder::MatchResult &Result, const StringLiteral *Literal,
-    std::string Replacement) {
-  DiagnosticBuilder Builder =
-      diag(Literal->getBeginLoc(),
-           "escaped string literal can be written as a raw string literal");
-  const SourceManager &SM = *Result.SourceManager;
+static std::optional<StringRef>
+createUserDefinedSuffix(const StringLiteral *Literal, const SourceManager &SM,
+                        const LangOptions &LangOpts) {
   const CharSourceRange TokenRange =
       CharSourceRange::getTokenRange(Literal->getSourceRange());
   Token T;
-  if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, getLangOpts()))
-    return;
+  if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, LangOpts))
+    return std::nullopt;
   const CharSourceRange CharRange =
-      Lexer::makeFileCharRange(TokenRange, SM, getLangOpts());
+      Lexer::makeFileCharRange(TokenRange, SM, LangOpts);
   if (T.hasUDSuffix()) {
-    StringRef Text = Lexer::getSourceText(CharRange, SM, getLangOpts());
+    StringRef Text = Lexer::getSourceText(CharRange, SM, LangOpts);
     const size_t UDSuffixPos = Text.find_last_of('"');
     if (UDSuffixPos == StringRef::npos)
-      return;
-    Replacement += Text.slice(UDSuffixPos + 1, Text.size());
+      return std::nullopt;
+    return Text.slice(UDSuffixPos + 1, Text.size());
+  }
+  return std::nullopt;
+}
+
+static std::string createRawStringLiteral(const StringLiteral *Literal,
+                                          const std::string &DelimiterStem,
+                                          const SourceManager &SM,
+                                          const LangOptions &LangOpts) {
+  const StringRef Bytes = Literal->getBytes();
+  std::string Delimiter;
+  for (int I = 0; containsDelimiter(Bytes, Delimiter); ++I) {
+    Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
+  }
+
+  std::optional<StringRef> UserDefinedSuffix =
+      createUserDefinedSuffix(Literal, SM, LangOpts);
+
+  if (Delimiter.empty())
+    return (R"(R"()" + Bytes + R"lit()")lit" + UserDefinedSuffix.value_or(""))
+        .str();
+
+  return (R"(R")" + Delimiter + "(" + Bytes + ")" + Delimiter + R"(")" +
+          UserDefinedSuffix.value_or(""))
+      .str();
+}
+
+static bool compareStringLength(StringRef Replacement,
+                                const StringLiteral *Literal,
+                                const SourceManager &SM,
+                                const LangOptions &LangOpts) {
+  return Replacement.size() <=
+         Lexer::MeasureTokenLength(Literal->getBeginLoc(), SM, LangOpts);
+}
+
+void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
+  if (Literal->getBeginLoc().isMacroID())
+    return;
+  SourceManager const &SM = *Result.SourceManager;
+  LangOptions const &LangOpts = getLangOpts();
+  if (containsEscapedCharacters(Result, Literal, DisallowedChars)) {
+    std::string Replacement =
+        createRawStringLiteral(Literal, DelimiterStem, SM, LangOpts);
+    if (ReplaceShorterLiterals ||
+        compareStringLength(Replacement, Literal, SM, LangOpts)) {
+      diag(Literal->getBeginLoc(),
+           "escaped string literal can be written as a raw string literal")
+          << FixItHint::CreateReplacement(Literal->getSourceRange(),
+                                          Replacement);
+    }
   }
-  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 6898e0624d1eb8..879255550dd5b6 100644
--- a/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h
@@ -33,10 +33,6 @@ class RawStringLiteralCheck : public ClangTidyCheck {
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
 
 private:
-  void replaceWithRawStringLiteral(
-      const ast_matchers::MatchFinder::MatchResult &Result,
-      const StringLiteral *Literal, std::string Replacement);
-
   std::string DelimiterStem;
   CharsBitSet DisallowedChars;
   const bool ReplaceShorterLiterals;

@HerrCai0907 HerrCai0907 force-pushed the users/ccc01-14-_clang-tidy_nfc_refactor_modernize-raw-string-literal_fix_hint branch from c413bd7 to 9e5c5eb Compare January 14, 2025 14:53
@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
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:45 AM EST: Graphite rebased this pull request as part of a merge.
  • Jan 17, 8:47 AM EST: A user merged this pull request with Graphite.

Base automatically changed from users/ccc01-14-_clang-tidy_fix_incorrect_auto-fix_for_the_string_contains_a_user-defined_suffix to main January 17, 2025 13:41
@HerrCai0907 HerrCai0907 force-pushed the users/ccc01-14-_clang-tidy_nfc_refactor_modernize-raw-string-literal_fix_hint branch from 9e5c5eb to 9d80866 Compare January 17, 2025 13:45
@HerrCai0907 HerrCai0907 merged commit 48d0ef1 into main Jan 17, 2025
5 of 7 checks passed
@HerrCai0907 HerrCai0907 deleted the users/ccc01-14-_clang-tidy_nfc_refactor_modernize-raw-string-literal_fix_hint branch January 17, 2025 13:47
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.

3 participants