11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 using namespace clang::ast_matchers;
22 bool isConcatenatedLiteralsOnPurpose(ASTContext* Ctx,
23 const StringLiteral* Lit) {
26 auto Parents = Ctx->getParents(*Lit);
27 if (Parents.size() == 1 && Parents[0].get<ParenExpr>() !=
nullptr)
39 const SourceManager&
SM = Ctx->getSourceManager();
40 bool IndentedCorrectly =
true;
41 SourceLocation FirstToken = Lit->getStrTokenLoc(0);
42 FileID BaseFID = SM.getFileID(FirstToken);
43 unsigned int BaseIndent = SM.getSpellingColumnNumber(FirstToken);
44 unsigned int BaseLine = SM.getSpellingLineNumber(FirstToken);
45 for (
unsigned int TokNum = 1; TokNum < Lit->getNumConcatenated(); ++ TokNum) {
46 SourceLocation Token = Lit->getStrTokenLoc(TokNum);
47 FileID FID = SM.getFileID(Token);
48 unsigned int Indent = SM.getSpellingColumnNumber(Token);
49 unsigned int Line = SM.getSpellingLineNumber(Token);
50 if (FID != BaseFID || Line != BaseLine + TokNum || Indent <= BaseIndent) {
51 IndentedCorrectly =
false;
55 if (IndentedCorrectly)
63 unsigned, MaxConcatenatedTokens) {
64 return Node.getNumConcatenated() > 1 &&
65 Node.getNumConcatenated() < MaxConcatenatedTokens &&
66 !isConcatenatedLiteralsOnPurpose(&
Finder->getASTContext(), &Node);
71 SuspiciousMissingCommaCheck::SuspiciousMissingCommaCheck(
74 SizeThreshold(Options.get(
"SizeThreshold", 5U)),
75 RatioThreshold(std::stod(Options.get(
"RatioThreshold",
".2"))),
76 MaxConcatenatedTokens(Options.get(
"MaxConcatenatedTokens", 5U)) {}
81 Options.
store(Opts,
"RatioThreshold", std::to_string(RatioThreshold));
82 Options.
store(Opts,
"MaxConcatenatedTokens", MaxConcatenatedTokens);
86 const auto ConcatenatedStringLiteral =
87 stringLiteral(isConcatenatedLiteral(MaxConcatenatedTokens)).bind(
"str");
89 const auto StringsInitializerList =
90 initListExpr(hasType(constantArrayType()),
91 has(ignoringParenImpCasts(expr(ConcatenatedStringLiteral))));
93 Finder->addMatcher(StringsInitializerList.bind(
"list"),
this);
97 const MatchFinder::MatchResult &
Result) {
98 const auto *InitializerList = Result.Nodes.getNodeAs<InitListExpr>(
"list");
99 const auto *ConcatenatedLiteral =
100 Result.Nodes.getNodeAs<StringLiteral>(
"str");
101 assert(InitializerList && ConcatenatedLiteral);
104 unsigned int Size = InitializerList->getNumInits();
105 if (Size < SizeThreshold)
return;
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) ++Count;
118 if (
double(Count) / Size > RatioThreshold)
return;
120 diag(ConcatenatedLiteral->getLocStart(),
121 "suspicious string literal, probably missing a comma");
std::unique_ptr< ast_matchers::MatchFinder > Finder
AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt, ast_matchers::internal::Matcher< DeclStmt >, InnerMatcher)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Base class for all clang-tidy checks.
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.
std::map< std::string, std::string > OptionMap
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.