brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.7 KiB · cf8bc97 Raw
127 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "SuspiciousMissingCommaCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::bugprone {16 17static bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,18                                            const StringLiteral *Lit) {19  // String literals surrounded by parentheses are assumed to be on purpose.20  //    i.e.:  const char* Array[] = { ("a" "b" "c"), "d", [...] };21 22  const TraversalKindScope RAII(*Ctx, TK_AsIs);23  auto Parents = Ctx->getParents(*Lit);24  if (Parents.size() == 1 && Parents[0].get<ParenExpr>() != nullptr)25    return true;26 27  // Appropriately indented string literals are assumed to be on purpose.28  // The following frequent indentation is accepted:29  //     const char* Array[] = {30  //       "first literal"31  //           "indented literal"32  //           "indented literal",33  //       "second literal",34  //       [...]35  //     };36  const SourceManager &SM = Ctx->getSourceManager();37  bool IndentedCorrectly = true;38  const SourceLocation FirstToken = Lit->getStrTokenLoc(0);39  const FileID BaseFID = SM.getFileID(FirstToken);40  const unsigned int BaseIndent = SM.getSpellingColumnNumber(FirstToken);41  const unsigned int BaseLine = SM.getSpellingLineNumber(FirstToken);42  for (unsigned int TokNum = 1; TokNum < Lit->getNumConcatenated(); ++TokNum) {43    const SourceLocation Token = Lit->getStrTokenLoc(TokNum);44    const FileID FID = SM.getFileID(Token);45    const unsigned int Indent = SM.getSpellingColumnNumber(Token);46    const unsigned int Line = SM.getSpellingLineNumber(Token);47    if (FID != BaseFID || Line != BaseLine + TokNum || Indent <= BaseIndent) {48      IndentedCorrectly = false;49      break;50    }51  }52  if (IndentedCorrectly)53    return true;54 55  // There is no pattern recognized by the checker, assume it's not on purpose.56  return false;57}58 59namespace {60 61AST_MATCHER_P(StringLiteral, isConcatenatedLiteral, unsigned,62              MaxConcatenatedTokens) {63  return Node.getNumConcatenated() > 1 &&64         Node.getNumConcatenated() < MaxConcatenatedTokens &&65         !isConcatenatedLiteralsOnPurpose(&Finder->getASTContext(), &Node);66}67 68} // namespace69 70SuspiciousMissingCommaCheck::SuspiciousMissingCommaCheck(71    StringRef Name, ClangTidyContext *Context)72    : ClangTidyCheck(Name, Context),73      SizeThreshold(Options.get("SizeThreshold", 5U)),74      RatioThreshold(std::stod(Options.get("RatioThreshold", ".2").str())),75      MaxConcatenatedTokens(Options.get("MaxConcatenatedTokens", 5U)) {}76 77void SuspiciousMissingCommaCheck::storeOptions(78    ClangTidyOptions::OptionMap &Opts) {79  Options.store(Opts, "SizeThreshold", SizeThreshold);80  Options.store(Opts, "RatioThreshold", std::to_string(RatioThreshold));81  Options.store(Opts, "MaxConcatenatedTokens", MaxConcatenatedTokens);82}83 84void SuspiciousMissingCommaCheck::registerMatchers(MatchFinder *Finder) {85  const auto ConcatenatedStringLiteral =86      stringLiteral(isConcatenatedLiteral(MaxConcatenatedTokens)).bind("str");87 88  const auto StringsInitializerList =89      initListExpr(hasType(constantArrayType()),90                   has(ignoringParenImpCasts(expr(ConcatenatedStringLiteral))));91 92  Finder->addMatcher(StringsInitializerList.bind("list"), this);93}94 95void SuspiciousMissingCommaCheck::check(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);101 102  // Skip small arrays as they often generate false-positive.103  const unsigned int Size = InitializerList->getNumInits();104  if (Size < SizeThreshold)105    return;106 107  // Count the number of occurrence of concatenated string literal.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)113        ++Count;114    }115  }116 117  // Warn only when concatenation is not common in this initializer list.118  // The current threshold is set to less than 1/5 of the string literals.119  if (double(Count) / Size > RatioThreshold)120    return;121 122  diag(ConcatenatedLiteral->getBeginLoc(),123       "suspicious string literal, probably missing a comma");124}125 126} // namespace clang::tidy::bugprone127