Skip to content

Commit 48d0ef1

Browse files
authored
[clang-tidy][NFC] refactor modernize-raw-string-literal fix hint (#122909)
1 parent 361f363 commit 48d0ef1

File tree

2 files changed

+62
-47
lines changed

2 files changed

+62
-47
lines changed

clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.cpp

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
#include "RawStringLiteralCheck.h"
1010
#include "clang/AST/ASTContext.h"
1111
#include "clang/ASTMatchers/ASTMatchFinder.h"
12+
#include "clang/Basic/LangOptions.h"
13+
#include "clang/Basic/SourceManager.h"
1214
#include "clang/Lex/Lexer.h"
1315
#include "llvm/ADT/StringRef.h"
16+
#include <optional>
1417

1518
using namespace clang::ast_matchers;
1619

@@ -67,20 +70,6 @@ bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
6770
: (")" + Delimiter + R"(")")) != StringRef::npos;
6871
}
6972

70-
std::string asRawStringLiteral(const StringLiteral *Literal,
71-
const std::string &DelimiterStem) {
72-
const StringRef Bytes = Literal->getBytes();
73-
std::string Delimiter;
74-
for (int I = 0; containsDelimiter(Bytes, Delimiter); ++I) {
75-
Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
76-
}
77-
78-
if (Delimiter.empty())
79-
return (R"(R"()" + Bytes + R"lit()")lit").str();
80-
81-
return (R"(R")" + Delimiter + "(" + Bytes + ")" + Delimiter + R"(")").str();
82-
}
83-
8473
} // namespace
8574

8675
RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name,
@@ -120,43 +109,73 @@ void RawStringLiteralCheck::registerMatchers(MatchFinder *Finder) {
120109
stringLiteral(unless(hasParent(predefinedExpr()))).bind("lit"), this);
121110
}
122111

123-
void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
124-
const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
125-
if (Literal->getBeginLoc().isMacroID())
126-
return;
127-
128-
if (containsEscapedCharacters(Result, Literal, DisallowedChars)) {
129-
std::string Replacement = asRawStringLiteral(Literal, DelimiterStem);
130-
if (ReplaceShorterLiterals ||
131-
Replacement.length() <=
132-
Lexer::MeasureTokenLength(Literal->getBeginLoc(),
133-
*Result.SourceManager, getLangOpts()))
134-
replaceWithRawStringLiteral(Result, Literal, Replacement);
135-
}
136-
}
137-
138-
void RawStringLiteralCheck::replaceWithRawStringLiteral(
139-
const MatchFinder::MatchResult &Result, const StringLiteral *Literal,
140-
std::string Replacement) {
141-
DiagnosticBuilder Builder =
142-
diag(Literal->getBeginLoc(),
143-
"escaped string literal can be written as a raw string literal");
144-
const SourceManager &SM = *Result.SourceManager;
112+
static std::optional<StringRef>
113+
createUserDefinedSuffix(const StringLiteral *Literal, const SourceManager &SM,
114+
const LangOptions &LangOpts) {
145115
const CharSourceRange TokenRange =
146116
CharSourceRange::getTokenRange(Literal->getSourceRange());
147117
Token T;
148-
if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, getLangOpts()))
149-
return;
118+
if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, LangOpts))
119+
return std::nullopt;
150120
const CharSourceRange CharRange =
151-
Lexer::makeFileCharRange(TokenRange, SM, getLangOpts());
121+
Lexer::makeFileCharRange(TokenRange, SM, LangOpts);
152122
if (T.hasUDSuffix()) {
153-
const StringRef Text = Lexer::getSourceText(CharRange, SM, getLangOpts());
123+
StringRef Text = Lexer::getSourceText(CharRange, SM, LangOpts);
154124
const size_t UDSuffixPos = Text.find_last_of('"');
155125
if (UDSuffixPos == StringRef::npos)
156-
return;
157-
Replacement += Text.slice(UDSuffixPos + 1, Text.size());
126+
return std::nullopt;
127+
return Text.slice(UDSuffixPos + 1, Text.size());
128+
}
129+
return std::nullopt;
130+
}
131+
132+
static std::string createRawStringLiteral(const StringLiteral *Literal,
133+
const std::string &DelimiterStem,
134+
const SourceManager &SM,
135+
const LangOptions &LangOpts) {
136+
const StringRef Bytes = Literal->getBytes();
137+
std::string Delimiter;
138+
for (int I = 0; containsDelimiter(Bytes, Delimiter); ++I) {
139+
Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
140+
}
141+
142+
std::optional<StringRef> UserDefinedSuffix =
143+
createUserDefinedSuffix(Literal, SM, LangOpts);
144+
145+
if (Delimiter.empty())
146+
return (R"(R"()" + Bytes + R"lit()")lit" + UserDefinedSuffix.value_or(""))
147+
.str();
148+
149+
return (R"(R")" + Delimiter + "(" + Bytes + ")" + Delimiter + R"(")" +
150+
UserDefinedSuffix.value_or(""))
151+
.str();
152+
}
153+
154+
static bool compareStringLength(StringRef Replacement,
155+
const StringLiteral *Literal,
156+
const SourceManager &SM,
157+
const LangOptions &LangOpts) {
158+
return Replacement.size() <=
159+
Lexer::MeasureTokenLength(Literal->getBeginLoc(), SM, LangOpts);
160+
}
161+
162+
void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
163+
const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
164+
if (Literal->getBeginLoc().isMacroID())
165+
return;
166+
const SourceManager &SM = *Result.SourceManager;
167+
const LangOptions &LangOpts = getLangOpts();
168+
if (containsEscapedCharacters(Result, Literal, DisallowedChars)) {
169+
const std::string Replacement =
170+
createRawStringLiteral(Literal, DelimiterStem, SM, LangOpts);
171+
if (ReplaceShorterLiterals ||
172+
compareStringLength(Replacement, Literal, SM, LangOpts)) {
173+
diag(Literal->getBeginLoc(),
174+
"escaped string literal can be written as a raw string literal")
175+
<< FixItHint::CreateReplacement(Literal->getSourceRange(),
176+
Replacement);
177+
}
158178
}
159-
Builder << FixItHint::CreateReplacement(CharRange, Replacement);
160179
}
161180

162181
} // namespace clang::tidy::modernize

clang-tools-extra/clang-tidy/modernize/RawStringLiteralCheck.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ class RawStringLiteralCheck : public ClangTidyCheck {
3333
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
3434

3535
private:
36-
void replaceWithRawStringLiteral(
37-
const ast_matchers::MatchFinder::MatchResult &Result,
38-
const StringLiteral *Literal, std::string Replacement);
39-
4036
std::string DelimiterStem;
4137
CharsBitSet DisallowedChars;
4238
const bool ReplaceShorterLiterals;

0 commit comments

Comments
 (0)