|
9 | 9 | #include "RawStringLiteralCheck.h"
|
10 | 10 | #include "clang/AST/ASTContext.h"
|
11 | 11 | #include "clang/ASTMatchers/ASTMatchFinder.h"
|
| 12 | +#include "clang/Basic/LangOptions.h" |
| 13 | +#include "clang/Basic/SourceManager.h" |
12 | 14 | #include "clang/Lex/Lexer.h"
|
13 | 15 | #include "llvm/ADT/StringRef.h"
|
| 16 | +#include <optional> |
14 | 17 |
|
15 | 18 | using namespace clang::ast_matchers;
|
16 | 19 |
|
@@ -67,20 +70,6 @@ bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
|
67 | 70 | : (")" + Delimiter + R"(")")) != StringRef::npos;
|
68 | 71 | }
|
69 | 72 |
|
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 |
| - |
84 | 73 | } // namespace
|
85 | 74 |
|
86 | 75 | RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name,
|
@@ -120,43 +109,73 @@ void RawStringLiteralCheck::registerMatchers(MatchFinder *Finder) {
|
120 | 109 | stringLiteral(unless(hasParent(predefinedExpr()))).bind("lit"), this);
|
121 | 110 | }
|
122 | 111 |
|
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) { |
145 | 115 | const CharSourceRange TokenRange =
|
146 | 116 | CharSourceRange::getTokenRange(Literal->getSourceRange());
|
147 | 117 | 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; |
150 | 120 | const CharSourceRange CharRange =
|
151 |
| - Lexer::makeFileCharRange(TokenRange, SM, getLangOpts()); |
| 121 | + Lexer::makeFileCharRange(TokenRange, SM, LangOpts); |
152 | 122 | if (T.hasUDSuffix()) {
|
153 |
| - const StringRef Text = Lexer::getSourceText(CharRange, SM, getLangOpts()); |
| 123 | + StringRef Text = Lexer::getSourceText(CharRange, SM, LangOpts); |
154 | 124 | const size_t UDSuffixPos = Text.find_last_of('"');
|
155 | 125 | 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 | + } |
158 | 178 | }
|
159 |
| - Builder << FixItHint::CreateReplacement(CharRange, Replacement); |
160 | 179 | }
|
161 | 180 |
|
162 | 181 | } // namespace clang::tidy::modernize
|
0 commit comments