clang 24.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
13#include "llvm/ADT/STLExtras.h"
14#include "llvm/Support/FormatVariadic.h"
15#include "llvm/Support/TimeProfiler.h"
16
17using namespace clang;
18using namespace ento;
19
20namespace {
21
23
24inline bool hasSuppression(const Decl *D) {
25 // FIXME: Implement diagnostic identifier arguments
26 // (checker names, "hashtags").
27 if (const auto *Suppression = D->getAttr<SuppressAttr>())
28 return !Suppression->isGSL() &&
29 (Suppression->diagnosticIdentifiers().empty());
30 return false;
31}
32inline bool hasSuppression(const AttributedStmt *S) {
33 // FIXME: Implement diagnostic identifier arguments
34 // (checker names, "hashtags").
35 return llvm::any_of(S->getAttrs(), [](const Attr *A) {
36 const auto *Suppression = dyn_cast<SuppressAttr>(A);
37 return Suppression && !Suppression->isGSL() &&
38 (Suppression->diagnosticIdentifiers().empty());
39 });
40}
41
42template <class NodeType> inline SourceRange getRange(const NodeType *Node) {
43 return Node->getSourceRange();
44}
45template <> inline SourceRange getRange(const AttributedStmt *S) {
46 // Begin location for attributed statement node seems to be ALWAYS invalid.
47 //
48 // It is unlikely that we ever report any warnings on suppression
49 // attribute itself, but even if we do, we wouldn't want that warning
50 // to be suppressed by that same attribute.
51 //
52 // Long story short, we can use inner statement and it's not going to break
53 // anything.
54 return getRange(S->getSubStmt());
55}
56
57inline bool isLessOrEqual(SourceLocation LHS, SourceLocation RHS,
58 const SourceManager &SM) {
59 // SourceManager::isBeforeInTranslationUnit tests for strict
60 // inequality, when we need a non-strict comparison (bug
61 // can be reported directly on the annotated note).
62 // For this reason, we use the following equivalence:
63 //
64 // A <= B <==> !(B < A)
65 //
66 return !SM.isBeforeInTranslationUnit(RHS, LHS);
67}
68
69inline bool fullyContains(SourceRange Larger, SourceRange Smaller,
70 const SourceManager &SM) {
71 // Essentially this means:
72 //
73 // Larger.fullyContains(Smaller)
74 //
75 // However, that method has a very trivial implementation and couldn't
76 // compare regular locations and locations from macro expansions.
77 // We could've converted everything into regular locations as a solution,
78 // but the following solution seems to be the most bulletproof.
79 return isLessOrEqual(Larger.getBegin(), Smaller.getBegin(), SM) &&
80 isLessOrEqual(Smaller.getEnd(), Larger.getEnd(), SM);
81}
82
83class CacheInitializer : public DynamicRecursiveASTVisitor {
84public:
85 static void initialize(const Decl *D, Ranges &ToInit) {
86 CacheInitializer(ToInit).TraverseDecl(const_cast<Decl *>(D));
87 }
88
89 bool VisitDecl(Decl *D) override {
90 // Bug location could be somewhere in the init value of
91 // a freshly declared variable. Even though it looks like the
92 // user applied attribute to a statement, it will apply to a
93 // variable declaration, and this is where we check for it.
94 return VisitAttributedNode(D);
95 }
96
97 bool VisitAttributedStmt(AttributedStmt *AS) override {
98 // When we apply attributes to statements, it actually creates
99 // a wrapper statement that only contains attributes and the wrapped
100 // statement.
101 return VisitAttributedNode(AS);
102 }
103
104private:
105 template <class NodeType> bool VisitAttributedNode(NodeType *Node) {
106 if (hasSuppression(Node)) {
107 // TODO: In the future, when we come up with good stable IDs for checkers
108 // we can return a list of kinds to ignore, or all if no arguments
109 // were provided.
110 addRange(getRange(Node));
111 }
112 // We should keep traversing AST.
113 return true;
114 }
115
116 void addRange(SourceRange R) {
117 if (R.isValid()) {
118 Result.push_back(R);
119 }
120 }
121
122 CacheInitializer(Ranges &R) : Result(R) {
123 ShouldVisitTemplateInstantiations = true;
124 ShouldWalkTypesOfTypeLocs = false;
125 ShouldVisitImplicitCode = false;
126 ShouldVisitLambdaBody = true;
127 }
128 Ranges &Result;
129};
130
131std::string timeScopeName(const Decl *DeclWithIssue) {
132 if (!llvm::timeTraceProfilerEnabled())
133 return "";
134 return llvm::formatv(
135 "BugSuppression::isSuppressed init suppressions cache for {0}",
136 DeclWithIssue->getDeclKindName())
137 .str();
138}
139
140llvm::TimeTraceMetadata getDeclTimeTraceMetadata(const Decl *DeclWithIssue) {
141 assert(DeclWithIssue);
142 assert(llvm::timeTraceProfilerEnabled());
143 std::string Name = "<noname>";
144 if (const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
145 Name = ND->getNameAsString();
146 }
147 const auto &SM = DeclWithIssue->getASTContext().getSourceManager();
148 auto Line = SM.getPresumedLineNumber(DeclWithIssue->getBeginLoc());
149 auto Fname = SM.getFilename(DeclWithIssue->getBeginLoc());
150 return llvm::TimeTraceMetadata{std::move(Name), Fname.str(),
151 static_cast<int>(Line)};
152}
153
154} // end anonymous namespace
155
156// TODO: Introduce stable IDs for checkers and check for those here
157// to be more specific. Attribute without arguments should still
158// be considered as "suppress all".
159// It is already much finer granularity than what we have now
160// (i.e. removing the whole function from the analysis).
162 PathDiagnosticLocation Location = R.getLocation();
163 PathDiagnosticLocation UniqueingLocation = R.getUniqueingLocation();
164 const Decl *DeclWithIssue = R.getDeclWithIssue();
165
166 return isSuppressed(Location, DeclWithIssue, {}) ||
167 isSuppressed(UniqueingLocation, DeclWithIssue, {});
168}
169
170static const ClassTemplateDecl *
172 // For nested member templates (e.g., S2 inside S1<T>), getInstantiatedFrom
173 // may return the member template as instantiated within an outer
174 // specialization (e.g., S2 as it appears in S1<int>). That instantiated
175 // member template has no definition redeclaration itself; we need to walk
176 // up the member template chain to reach the primary template definition.
177 // \code
178 // template <class> struct S1 {
179 // template <class> struct S2 {
180 // int i;
181 // template <class T> int m(const S2<T>& s2) {
182 // return s2.i;
183 // }
184 // };
185 // }
186 // /code
187 const ClassTemplateDecl *MemberTmpl;
188 while ((MemberTmpl = Tmpl->getInstantiatedFromMemberTemplate())) {
189 if (Tmpl->isMemberSpecialization())
190 break;
191 Tmpl = MemberTmpl;
192 }
193 return Tmpl;
194}
195
197 const ClassTemplatePartialSpecializationDecl *PartialSpec) {
199 while ((MemberPS = PartialSpec->getInstantiatedFromMember())) {
200 if (PartialSpec->isMemberSpecialization())
201 break;
202 PartialSpec = MemberPS;
203 }
204 return PartialSpec;
205}
206
207template <class T> static const T *chooseDefinitionRedecl(const T *Tmpl) {
208 static_assert(llvm::is_one_of<T, ClassTemplateDecl,
210 for (const auto *Redecl : Tmpl->redecls()) {
211 if (const T *D = cast<T>(Redecl); D->isThisDeclarationADefinition()) {
212 return D;
213 }
214 }
215 assert(false && "This template must have a redecl that is a definition");
216 return Tmpl;
217}
218
219// For template specializations, returns the primary template definition or
220// partial specialization that was used to instantiate the specialization.
221// This ensures suppression attributes on templates apply to their
222// specializations.
223//
224// For example, given:
225// template <typename T> class [[clang::suppress]] Wrapper { ... };
226// Wrapper<int> w; // instantiates ClassTemplateSpecializationDecl
227//
228// When analyzing code in Wrapper<int>, this function maps the specialization
229// back to the primary template definition, allowing us to find the suppression
230// attribute.
231//
232// The function handles specializations (and partial specializations) of
233// class and function templates.
234// For any other decl, it returns the input unchagned.
235static const Decl *
237 // For function template specializations (including instantiated friend
238 // function templates), map back to the primary template's FunctionDecl so
239 // that the lexical parent chain walk reaches the class where the template
240 // was defined inline.
241 //
242 // This handles the case where a friend function template is defined inline
243 // inside a [[clang::suppress]]-annotated class but was pre-declared at
244 // namespace scope. In that case the instantiation's lexical DC is the
245 // namespace (from the pre-declaration), not the class. Walking back to the
246 // primary template FunctionDecl — whose lexical DC IS the class — lets the
247 // existing parent-chain walk find the suppression attribute.
248 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
249 if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern())
250 return Pattern->getDefinition();
251 }
252
253 const auto *SpecializationDecl = dyn_cast<ClassTemplateSpecializationDecl>(D);
254 if (!SpecializationDecl)
255 return D;
256
257 auto InstantiatedFrom = SpecializationDecl->getInstantiatedFrom();
258 if (!InstantiatedFrom)
259 return D;
260
261 if (const auto *Tmpl = dyn_cast<ClassTemplateDecl *>(InstantiatedFrom)) {
262 // Interestingly, the source template might be a forward declaration, so we
263 // need to find the definition redeclaration.
265 }
268}
269
271 const Decl *DeclWithIssue,
272 DiagnosticIdentifierList Hashtags) {
273 if (!Location.isValid())
274 return false;
275
276 if (!DeclWithIssue) {
277 // FIXME: This defeats the purpose of passing DeclWithIssue to begin with.
278 // If this branch is ever hit, we're re-doing all the work we've already
279 // done as well as perform a lot of work we'll never need.
280 // Gladly, none of our on-by-default checkers currently need it.
281 DeclWithIssue = ACtx.getTranslationUnitDecl();
282 } else {
283 // This is the fast path. However, we should still consider the topmost
284 // declaration that isn't TranslationUnitDecl, because we should respect
285 // attributes on the entire declaration chain.
286 while (true) {
287
288 // Template specializations (e.g., Wrapper<int>) should inherit
289 // suppression attributes from their primary template or partial
290 // specialization. Transform specializations to their template definitions
291 // before checking for suppressions or walking up the lexical parent
292 // chain.
293 // Simply taking the lexical parent of template specializations might land
294 // us in a completely different namespace.
295 DeclWithIssue =
297
298 // Use the "lexical" parent. Eg., if the attribute is on a class, suppress
299 // warnings in inline methods but not in out-of-line methods.
300 const Decl *Parent =
301 dyn_cast_or_null<Decl>(DeclWithIssue->getLexicalDeclContext());
302 if (Parent == nullptr || isa<TranslationUnitDecl>(Parent))
303 break;
304
305 DeclWithIssue = Parent;
306 }
307 }
308
309 // While some warnings are attached to AST nodes (mostly path-sensitive
310 // checks), others are simply associated with a plain source location
311 // or range. Figuring out the node based on locations can be tricky,
312 // so instead, we traverse the whole body of the declaration and gather
313 // information on ALL suppressions. After that we can simply check if
314 // any of those suppressions affect the warning in question.
315 //
316 // Traversing AST of a function is not a heavy operation, but for
317 // large functions with a lot of bugs it can make a dent in performance.
318 // In order to avoid this scenario, we cache traversal results.
319 auto InsertionResult = CachedSuppressionLocations.insert(
320 std::make_pair(DeclWithIssue, CachedRanges{}));
321 Ranges &SuppressionRanges = InsertionResult.first->second;
322 if (InsertionResult.second) {
323 llvm::TimeTraceScope TimeScope(
324 timeScopeName(DeclWithIssue),
325 [DeclWithIssue]() { return getDeclTimeTraceMetadata(DeclWithIssue); });
326 // We haven't checked this declaration for suppressions yet!
327 CacheInitializer::initialize(DeclWithIssue, SuppressionRanges);
328 }
329
330 SourceRange BugRange = Location.asRange();
331 const SourceManager &SM = Location.getManager();
332
333 return llvm::any_of(SuppressionRanges,
334 [BugRange, &SM](SourceRange Suppression) {
335 return fullyContains(Suppression, BugRange, SM);
336 });
337}
static const T * chooseDefinitionRedecl(const T *Tmpl)
static const ClassTemplateDecl * walkInstantiatedFromChain(const ClassTemplateDecl *Tmpl)
static const Decl * preferTemplateDefinitionForTemplateSpecializations(const Decl *D)
static CharSourceRange getRange(const CharSourceRange &EditRange, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeMacroExpansion)
Defines the SourceManager interface.
SourceManager & getSourceManager()
Definition ASTContext.h:913
Attr - This represents one attribute.
Definition Attr.h:46
Represents an attribute applied to a statement.
Definition Stmt.h:2215
Stmt * getSubStmt()
Definition Stmt.h:2251
ArrayRef< const Attr * > getAttrs() const
Definition Stmt.h:2247
Declaration of a class template.
ClassTemplateDecl * getInstantiatedFromMemberTemplate() const
ClassTemplatePartialSpecializationDecl * getInstantiatedFromMember() const
Retrieve the member class template partial specialization from which this particular class template p...
bool isMemberSpecialization() const
Determines whether this class template partial specialization template was a specialization of a memb...
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
T * getAttr() const
Definition DeclBase.h:583
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:550
const char * getDeclKindName() const
Definition DeclBase.cpp:169
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:441
TranslationUnitDecl * getTranslationUnitDecl()
Definition DeclBase.cpp:535
DeclContext * getLexicalDeclContext()
getLexicalDeclContext - The declaration context where this Decl was lexically declared (LexicalDC).
Definition DeclBase.h:937
Represents a function declaration or definition.
Definition Decl.h:2071
FunctionDecl * getTemplateInstantiationPattern(bool ForDefinition=true) const
Retrieve the function declaration from which this function could be instantiated, if it is an instant...
Definition Decl.cpp:4321
bool isMemberSpecialization() const
Determines whether this template was a specialization of a member template.
Encodes a location in the source.
This class handles loading and caching of source files into memory.
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
StringRef getFilename(SourceLocation SpellingLoc) const
Return the filename of the file containing a SourceLocation.
bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const
Determines the order of 2 source locations in the translation unit.
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.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
const FunctionProtoType * T
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
U cast(CodeGen::Address addr)
Definition Address.h:327