-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[analyzer] Fix [[clang::suppress]] for template instantiations #168954
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,27 @@ | ||
| // RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s | ||
| // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s | ||
|
|
||
| void clang_analyzer_warnIfReached(); | ||
|
|
||
| struct Clazz { | ||
| template <typename T> | ||
| static void templated_memfn(); | ||
| }; | ||
|
|
||
| // This must come before the 'templated_memfn' is defined! | ||
| static void instantiate() { | ||
| Clazz::templated_memfn<int>(); | ||
| } | ||
|
|
||
| template <typename T> | ||
| void Clazz::templated_memfn() { | ||
| // When we report a bug in a function, we traverse the lexical decl context | ||
| // of it while looking for suppression attributes to record what source | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm trying to understand how What would happen if I move
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You are right. Sharp eyes. I lied a bit because we also swap the decl context to the parent context, which is going to be the class definition itself, that has the template instantiations as child nodes in the AST - including the one where we have the suppression. However, if we skip traversing instantiations then we would not see the suppression. } else {
// This is the fast path. However, we should still consider the topmost
// declaration that isn't TranslationUnitDecl, because we should respect
// attributes on the entire declaration chain.
while (true) {
// Use the "lexical" parent. Eg., if the attribute is on a class, suppress
// warnings in inline methods but not in out-of-line methods.
const Decl *Parent =
dyn_cast_or_null<Decl>(DeclWithIssue->getLexicalDeclContext());
if (Parent == nullptr || isa<TranslationUnitDecl>(Parent))
break;
DeclWithIssue = Parent;
}
}
I've not tried, but I'm almost certain that having a suppress for a statement (such as your proposed attribute at that CallExpr) would only suppress issues raised within the source range of the effective statement (CallExpr). If it would suppress it, then that would be a bug in the suppression algorithm.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Aha, this explains it.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that's correct. FWIW it's quite valuable to have a *variant* of the suppress attribute that *would* work when applied at call site. This is particularly useful when the warning is positioned in a system header. Eg. you dereference a null smart pointer, and the warning is in the implementation of the overloaded dereference operator, so it'd make a lot of sense to put suppression at the call site of the dereference operator, so that you could keep getting warnings about the rest of null dereferences through that operator. But we probably shouldn't handle such cases by default. That's too coarse as it allows you to accidentally suppress bug reports across thousands of lines of code that you didn't even know were invoked from inside that source range. So we only want such a behavior under very specific circumstances.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Eh, I'm not sure. Wouldn't that mean that in practice most callsites would need that attribute? Like if there is a branch in that definition, that happens to be triggered from almost all callsites. At that point, it would be very inconvenient to apply a suppression attribute at the callsites. I agree that it feels niche.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there's a branch then there's probably an assertion failure on the null side of the branch. So there won't be a bug report and there's nothing to suppress. But it's very common to not have a branch, just dereference it blindly inside Basically if your code never uses raw pointers and only uses standard smart pointers (and our experimental explicit models for those standard smart pointers aren't turned on) then *every* null dereference warning would suffer from this problem, and *every* false positive among those otherwise reasonably-reliable warnings would need to be suppressed at the call site this way. (Ok ok not every warning. The call sites that perform dereference with But either way, as long as we believe that emitting warnings in the system headers is a good idea (simply because the root cause of the problem isn't necessarily in the system header), we have to accept that we need a way to suppress these warnings without touching system headers (when the root cause of the *false positive* isn't necessarily in the system header either). |
||
| // ranges should the suppression apply to. | ||
| // In the past, that traversal didn't follow template instantiations, only | ||
| // primary templates. | ||
| [[clang::suppress]] clang_analyzer_warnIfReached(); // no-warning | ||
|
|
||
| } | ||
|
|
||
| namespace [[clang::suppress]] | ||
| suppressed_namespace { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.