diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy/misc/SuspiciousMissingCommaCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/misc/SuspiciousMissingCommaCheck.cpp | 58 |
1 files changed, 52 insertions, 6 deletions
diff --git a/clang-tools-extra/clang-tidy/misc/SuspiciousMissingCommaCheck.cpp b/clang-tools-extra/clang-tidy/misc/SuspiciousMissingCommaCheck.cpp index a3a479c89b3..be983aa9233 100644 --- a/clang-tools-extra/clang-tidy/misc/SuspiciousMissingCommaCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/SuspiciousMissingCommaCheck.cpp @@ -19,8 +19,51 @@ namespace misc { namespace { -AST_MATCHER(StringLiteral, isConcatenatedLiteral) { - return Node.getNumConcatenated() > 1; +bool isConcatenatedLiteralsOnPurpose(ASTContext* Ctx, + const StringLiteral* Lit) { + // String literals surrounded by parentheses are assumed to be on purpose. + // i.e.: const char* Array[] = { ("a" "b" "c"), "d", [...] }; + auto Parents = Ctx->getParents(*Lit); + if (Parents.size() == 1 && Parents[0].get<ParenExpr>() != nullptr) + return true; + + // Appropriately indented string literals are assumed to be on purpose. + // The following frequent indentation is accepted: + // const char* Array[] = { + // "first literal" + // "indented literal" + // "indented literal", + // "second literal", + // [...] + // }; + const SourceManager& SM = Ctx->getSourceManager(); + bool IndentedCorrectly = true; + SourceLocation FirstToken = Lit->getStrTokenLoc(0); + FileID BaseFID = SM.getFileID(FirstToken); + unsigned int BaseIndent = SM.getSpellingColumnNumber(FirstToken); + unsigned int BaseLine = SM.getSpellingLineNumber(FirstToken); + for (unsigned int TokNum = 1; TokNum < Lit->getNumConcatenated(); ++ TokNum) { + SourceLocation Token = Lit->getStrTokenLoc(TokNum); + FileID FID = SM.getFileID(Token); + unsigned int Indent = SM.getSpellingColumnNumber(Token); + unsigned int Line = SM.getSpellingLineNumber(Token); + if (FID != BaseFID || Line != BaseLine + TokNum || Indent <= BaseIndent) { + IndentedCorrectly = false; + break; + } + } + if (IndentedCorrectly) + return true; + + // There is no pattern recognized by the checker, assume it's not on purpose. + return false; +} + +AST_MATCHER_P(StringLiteral, isConcatenatedLiteral, + unsigned, MaxConcatenatedTokens) { + return Node.getNumConcatenated() > 1 && + Node.getNumConcatenated() < MaxConcatenatedTokens && + !isConcatenatedLiteralsOnPurpose(&Finder->getASTContext(), &Node); } } // namespace @@ -29,17 +72,19 @@ SuspiciousMissingCommaCheck::SuspiciousMissingCommaCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), SizeThreshold(Options.get("SizeThreshold", 5U)), - RatioThreshold(std::stod(Options.get("RatioThreshold", ".2"))) {} + RatioThreshold(std::stod(Options.get("RatioThreshold", ".2"))), + MaxConcatenatedTokens(Options.get("MaxConcatenatedTokens", 5U)) {} void SuspiciousMissingCommaCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "SizeThreshold", SizeThreshold); Options.store(Opts, "RatioThreshold", std::to_string(RatioThreshold)); + Options.store(Opts, "MaxConcatenatedTokens", MaxConcatenatedTokens); } void SuspiciousMissingCommaCheck::registerMatchers(MatchFinder *Finder) { const auto ConcatenatedStringLiteral = - stringLiteral(isConcatenatedLiteral()).bind("str"); + stringLiteral(isConcatenatedLiteral(MaxConcatenatedTokens)).bind("str"); const auto StringsInitializerList = initListExpr(hasType(constantArrayType()), @@ -51,9 +96,10 @@ void SuspiciousMissingCommaCheck::registerMatchers(MatchFinder *Finder) { void SuspiciousMissingCommaCheck::check( const MatchFinder::MatchResult &Result) { const auto *InitializerList = Result.Nodes.getNodeAs<InitListExpr>("list"); - const auto *ConcatenatedLiteral = Result.Nodes.getNodeAs<Expr>("str"); + const auto *ConcatenatedLiteral = + Result.Nodes.getNodeAs<StringLiteral>("str"); assert(InitializerList && ConcatenatedLiteral); - + // Skip small arrays as they often generate false-positive. unsigned int Size = InitializerList->getNumInits(); if (Size < SizeThreshold) return; |