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 CharSourceRange CharRange = Lexer::makeFileCharRange(
55 CharSourceRange::getTokenRange(Literal->getSourceRange()),
56 *Result.SourceManager, Result.Context->getLangOpts());
57 StringRef Text = Lexer::getSourceText(CharRange, *Result.SourceManager,
58 Result.Context->getLangOpts());
66 return Bytes.find(Delimiter.empty()
67 ? std::string(R
"lit()")lit")
68 : (")" + Delimiter + R
"(")")) != StringRef::npos;
74 DelimiterStem(Options.get("DelimiterStem",
"lit")),
75 ReplaceShorterLiterals(Options.get(
"ReplaceShorterLiterals", false)) {
85 for (
const unsigned char C : StringRef(
"\000\001\002\003\004\005\006\a"
86 "\b\t\n\v\f\r\016\017"
87 "\020\021\022\023\024\025\026\027"
88 "\030\031\032\033\034\035\036\037"
91 DisallowedChars.set(C);
94 for (
unsigned int C = 0x80U; C <= 0xFFU; ++C)
95 DisallowedChars.set(
static_cast<unsigned char>(C));
99 Options.store(Opts,
"DelimiterStem", DelimiterStem);
100 Options.store(Opts,
"ReplaceShorterLiterals", ReplaceShorterLiterals);
105 stringLiteral(unless(hasParent(predefinedExpr()))).bind(
"lit"),
this);
108static std::optional<StringRef>
110 const LangOptions &LangOpts) {
111 const CharSourceRange TokenRange =
112 CharSourceRange::getTokenRange(Literal->getSourceRange());
114 if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, LangOpts))
116 const CharSourceRange CharRange =
117 Lexer::makeFileCharRange(TokenRange, SM, LangOpts);
118 if (T.hasUDSuffix()) {
119 StringRef Text = Lexer::getSourceText(CharRange, SM, LangOpts);
120 const size_t UDSuffixPos = Text.find_last_of(
'"');
121 if (UDSuffixPos == StringRef::npos)
123 return Text.slice(UDSuffixPos + 1, Text.size());
129 const std::string &DelimiterStem,
130 const SourceManager &SM,
131 const LangOptions &LangOpts) {
132 const StringRef Bytes = Literal->getBytes();
133 std::string Delimiter;
135 Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
138 std::optional<StringRef> UserDefinedSuffix =
141 if (Delimiter.empty())
142 return (R
"(R"()" + Bytes + R"lit()")lit" + UserDefinedSuffix.value_or(""))
145 return (R
"(R")" + Delimiter + "(" + Bytes +
")" + Delimiter + R
"(")" +
146 UserDefinedSuffix.value_or(""))
151 const StringLiteral *Literal,
152 const SourceManager &SM,
153 const LangOptions &LangOpts) {
154 return Replacement.size() <=
155 Lexer::MeasureTokenLength(Literal->getBeginLoc(), SM, LangOpts);
159 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>(
"lit");
160 if (Literal->getBeginLoc().isMacroID())
162 const SourceManager &SM = *Result.SourceManager;
163 const LangOptions &LangOpts = getLangOpts();
165 const std::string Replacement =
167 if (ReplaceShorterLiterals ||
169 diag(Literal->getBeginLoc(),
170 "escaped string literal can be written as a raw string literal")
171 << 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