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');
48 const SourceManager &SM,
49 const LangOptions &LangOpts) {
51 if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, LangOpts))
53 return tok::isStringLiteral(T.getKind());
57 const StringLiteral *Literal,
60 if (!Literal->isOrdinary())
63 for (
const unsigned char C : Literal->getBytes())
64 if (DisallowedChars.test(C))
68 Result.Context->getLangOpts()))
71 const CharSourceRange CharRange = Lexer::makeFileCharRange(
72 CharSourceRange::getTokenRange(Literal->getSourceRange()),
73 *Result.SourceManager, Result.Context->getLangOpts());
74 const StringRef Text = Lexer::getSourceText(CharRange, *Result.SourceManager,
75 Result.Context->getLangOpts());
83 return Bytes.contains(Delimiter.empty() ? std::string(R
"lit()")lit")
84 : (")" + Delimiter + R
"(")"));
90 DelimiterStem(Options.get("DelimiterStem",
"lit")),
91 ReplaceShorterLiterals(Options.get(
"ReplaceShorterLiterals", false)) {
101 for (
const unsigned char C : StringRef(
"\000\001\002\003\004\005\006\a"
102 "\b\t\n\v\f\r\016\017"
103 "\020\021\022\023\024\025\026\027"
104 "\030\031\032\033\034\035\036\037"
107 DisallowedChars.set(C);
110 for (
unsigned int C = 0x80U; C <= 0xFFU; ++C)
111 DisallowedChars.set(
static_cast<unsigned char>(C));
115 Options.store(Opts,
"DelimiterStem", DelimiterStem);
116 Options.store(Opts,
"ReplaceShorterLiterals", ReplaceShorterLiterals);
121 stringLiteral(unless(hasParent(predefinedExpr()))).bind(
"lit"),
this);
124static std::optional<StringRef>
126 const LangOptions &LangOpts) {
127 const CharSourceRange TokenRange =
128 CharSourceRange::getTokenRange(Literal->getSourceRange());
130 if (Lexer::getRawToken(Literal->getBeginLoc(), T, SM, LangOpts))
132 const CharSourceRange CharRange =
133 Lexer::makeFileCharRange(TokenRange, SM, LangOpts);
134 if (T.hasUDSuffix()) {
135 const StringRef Text = Lexer::getSourceText(CharRange, SM, LangOpts);
136 const size_t UDSuffixPos = Text.find_last_of(
'"');
137 if (UDSuffixPos == StringRef::npos)
139 return Text.slice(UDSuffixPos + 1, Text.size());
145 const std::string &DelimiterStem,
146 const SourceManager &SM,
147 const LangOptions &LangOpts) {
148 const StringRef Bytes = Literal->getBytes();
149 std::string Delimiter;
151 Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
153 const std::optional<StringRef> UserDefinedSuffix =
156 if (Delimiter.empty())
157 return (R
"(R"()" + Bytes + R"lit()")lit" + UserDefinedSuffix.value_or(""))
160 return (R
"(R")" + Delimiter + "(" + Bytes +
")" + Delimiter + R
"(")" +
161 UserDefinedSuffix.value_or(""))
166 const StringLiteral *Literal,
167 const SourceManager &SM,
168 const LangOptions &LangOpts) {
169 return Replacement.size() <=
170 Lexer::MeasureTokenLength(Literal->getBeginLoc(), SM, LangOpts);
174 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>(
"lit");
175 if (Literal->getBeginLoc().isMacroID())
177 const SourceManager &SM = *Result.SourceManager;
178 const LangOptions &LangOpts = getLangOpts();
180 const std::string Replacement =
182 if (ReplaceShorterLiterals ||
184 diag(Literal->getBeginLoc(),
185 "escaped string literal can be written as a raw string literal")
186 << 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 bool isSpelledAsStringLiteral(const StringLiteral *Literal, 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