clang  19.0.0git
BugSuppression.cpp
Go to the documentation of this file.
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
12 
13 using namespace clang;
14 using namespace ento;
15 
16 namespace {
17 
19 
20 inline bool hasSuppression(const Decl *D) {
21  // FIXME: Implement diagnostic identifier arguments
22  // (checker names, "hashtags").
23  if (const auto *Suppression = D->getAttr<SuppressAttr>())
24  return !Suppression->isGSL() &&
25  (Suppression->diagnosticIdentifiers().empty());
26  return false;
27 }
28 inline bool hasSuppression(const AttributedStmt *S) {
29  // FIXME: Implement diagnostic identifier arguments
30  // (checker names, "hashtags").
31  return llvm::any_of(S->getAttrs(), [](const Attr *A) {
32  const auto *Suppression = dyn_cast<SuppressAttr>(A);
33  return Suppression && !Suppression->isGSL() &&
34  (Suppression->diagnosticIdentifiers().empty());
35  });
36 }
37 
38 template <class NodeType> inline SourceRange getRange(const NodeType *Node) {
39  return Node->getSourceRange();
40 }
41 template <> inline SourceRange getRange(const AttributedStmt *S) {
42  // Begin location for attributed statement node seems to be ALWAYS invalid.
43  //
44  // It is unlikely that we ever report any warnings on suppression
45  // attribute itself, but even if we do, we wouldn't want that warning
46  // to be suppressed by that same attribute.
47  //
48  // Long story short, we can use inner statement and it's not going to break
49  // anything.
50  return getRange(S->getSubStmt());
51 }
52 
53 inline bool isLessOrEqual(SourceLocation LHS, SourceLocation RHS,
54  const SourceManager &SM) {
55  // SourceManager::isBeforeInTranslationUnit tests for strict
56  // inequality, when we need a non-strict comparison (bug
57  // can be reported directly on the annotated note).
58  // For this reason, we use the following equivalence:
59  //
60  // A <= B <==> !(B < A)
61  //
62  return !SM.isBeforeInTranslationUnit(RHS, LHS);
63 }
64 
65 inline bool fullyContains(SourceRange Larger, SourceRange Smaller,
66  const SourceManager &SM) {
67  // Essentially this means:
68  //
69  // Larger.fullyContains(Smaller)
70  //
71  // However, that method has a very trivial implementation and couldn't
72  // compare regular locations and locations from macro expansions.
73  // We could've converted everything into regular locations as a solution,
74  // but the following solution seems to be the most bulletproof.
75  return isLessOrEqual(Larger.getBegin(), Smaller.getBegin(), SM) &&
76  isLessOrEqual(Smaller.getEnd(), Larger.getEnd(), SM);
77 }
78 
79 class CacheInitializer : public RecursiveASTVisitor<CacheInitializer> {
80 public:
81  static void initialize(const Decl *D, Ranges &ToInit) {
82  CacheInitializer(ToInit).TraverseDecl(const_cast<Decl *>(D));
83  }
84 
85  bool VisitDecl(Decl *D) {
86  // Bug location could be somewhere in the init value of
87  // a freshly declared variable. Even though it looks like the
88  // user applied attribute to a statement, it will apply to a
89  // variable declaration, and this is where we check for it.
90  return VisitAttributedNode(D);
91  }
92 
93  bool VisitAttributedStmt(AttributedStmt *AS) {
94  // When we apply attributes to statements, it actually creates
95  // a wrapper statement that only contains attributes and the wrapped
96  // statement.
97  return VisitAttributedNode(AS);
98  }
99 
100 private:
101  template <class NodeType> bool VisitAttributedNode(NodeType *Node) {
102  if (hasSuppression(Node)) {
103  // TODO: In the future, when we come up with good stable IDs for checkers
104  // we can return a list of kinds to ignore, or all if no arguments
105  // were provided.
106  addRange(getRange(Node));
107  }
108  // We should keep traversing AST.
109  return true;
110  }
111 
112  void addRange(SourceRange R) {
113  if (R.isValid()) {
114  Result.push_back(R);
115  }
116  }
117 
118  CacheInitializer(Ranges &R) : Result(R) {}
119  Ranges &Result;
120 };
121 
122 } // end anonymous namespace
123 
124 // TODO: Introduce stable IDs for checkers and check for those here
125 // to be more specific. Attribute without arguments should still
126 // be considered as "suppress all".
127 // It is already much finer granularity than what we have now
128 // (i.e. removing the whole function from the analysis).
130  PathDiagnosticLocation Location = R.getLocation();
131  PathDiagnosticLocation UniqueingLocation = R.getUniqueingLocation();
132  const Decl *DeclWithIssue = R.getDeclWithIssue();
133 
134  return isSuppressed(Location, DeclWithIssue, {}) ||
135  isSuppressed(UniqueingLocation, DeclWithIssue, {});
136 }
137 
139  const Decl *DeclWithIssue,
140  DiagnosticIdentifierList Hashtags) {
141  if (!Location.isValid())
142  return false;
143 
144  if (!DeclWithIssue) {
145  // FIXME: This defeats the purpose of passing DeclWithIssue to begin with.
146  // If this branch is ever hit, we're re-doing all the work we've already
147  // done as well as perform a lot of work we'll never need.
148  // Gladly, none of our on-by-default checkers currently need it.
149  DeclWithIssue = ACtx.getTranslationUnitDecl();
150  } else {
151  // This is the fast path. However, we should still consider the topmost
152  // declaration that isn't TranslationUnitDecl, because we should respect
153  // attributes on the entire declaration chain.
154  while (true) {
155  // Use the "lexical" parent. Eg., if the attribute is on a class, suppress
156  // warnings in inline methods but not in out-of-line methods.
157  const Decl *Parent =
158  dyn_cast_or_null<Decl>(DeclWithIssue->getLexicalDeclContext());
159  if (Parent == nullptr || isa<TranslationUnitDecl>(Parent))
160  break;
161 
162  DeclWithIssue = Parent;
163  }
164  }
165 
166  // While some warnings are attached to AST nodes (mostly path-sensitive
167  // checks), others are simply associated with a plain source location
168  // or range. Figuring out the node based on locations can be tricky,
169  // so instead, we traverse the whole body of the declaration and gather
170  // information on ALL suppressions. After that we can simply check if
171  // any of those suppressions affect the warning in question.
172  //
173  // Traversing AST of a function is not a heavy operation, but for
174  // large functions with a lot of bugs it can make a dent in performance.
175  // In order to avoid this scenario, we cache traversal results.
176  auto InsertionResult = CachedSuppressionLocations.insert(
177  std::make_pair(DeclWithIssue, CachedRanges{}));
178  Ranges &SuppressionRanges = InsertionResult.first->second;
179  if (InsertionResult.second) {
180  // We haven't checked this declaration for suppressions yet!
181  CacheInitializer::initialize(DeclWithIssue, SuppressionRanges);
182  }
183 
184  SourceRange BugRange = Location.asRange();
185  const SourceManager &SM = Location.getManager();
186 
187  return llvm::any_of(SuppressionRanges,
188  [BugRange, &SM](SourceRange Suppression) {
189  return fullyContains(Suppression, BugRange, SM);
190  });
191 }
NodeId Parent
Definition: ASTDiff.cpp:191
DynTypedNode Node
#define SM(sm)
Definition: Cuda.cpp:83
static CharSourceRange getRange(const CharSourceRange &EditRange, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeMacroExpansion)
Definition: SourceCode.cpp:152
TranslationUnitDecl * getTranslationUnitDecl() const
Definition: ASTContext.h:1076
Attr - This represents one attribute.
Definition: Attr.h:46
Represents an attribute applied to a statement.
Definition: Stmt.h:2080
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
DeclContext * getLexicalDeclContext()
getLexicalDeclContext - The declaration context where this Decl was lexically declared (LexicalDC).
Definition: DeclBase.h:908
T * getAttr() const
Definition: DeclBase.h:579
SourceRange getSourceRange() const
For nodes which represent textual entities in the source code, return their SourceRange.
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Encodes a location in the source.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
bool isValid() const
This class provides an interface through which checkers can create individual bug reports.
Definition: BugReporter.h:119
virtual PathDiagnosticLocation getUniqueingLocation() const =0
Get the location on which the report should be uniqued.
virtual PathDiagnosticLocation getLocation() const =0
The primary location of the bug report that points at the undesirable behavior in the code.
virtual const Decl * getDeclWithIssue() const =0
The smallest declaration that contains the bug location.
bool isSuppressed(const BugReport &)
Return true if the given bug report was explicitly suppressed by the user.
PathDiagnosticRange asRange() const
const SourceManager & getManager() const
The JSON file list parser is used to communicate input to InstallAPI.
void initialize(TemplateInstantiationCallbackPtrs &Callbacks, const Sema &TheSema)