Skip to content

Commit 017675f

Browse files
authored
[attributes][analyzer] Generalize [[clang::suppress]] to declarations. (llvm#80371)
The attribute is now allowed on an assortment of declarations, to suppress warnings related to declarations themselves, or all warnings in the lexical scope of the declaration. I don't necessarily see a reason to have a list at all, but it does look as if some of those more niche items aren't properly supported by the compiler itself so let's maintain a short safe list for now. The initial implementation raised a question whether the attribute should apply to lexical declaration context vs. "actual" declaration context. I'm using "lexical" here because it results in less warnings suppressed, which is the conservative behavior: we can always expand it later if we think this is wrong, without breaking any existing code. I also think that this is the correct behavior that we will probably never want to change, given that the user typically desires to keep the suppressions as localized as possible.
1 parent e06f352 commit 017675f

18 files changed

+251
-43
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2891,6 +2891,13 @@ def Suppress : DeclOrStmtAttr {
28912891
let Spellings = [CXX11<"gsl", "suppress">, Clang<"suppress">];
28922892
let Args = [VariadicStringArgument<"DiagnosticIdentifiers">];
28932893
let Accessors = [Accessor<"isGSL", [CXX11<"gsl", "suppress">]>];
2894+
// There's no fundamental reason why we can't simply accept all Decls
2895+
// but let's make a short list so that to avoid supporting something weird
2896+
// by accident. We can always expand the list later.
2897+
let Subjects = SubjectList<[
2898+
Stmt, Var, Field, ObjCProperty, Function, ObjCMethod, Record, ObjCInterface,
2899+
ObjCImplementation, Namespace, Empty
2900+
], ErrorDiag, "variables, functions, structs, interfaces, and namespaces">;
28942901
let Documentation = [SuppressDocs];
28952902
}
28962903

clang/include/clang/Basic/AttrDocs.td

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5321,6 +5321,29 @@ Putting the attribute on a compound statement suppresses all warnings in scope:
53215321
}
53225322
}
53235323

5324+
The attribute can also be placed on entire declarations of functions, classes,
5325+
variables, member variables, and so on, to suppress warnings related
5326+
to the declarations themselves. When used this way, the attribute additionally
5327+
suppresses all warnings in the lexical scope of the declaration:
5328+
5329+
.. code-block:: c++
5330+
5331+
class [[clang::suppress]] C {
5332+
int foo() {
5333+
int *x = nullptr;
5334+
...
5335+
return *x; // warnings suppressed in the entire class scope
5336+
}
5337+
5338+
int bar();
5339+
};
5340+
5341+
int C::bar() {
5342+
int *x = nullptr;
5343+
...
5344+
return *x; // warning NOT suppressed! - not lexically nested in 'class C{}'
5345+
}
5346+
53245347
Some static analysis warnings are accompanied by one or more notes, and the
53255348
line of code against which the warning is emitted isn't necessarily the best
53265349
for suppression purposes. In such cases the tools are allowed to implement

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2960,6 +2960,9 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
29602960
S.mergeHLSLNumThreadsAttr(D, *NT, NT->getX(), NT->getY(), NT->getZ());
29612961
else if (const auto *SA = dyn_cast<HLSLShaderAttr>(Attr))
29622962
NewAttr = S.mergeHLSLShaderAttr(D, *SA, SA->getType());
2963+
else if (const auto *SupA = dyn_cast<SuppressAttr>(Attr))
2964+
// Do nothing. Each redeclaration should be suppressed separately.
2965+
NewAttr = nullptr;
29632966
else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr))
29642967
NewAttr = cast<InheritableAttr>(Attr->clone(S.Context));
29652968

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5260,11 +5260,6 @@ static void handleSuppressAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
52605260
// Suppression attribute with GSL spelling requires at least 1 argument.
52615261
if (!AL.checkAtLeastNumArgs(S, 1))
52625262
return;
5263-
} else if (!isa<VarDecl>(D)) {
5264-
// Analyzer suppression applies only to variables and statements.
5265-
S.Diag(AL.getLoc(), diag::err_attribute_wrong_decl_type_str)
5266-
<< AL << 0 << "variables and statements";
5267-
return;
52685263
}
52695264

52705265
std::vector<StringRef> DiagnosticIdentifiers;

clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
161161

162162
PathDiagnosticLocation L =
163163
PathDiagnosticLocation::create(Ivar, BR.getSourceManager());
164-
BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
165-
os.str(), L);
164+
BR.EmitBasicReport(ID, Checker, "Unused instance variable",
165+
"Optimization", os.str(), L);
166166
}
167167
}
168168

clang/lib/StaticAnalyzer/Core/BugSuppression.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ class CacheInitializer : public RecursiveASTVisitor<CacheInitializer> {
8282
CacheInitializer(ToInit).TraverseDecl(const_cast<Decl *>(D));
8383
}
8484

85-
bool VisitVarDecl(VarDecl *VD) {
85+
bool VisitDecl(Decl *D) {
8686
// Bug location could be somewhere in the init value of
8787
// a freshly declared variable. Even though it looks like the
8888
// user applied attribute to a statement, it will apply to a
8989
// variable declaration, and this is where we check for it.
90-
return VisitAttributedNode(VD);
90+
return VisitAttributedNode(D);
9191
}
9292

9393
bool VisitAttributedStmt(AttributedStmt *AS) {
@@ -147,6 +147,20 @@ bool BugSuppression::isSuppressed(const PathDiagnosticLocation &Location,
147147
// done as well as perform a lot of work we'll never need.
148148
// Gladly, none of our on-by-default checkers currently need it.
149149
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+
}
150164
}
151165

152166
// While some warnings are attached to AST nodes (mostly path-sensitive

clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,17 @@ struct DerivedWithVirtualDtor : RefCntblBase {
1313
virtual ~DerivedWithVirtualDtor() {}
1414
};
1515

16+
// Confirm that the checker respects [[clang::suppress]]
17+
struct [[clang::suppress]] SuppressedDerived : RefCntblBase { };
18+
struct [[clang::suppress]] SuppressedDerivedWithVirtualDtor : RefCntblBase {
19+
virtual ~SuppressedDerivedWithVirtualDtor() {}
20+
};
1621

22+
// FIXME: Support attributes on base specifiers? Currently clang
23+
// doesn't support such attributes at all, even though it knows
24+
// how to parse them.
25+
//
26+
// struct SuppressedBaseSpecDerived : [[clang::suppress]] RefCntblBase { };
1727

1828
template<class T>
1929
struct DerivedClassTmpl : T { };

clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ void raw_ptr() {
1515
// CHECK-NEXT:{{^ | }} ^
1616
auto foo4 = [=](){ (void) ref_countable; };
1717
// CHECK: warning: Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
18+
19+
// Confirm that the checker respects [[clang::suppress]].
20+
RefCountable* suppressed_ref_countable = nullptr;
21+
[[clang::suppress]] auto foo5 = [suppressed_ref_countable](){};
22+
// CHECK-NOT: warning: Captured raw-pointer 'suppressed_ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]
1823
}
1924

2025
void references() {

clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class Foo {
6060
// expected-warning@-1{{Local variable 'baz' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
6161
auto *baz2 = this->provide_ref_ctnbl();
6262
// expected-warning@-1{{Local variable 'baz2' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
63+
[[clang::suppress]] auto *baz_suppressed = provide_ref_ctnbl(); // no-warning
6364
}
6465
};
6566
} // namespace auto_keyword

clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ namespace members {
88
RefCountable* a = nullptr;
99
// expected-warning@-1{{Member variable 'a' in 'members::Foo' is a raw pointer to ref-countable type 'RefCountable'}}
1010

11+
[[clang::suppress]]
12+
RefCountable* a_suppressed = nullptr;
13+
1114
protected:
1215
RefPtr<RefCountable> b;
1316

@@ -25,8 +28,14 @@ namespace members {
2528
};
2629

2730
void forceTmplToInstantiate(FooTmpl<RefCountable>) {}
31+
32+
struct [[clang::suppress]] FooSuppressed {
33+
private:
34+
RefCountable* a = nullptr;
35+
};
2836
}
2937

38+
3039
namespace ignore_unions {
3140
union Foo {
3241
RefCountable* a;

0 commit comments

Comments
 (0)