10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Basic/LangOptions.h"
13#include "clang/Basic/SourceManager.h"
14#include "clang/Lex/Lexer.h"
15#include "llvm/ADT/StringRef.h"
23 size_t BackSlash = HayStack.find(
'\\');
24 if (BackSlash == StringRef::npos)
27 while (BackSlash != StringRef::npos) {
28 if (!Escapes.contains(HayStack[BackSlash + 1]))
30 BackSlash = HayStack.find(
'\\', BackSlash + 2);
38 const size_t QuotePos = Text.find(
'"');
39 assert(QuotePos != StringRef::npos);
40 return (QuotePos > 0) && (Text[QuotePos - 1] ==
'R');
44 const StringLiteral *Literal,
47 if (!Literal->isOrdinary())
50 for (
const unsigned char C : Literal->getBytes())
51 if (DisallowedChars.test(C))
54 const CharSourceRange CharRange = Lexer::makeFileCharRange(
55 CharSourceRange::getTokenRange(Literal->getSourceRange()),
56 *Result.SourceManager, Result.Context->getLangOpts());
57 const StringRef Text = Lexer::getSourceText(CharRange, *Result.SourceManager,
58 Result.Context->getLangOpts());
66 return Bytes.contains(Delimiter.empty() ? std::string(R
"lit()")lit")
67 : (")" + Delimiter + R
"(")"));
73 DelimiterStem(Options.get("DelimiterStem",
"lit")),
74 ReplaceShorterLiterals(Options.get(
"ReplaceShorterLiterals", false)) {
84 for (
const unsigned char C : StringRef(
"\000\001\002\003\004\005\006\a"
85 "\b\t\n\v\f\r\016\017"
86 "\020\021\022\023\024\025\026\027"
87 "\030\031\032\033\034\035\036\037"
90 DisallowedChars.set(C);
93 for (
unsigned int C = 0x80U; C <= 0xFFU; ++C)
94 DisallowedChars.set(
static_cast<unsigned char>(C));
98 Options.store(Opts,
"DelimiterStem", DelimiterStem);
99 Options.store(Opts,
"ReplaceShorterLiterals", ReplaceShorterLiterals);
104 stringLiteral(unless(hasParent(predefinedExpr()))).bind(
"lit"),
this);
107static std::optional<StringRef>
109 const LangOptions &LangOpts) {
110 const CharSourceRange TokenRange =
111 CharSourceRange::getTokenRange(Literal->getSourceRange());
113 if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, LangOpts))
115 const CharSourceRange CharRange =
116 Lexer::makeFileCharRange(TokenRange, SM, LangOpts);
117 if (T.hasUDSuffix()) {
118 const StringRef Text = Lexer::getSourceText(CharRange, SM, LangOpts);
119 const size_t UDSuffixPos = Text.find_last_of(
'"');
120 if (UDSuffixPos == StringRef::npos)
122 return Text.slice(UDSuffixPos + 1, Text.size());
128 const std::string &DelimiterStem,
129 const SourceManager &SM,
130 const LangOptions &LangOpts) {
131 const StringRef Bytes = Literal->getBytes();
132 std::string Delimiter;
134 Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
136 const std::optional<StringRef> UserDefinedSuffix =
139 if (Delimiter.empty())
140 return (R
"(R"()" + Bytes + R"lit()")lit" + UserDefinedSuffix.value_or(""))
143 return (R
"(R")" + Delimiter + "(" + Bytes +
")" + Delimiter + R
"(")" +
144 UserDefinedSuffix.value_or(""))
149 const StringLiteral *Literal,
150 const SourceManager &SM,
151 const LangOptions &LangOpts) {
152 return Replacement.size() <=
153 Lexer::MeasureTokenLength(Literal->getBeginLoc(), SM, LangOpts);
157 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>(
"lit");
158 if (Literal->getBeginLoc().isMacroID())
160 const SourceManager &SM = *Result.SourceManager;
161 const LangOptions &LangOpts = getLangOpts();
163 const std::string Replacement =
165 if (ReplaceShorterLiterals ||
167 diag(Literal->getBeginLoc(),
168 "escaped string literal can be written as a raw string literal")
169 << FixItHint::CreateReplacement(Literal->getSourceRange(),
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
RawStringLiteralCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static bool containsEscapedCharacters(const MatchFinder::MatchResult &Result, const StringLiteral *Literal, const CharsBitSet &DisallowedChars)
static bool compareStringLength(StringRef Replacement, const StringLiteral *Literal, const SourceManager &SM, const LangOptions &LangOpts)
static std::string createRawStringLiteral(const StringLiteral *Literal, const std::string &DelimiterStem, const SourceManager &SM, const LangOptions &LangOpts)
static std::optional< StringRef > createUserDefinedSuffix(const StringLiteral *Literal, const SourceManager &SM, const LangOptions &LangOpts)
static bool isRawStringLiteral(StringRef Text)
static bool containsEscapes(StringRef HayStack, StringRef Escapes)
std::bitset< 1<< CHAR_BIT > CharsBitSet
static bool containsDelimiter(StringRef Bytes, const std::string &Delimiter)
llvm::StringMap< ClangTidyValue > OptionMap