clang 23.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#include "llvm/ADT/STLExtras.h"
13#include "llvm/Support/FormatVariadic.h"
14#include "llvm/Support/TimeProfiler.h"
15
16using namespace clang;
17using namespace ento;
18
19namespace {
20
22
23inline bool hasSuppression(const Decl *D) {
24 // FIXME: Implement diagnostic identifier arguments
25 // (checker names, "hashtags").
26 if (const auto *Suppression = D->getAttr<SuppressAttr>())
27 return !Suppression->isGSL() &&
28 (Suppression->diagnosticIdentifiers().empty());
29 return false;
30}
31inline bool hasSuppression(const AttributedStmt *S) {
32 // FIXME: Implement diagnostic identifier arguments
33 // (checker names, "hashtags").
34 return llvm::any_of(S->getAttrs(), [](const Attr *A) {
35 const auto *Suppression = dyn_cast<SuppressAttr>(A);
36 return Suppression && !Suppression->isGSL() &&
37 (Suppression->diagnosticIdentifiers().empty());
38 });
39}
40
41template <class NodeType> inline SourceRange getRange(const NodeType *Node) {
42 return Node->getSourceRange();
43}
44template <> inline SourceRange getRange(const AttributedStmt *S) {
45 // Begin location for attributed statement node seems to be ALWAYS invalid.
46 //
47 // It is unlikely that we ever report any warnings on suppression
48 // attribute itself, but even if we do, we wouldn't want that warning
49 // to be suppressed by that same attribute.
50 //
51 // Long story short, we can use inner statement and it's not going to break
52 // anything.
53 return getRange(S->getSubStmt());
54}
55
56inline bool isLessOrEqual(SourceLocation LHS, SourceLocation RHS,
57 const SourceManager &SM) {
58 // SourceManager::isBeforeInTranslationUnit tests for strict
59 // inequality, when we need a non-strict comparison (bug
60 // can be reported directly on the annotated note).
61 // For this reason, we use the following equivalence:
62 //
63 // A <= B <==> !(B < A)
64 //
65 return !SM.isBeforeInTranslationUnit(RHS, LHS);
66}
67
68inline bool fullyContains(SourceRange Larger, SourceRange Smaller,
69 const SourceManager &SM) {
70 // Essentially this means:
71 //
72 // Larger.fullyContains(Smaller)
73 //
74 // However, that method has a very trivial implementation and couldn't
75 // compare regular locations and locations from macro expansions.
76 // We could've converted everything into regular locations as a solution,
77 // but the following solution seems to be the most bulletproof.
78 return isLessOrEqual(Larger.getBegin(), Smaller.getBegin(), SM) &&
79 isLessOrEqual(Smaller.getEnd(), Larger.getEnd(), SM);
80}
81
82class CacheInitializer : public DynamicRecursiveASTVisitor {
83public:
84 static void initialize(const Decl *D, Ranges &ToInit) {
85 CacheInitializer(ToInit).TraverseDecl(const_cast<Decl *>(D));
86 }
87
88 bool VisitDecl(Decl *D) override {
89 // Bug location could be somewhere in the init value of
90 // a freshly declared variable. Even though it looks like the
91 // user applied attribute to a statement, it will apply to a
92 // variable declaration, and this is where we check for it.
93 return VisitAttributedNode(D);
94 }
95
96 bool VisitAttributedStmt(AttributedStmt *AS) override {
97 // When we apply attributes to statements, it actually creates
98 // a wrapper statement that only contains attributes and the wrapped
99 // statement.
100 return VisitAttributedNode(AS);
101 }
102
103private:
104 template <class NodeType> bool VisitAttributedNode(NodeType *Node) {
105 if (hasSuppression(Node)) {
106 // TODO: In the future, when we come up with good stable IDs for checkers
107 // we can return a list of kinds to ignore, or all if no arguments
108 // were provided.
109 addRange(getRange(Node));
110 }
111 // We should keep traversing AST.
112 return true;
113 }
114
115 void addRange(SourceRange R) {
116 if (R.isValid()) {
117 Result.push_back(R);
118 }
119 }
120
121 CacheInitializer(Ranges &R) : Result(R) {
122 ShouldVisitTemplateInstantiations = true;
123 ShouldWalkTypesOfTypeLocs = false;
124 ShouldVisitImplicitCode = false;
125 ShouldVisitLambdaBody = true;
126 }
127 Ranges &Result;
128};
129
130std::string timeScopeName(const Decl *DeclWithIssue) {
131 if (!llvm::timeTraceProfilerEnabled())
132 return "";
133 return llvm::formatv(
134 "BugSuppression::isSuppressed init suppressions cache for {0}",
135 DeclWithIssue->getDeclKindName())
136 .str();
137}
138
139llvm::TimeTraceMetadata getDeclTimeTraceMetadata(const Decl *DeclWithIssue) {
140 assert(DeclWithIssue);
141 assert(llvm::timeTraceProfilerEnabled());
142 std::string Name = "<noname>";
143 if (const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
144 Name = ND->getNameAsString();
145 }
146 const auto &SM = DeclWithIssue->getASTContext().getSourceManager();
147 auto Line = SM.getPresumedLineNumber(DeclWithIssue->getBeginLoc());
148 auto Fname = SM.getFilename(DeclWithIssue->getBeginLoc());
149 return llvm::TimeTraceMetadata{std::move(Name), Fname.str(),
150 static_cast<int>(Line)};
151}
152
153} // end anonymous namespace
154
155// TODO: Introduce stable IDs for checkers and check for those here
156// to be more specific. Attribute without arguments should still
157// be considered as "suppress all".
158// It is already much finer granularity than what we have now
159// (i.e. removing the whole function from the analysis).
161 PathDiagnosticLocation Location = R.getLocation();
162 PathDiagnosticLocation UniqueingLocation = R.getUniqueingLocation();
163 const Decl *DeclWithIssue = R.getDeclWithIssue();
164
165 return isSuppressed(Location, DeclWithIssue, {}) ||
166 isSuppressed(UniqueingLocation, DeclWithIssue, {});
167}
168
169// For template specializations, returns the primary template definition or
170// partial specialization that was used to instantiate the specialization.
171// This ensures suppression attributes on templates apply to their
172// specializations.
173//
174// For example, given:
175// template <typename T> class [[clang::suppress]] Wrapper { ... };
176// Wrapper<int> w; // instantiates ClassTemplateSpecializationDecl
177//
178// When analyzing code in Wrapper<int>, this function maps the specialization
179// back to the primary template definition, allowing us to find the suppression
180// attribute.
181//
182// The function handles specializations (and partial specializations) of
183// class templates.
184// For any other decl, it returns the input unchanged.
185static const Decl *
187 const auto *RD = dyn_cast<CXXRecordDecl>(D);
188 if (!RD)
189 return D;
190 if (const CXXRecordDecl *Pattern = RD->getTemplateInstantiationPattern())
191 RD = Pattern;
192 RD = RD->getDefinitionOrSelf();
193 if (const auto *CTD = RD->getDescribedClassTemplate())
194 return CTD;
195 return RD;
196}
197
199 const Decl *DeclWithIssue,
200 DiagnosticIdentifierList Hashtags) {
201 if (!Location.isValid())
202 return false;
203
204 if (!DeclWithIssue) {
205 // FIXME: This defeats the purpose of passing DeclWithIssue to begin with.
206 // If this branch is ever hit, we're re-doing all the work we've already
207 // done as well as perform a lot of work we'll never need.
208 // Gladly, none of our on-by-default checkers currently need it.
209 DeclWithIssue = ACtx.getTranslationUnitDecl();
210 } else {
211 // This is the fast path. However, we should still consider the topmost
212 // declaration that isn't TranslationUnitDecl, because we should respect
213 // attributes on the entire declaration chain.
214 while (true) {
215
216 // Template specializations (e.g., Wrapper<int>) should inherit
217 // suppression attributes from their primary template or partial
218 // specialization. Transform specializations to their template definitions
219 // before checking for suppressions or walking up the lexical parent
220 // chain.
221 // Simply taking the lexical parent of template specializations might land
222 // us in a completely different namespace.
223 DeclWithIssue =
225
226 // Use the "lexical" parent. Eg., if the attribute is on a class, suppress
227 // warnings in inline methods but not in out-of-line methods.
228 const Decl *Parent =
229 dyn_cast_or_null<Decl>(DeclWithIssue->getLexicalDeclContext());
230 if (Parent == nullptr || isa<TranslationUnitDecl>(Parent))
231 break;
232
233 DeclWithIssue = Parent;
234 }
235 }
236
237 // While some warnings are attached to AST nodes (mostly path-sensitive
238 // checks), others are simply associated with a plain source location
239 // or range. Figuring out the node based on locations can be tricky,
240 // so instead, we traverse the whole body of the declaration and gather
241 // information on ALL suppressions. After that we can simply check if
242 // any of those suppressions affect the warning in question.
243 //
244 // Traversing AST of a function is not a heavy operation, but for
245 // large functions with a lot of bugs it can make a dent in performance.
246 // In order to avoid this scenario, we cache traversal results.
247 auto InsertionResult = CachedSuppressionLocations.insert(
248 std::make_pair(DeclWithIssue, CachedRanges{}));
249 Ranges &SuppressionRanges = InsertionResult.first->second;
250 if (InsertionResult.second) {
251 llvm::TimeTraceScope TimeScope(
252 timeScopeName(DeclWithIssue),
253 [DeclWithIssue]() { return getDeclTimeTraceMetadata(DeclWithIssue); });
254 // We haven't checked this declaration for suppressions yet!
255 CacheInitializer::initialize(DeclWithIssue, SuppressionRanges);
256 }
257
258 SourceRange BugRange = Location.asRange();
259 const SourceManager &SM = Location.getManager();
260
261 return llvm::any_of(SuppressionRanges,
262 [BugRange, &SM](SourceRange Suppression) {
263 return fullyContains(Suppression, BugRange, SM);
264 });
265}
static const Decl * preferTemplateDefinitionForTemplateSpecializations(const Decl *D)
#define SM(sm)
static CharSourceRange getRange(const CharSourceRange &EditRange, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeMacroExpansion)
SourceManager & getSourceManager()
Definition ASTContext.h:863
Attr - This represents one attribute.
Definition Attr.h:46
Represents an attribute applied to a statement.
Definition Stmt.h:2213
Stmt * getSubStmt()
Definition Stmt.h:2249
ArrayRef< const Attr * > getAttrs() const
Definition Stmt.h:2245
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
const CXXRecordDecl * getTemplateInstantiationPattern() const
Retrieve the record declaration from which this record could be instantiated.
Definition DeclCXX.cpp:2084
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
T * getAttr() const
Definition DeclBase.h:581
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:547
const char * getDeclKindName() const
Definition DeclBase.cpp:169
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:439
TranslationUnitDecl * getTranslationUnitDecl()
Definition DeclBase.cpp:532
DeclContext * getLexicalDeclContext()
getLexicalDeclContext - The declaration context where this Decl was lexically declared (LexicalDC).
Definition DeclBase.h:931
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
This class provides an interface through which checkers can create individual bug reports.
llvm::ArrayRef< llvm::StringRef > DiagnosticIdentifierList
bool isSuppressed(const BugReport &)
Return true if the given bug report was explicitly suppressed by the user.
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
void initialize(TemplateInstantiationCallbackPtrs &Callbacks, const Sema &TheSema)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor