clang-tools 6.0.0

SuspiciousMissingCommaCheck.cpp
Go to the documentation of this file.
1 //===--- SuspiciousMissingCommaCheck.cpp - clang-tidy----------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "SuspiciousMissingCommaCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13
14 using namespace clang::ast_matchers;
15
16 namespace clang {
17 namespace tidy {
18 namespace misc {
19
20 namespace {
21
22 bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
23 const StringLiteral *Lit) {
24 // String literals surrounded by parentheses are assumed to be on purpose.
25 // i.e.: const char* Array[] = { ("a" "b" "c"), "d", [...] };
26 auto Parents = Ctx->getParents(*Lit);
27 if (Parents.size() == 1 && Parents[0].get<ParenExpr>() != nullptr)
28 return true;
29
30 // Appropriately indented string literals are assumed to be on purpose.
31 // The following frequent indentation is accepted:
32 // const char* Array[] = {
33 // "first literal"
34 // "indented literal"
35 // "indented literal",
36 // "second literal",
37 // [...]
38 // };
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;
52 break;
53 }
54 }
55 if (IndentedCorrectly)
56 return true;
57
58 // There is no pattern recognized by the checker, assume it's not on purpose.
59 return false;
60 }
61
62 AST_MATCHER_P(StringLiteral, isConcatenatedLiteral, unsigned,
63 MaxConcatenatedTokens) {
64 return Node.getNumConcatenated() > 1 &&
65 Node.getNumConcatenated() < MaxConcatenatedTokens &&
66 !isConcatenatedLiteralsOnPurpose(&Finder->getASTContext(), &Node);
67 }
68
69 } // namespace
70
71 SuspiciousMissingCommaCheck::SuspiciousMissingCommaCheck(
72 StringRef Name, ClangTidyContext *Context)
73 : ClangTidyCheck(Name, Context),
74 SizeThreshold(Options.get("SizeThreshold", 5U)),
75 RatioThreshold(std::stod(Options.get("RatioThreshold", ".2"))),
76 MaxConcatenatedTokens(Options.get("MaxConcatenatedTokens", 5U)) {}
77
78 void SuspiciousMissingCommaCheck::storeOptions(
79 ClangTidyOptions::OptionMap &Opts) {
80 Options.store(Opts, "SizeThreshold", SizeThreshold);
81 Options.store(Opts, "RatioThreshold", std::to_string(RatioThreshold));
82 Options.store(Opts, "MaxConcatenatedTokens", MaxConcatenatedTokens);
83 }
84
85 void SuspiciousMissingCommaCheck::registerMatchers(MatchFinder *Finder) {
86 const auto ConcatenatedStringLiteral =
87 stringLiteral(isConcatenatedLiteral(MaxConcatenatedTokens)).bind("str");
88
89 const auto StringsInitializerList =
90 initListExpr(hasType(constantArrayType()),
91 has(ignoringParenImpCasts(expr(ConcatenatedStringLiteral))));
92
93 Finder->addMatcher(StringsInitializerList.bind("list"), this);
94 }
95
96 void SuspiciousMissingCommaCheck::check(
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);
102
103 // Skip small arrays as they often generate false-positive.
104 unsigned int Size = InitializerList->getNumInits();
105 if (Size < SizeThreshold)
106 return;
107
108 // Count the number of occurence of concatenated string literal.
109 unsigned int Count = 0;
110 for (unsigned int i = 0; i < Size; ++i) {
111 const Expr *Child = InitializerList->getInit(i)->IgnoreImpCasts();
112 if (const auto *Literal = dyn_cast<StringLiteral>(Child)) {
113 if (Literal->getNumConcatenated() > 1)
114 ++Count;
115 }
116 }
117
118 // Warn only when concatenation is not common in this initializer list.
119 // The current threshold is set to less than 1/5 of the string literals.
120 if (double(Count) / Size > RatioThreshold)
121 return;
122
123 diag(ConcatenatedLiteral->getLocStart(),
124 "suspicious string literal, probably missing a comma");
125 }
126
127 } // namespace misc
128 } // namespace tidy
129 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::OptionsView::store
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.
Definition: ClangTidy.cpp:449
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
clang::tidy::misc::SuspiciousMissingCommaCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SuspiciousMissingCommaCheck.cpp:85
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:183
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:99
clang::tidy::misc::SuspiciousMissingCommaCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: SuspiciousMissingCommaCheck.cpp:78
clang
Definition: AndroidTidyModule.cpp:28
Line
int Line
Definition: PreprocessorTracker.cpp:527
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:104
clang::tidy::misc::SuspiciousMissingCommaCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: SuspiciousMissingCommaCheck.cpp:96
clang::tidy::bugprone::AST_MATCHER_P
AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N)
Definition: StringConstructorCheck.cpp:21
SuspiciousMissingCommaCheck.h
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:416