brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.6 KiB · a7300b7 Raw
225 lines · cpp
1//===- BugSuppression.cpp - Suppression interface -------------------------===//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 "clang/StaticAnalyzer/Core/BugReporter/BugSuppression.h"10#include "clang/AST/DynamicRecursiveASTVisitor.h"11#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"12#include "llvm/Support/FormatVariadic.h"13#include "llvm/Support/TimeProfiler.h"14 15using namespace clang;16using namespace ento;17 18namespace {19 20using Ranges = llvm::SmallVectorImpl<SourceRange>;21 22inline bool hasSuppression(const Decl *D) {23  // FIXME: Implement diagnostic identifier arguments24  // (checker names, "hashtags").25  if (const auto *Suppression = D->getAttr<SuppressAttr>())26    return !Suppression->isGSL() &&27           (Suppression->diagnosticIdentifiers().empty());28  return false;29}30inline bool hasSuppression(const AttributedStmt *S) {31  // FIXME: Implement diagnostic identifier arguments32  // (checker names, "hashtags").33  return llvm::any_of(S->getAttrs(), [](const Attr *A) {34    const auto *Suppression = dyn_cast<SuppressAttr>(A);35    return Suppression && !Suppression->isGSL() &&36           (Suppression->diagnosticIdentifiers().empty());37  });38}39 40template <class NodeType> inline SourceRange getRange(const NodeType *Node) {41  return Node->getSourceRange();42}43template <> inline SourceRange getRange(const AttributedStmt *S) {44  // Begin location for attributed statement node seems to be ALWAYS invalid.45  //46  // It is unlikely that we ever report any warnings on suppression47  // attribute itself, but even if we do, we wouldn't want that warning48  // to be suppressed by that same attribute.49  //50  // Long story short, we can use inner statement and it's not going to break51  // anything.52  return getRange(S->getSubStmt());53}54 55inline bool isLessOrEqual(SourceLocation LHS, SourceLocation RHS,56                          const SourceManager &SM) {57  // SourceManager::isBeforeInTranslationUnit tests for strict58  // inequality, when we need a non-strict comparison (bug59  // can be reported directly on the annotated note).60  // For this reason, we use the following equivalence:61  //62  //   A <= B <==> !(B < A)63  //64  return !SM.isBeforeInTranslationUnit(RHS, LHS);65}66 67inline bool fullyContains(SourceRange Larger, SourceRange Smaller,68                          const SourceManager &SM) {69  // Essentially this means:70  //71  //   Larger.fullyContains(Smaller)72  //73  // However, that method has a very trivial implementation and couldn't74  // compare regular locations and locations from macro expansions.75  // We could've converted everything into regular locations as a solution,76  // but the following solution seems to be the most bulletproof.77  return isLessOrEqual(Larger.getBegin(), Smaller.getBegin(), SM) &&78         isLessOrEqual(Smaller.getEnd(), Larger.getEnd(), SM);79}80 81class CacheInitializer : public DynamicRecursiveASTVisitor {82public:83  static void initialize(const Decl *D, Ranges &ToInit) {84    CacheInitializer(ToInit).TraverseDecl(const_cast<Decl *>(D));85  }86 87  bool VisitDecl(Decl *D) override {88    // Bug location could be somewhere in the init value of89    // a freshly declared variable.  Even though it looks like the90    // user applied attribute to a statement, it will apply to a91    // variable declaration, and this is where we check for it.92    return VisitAttributedNode(D);93  }94 95  bool VisitAttributedStmt(AttributedStmt *AS) override {96    // When we apply attributes to statements, it actually creates97    // a wrapper statement that only contains attributes and the wrapped98    // statement.99    return VisitAttributedNode(AS);100  }101 102private:103  template <class NodeType> bool VisitAttributedNode(NodeType *Node) {104    if (hasSuppression(Node)) {105      // TODO: In the future, when we come up with good stable IDs for checkers106      //       we can return a list of kinds to ignore, or all if no arguments107      //       were provided.108      addRange(getRange(Node));109    }110    // We should keep traversing AST.111    return true;112  }113 114  void addRange(SourceRange R) {115    if (R.isValid()) {116      Result.push_back(R);117    }118  }119 120  CacheInitializer(Ranges &R) : Result(R) {121    ShouldVisitTemplateInstantiations = true;122    ShouldWalkTypesOfTypeLocs = false;123    ShouldVisitImplicitCode = false;124    ShouldVisitLambdaBody = true;125  }126  Ranges &Result;127};128 129std::string timeScopeName(const Decl *DeclWithIssue) {130  if (!llvm::timeTraceProfilerEnabled())131    return "";132  return llvm::formatv(133             "BugSuppression::isSuppressed init suppressions cache for {0}",134             DeclWithIssue->getDeclKindName())135      .str();136}137 138llvm::TimeTraceMetadata getDeclTimeTraceMetadata(const Decl *DeclWithIssue) {139  assert(DeclWithIssue);140  assert(llvm::timeTraceProfilerEnabled());141  std::string Name = "<noname>";142  if (const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {143    Name = ND->getNameAsString();144  }145  const auto &SM = DeclWithIssue->getASTContext().getSourceManager();146  auto Line = SM.getPresumedLineNumber(DeclWithIssue->getBeginLoc());147  auto Fname = SM.getFilename(DeclWithIssue->getBeginLoc());148  return llvm::TimeTraceMetadata{std::move(Name), Fname.str(),149                                 static_cast<int>(Line)};150}151 152} // end anonymous namespace153 154// TODO: Introduce stable IDs for checkers and check for those here155//       to be more specific.  Attribute without arguments should still156//       be considered as "suppress all".157//       It is already much finer granularity than what we have now158//       (i.e. removing the whole function from the analysis).159bool BugSuppression::isSuppressed(const BugReport &R) {160  PathDiagnosticLocation Location = R.getLocation();161  PathDiagnosticLocation UniqueingLocation = R.getUniqueingLocation();162  const Decl *DeclWithIssue = R.getDeclWithIssue();163 164  return isSuppressed(Location, DeclWithIssue, {}) ||165         isSuppressed(UniqueingLocation, DeclWithIssue, {});166}167 168bool BugSuppression::isSuppressed(const PathDiagnosticLocation &Location,169                                  const Decl *DeclWithIssue,170                                  DiagnosticIdentifierList Hashtags) {171  if (!Location.isValid())172    return false;173 174  if (!DeclWithIssue) {175    // FIXME: This defeats the purpose of passing DeclWithIssue to begin with.176    // If this branch is ever hit, we're re-doing all the work we've already177    // done as well as perform a lot of work we'll never need.178    // Gladly, none of our on-by-default checkers currently need it.179    DeclWithIssue = ACtx.getTranslationUnitDecl();180  } else {181    // This is the fast path. However, we should still consider the topmost182    // declaration that isn't TranslationUnitDecl, because we should respect183    // attributes on the entire declaration chain.184    while (true) {185      // Use the "lexical" parent. Eg., if the attribute is on a class, suppress186      // warnings in inline methods but not in out-of-line methods.187      const Decl *Parent =188          dyn_cast_or_null<Decl>(DeclWithIssue->getLexicalDeclContext());189      if (Parent == nullptr || isa<TranslationUnitDecl>(Parent))190        break;191 192      DeclWithIssue = Parent;193    }194  }195 196  // While some warnings are attached to AST nodes (mostly path-sensitive197  // checks), others are simply associated with a plain source location198  // or range.  Figuring out the node based on locations can be tricky,199  // so instead, we traverse the whole body of the declaration and gather200  // information on ALL suppressions.  After that we can simply check if201  // any of those suppressions affect the warning in question.202  //203  // Traversing AST of a function is not a heavy operation, but for204  // large functions with a lot of bugs it can make a dent in performance.205  // In order to avoid this scenario, we cache traversal results.206  auto InsertionResult = CachedSuppressionLocations.insert(207      std::make_pair(DeclWithIssue, CachedRanges{}));208  Ranges &SuppressionRanges = InsertionResult.first->second;209  if (InsertionResult.second) {210    llvm::TimeTraceScope TimeScope(211        timeScopeName(DeclWithIssue),212        [DeclWithIssue]() { return getDeclTimeTraceMetadata(DeclWithIssue); });213    // We haven't checked this declaration for suppressions yet!214    CacheInitializer::initialize(DeclWithIssue, SuppressionRanges);215  }216 217  SourceRange BugRange = Location.asRange();218  const SourceManager &SM = Location.getManager();219 220  return llvm::any_of(SuppressionRanges,221                      [BugRange, &SM](SourceRange Suppression) {222                        return fullyContains(Suppression, BugRange, SM);223                      });224}225