10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
19bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
20 const StringLiteral *Lit) {
24 TraversalKindScope RAII(*Ctx, TK_AsIs);
25 auto Parents = Ctx->getParents(*Lit);
26 if (
Parents.size() == 1 && Parents[0].get<ParenExpr>() !=
nullptr)
38 const SourceManager &SM = Ctx->getSourceManager();
39 bool IndentedCorrectly =
true;
40 SourceLocation FirstToken = Lit->getStrTokenLoc(0);
41 FileID BaseFID = SM.getFileID(FirstToken);
42 unsigned int BaseIndent = SM.getSpellingColumnNumber(FirstToken);
43 unsigned int BaseLine = SM.getSpellingLineNumber(FirstToken);
44 for (
unsigned int TokNum = 1; TokNum < Lit->getNumConcatenated(); ++TokNum) {
45 SourceLocation Token = Lit->getStrTokenLoc(TokNum);
46 FileID FID = SM.getFileID(Token);
47 unsigned int Indent = SM.getSpellingColumnNumber(Token);
48 unsigned int Line = SM.getSpellingLineNumber(Token);
49 if (FID != BaseFID ||
Line != BaseLine + TokNum || Indent <= BaseIndent) {
50 IndentedCorrectly =
false;
54 if (IndentedCorrectly)
62 MaxConcatenatedTokens) {
63 return Node.getNumConcatenated() > 1 &&
64 Node.getNumConcatenated() < MaxConcatenatedTokens &&
65 !isConcatenatedLiteralsOnPurpose(&Finder->getASTContext(), &Node);
73 SizeThreshold(Options.get(
"SizeThreshold", 5U)),
74 RatioThreshold(std::stod(Options.get(
"RatioThreshold",
".2").str())),
75 MaxConcatenatedTokens(Options.get(
"MaxConcatenatedTokens", 5U)) {}
80 Options.
store(Opts,
"RatioThreshold", std::to_string(RatioThreshold));
81 Options.
store(Opts,
"MaxConcatenatedTokens", MaxConcatenatedTokens);
85 const auto ConcatenatedStringLiteral =
86 stringLiteral(isConcatenatedLiteral(MaxConcatenatedTokens)).bind(
"str");
88 const auto StringsInitializerList =
89 initListExpr(hasType(constantArrayType()),
90 has(ignoringParenImpCasts(expr(ConcatenatedStringLiteral))));
92 Finder->addMatcher(StringsInitializerList.bind(
"list"),
this);
96 const MatchFinder::MatchResult &Result) {
97 const auto *InitializerList = Result.Nodes.getNodeAs<InitListExpr>(
"list");
98 const auto *ConcatenatedLiteral =
99 Result.Nodes.getNodeAs<StringLiteral>(
"str");
100 assert(InitializerList && ConcatenatedLiteral);
103 unsigned int Size = InitializerList->getNumInits();
104 if (Size < SizeThreshold)
108 unsigned int Count = 0;
109 for (
unsigned int I = 0; I < Size; ++I) {
110 const Expr *Child = InitializerList->getInit(I)->IgnoreImpCasts();
111 if (
const auto *Literal = dyn_cast<StringLiteral>(Child)) {
112 if (Literal->getNumConcatenated() > 1)
119 if (
double(Count) / Size > RatioThreshold)
122 diag(ConcatenatedLiteral->getBeginLoc(),
123 "suspicious string literal, probably missing a comma");
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
SuspiciousMissingCommaCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
llvm::StringMap< ClangTidyValue > OptionMap