|
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