[clang] ef4f874 - [analyzer] Fix [[clang::suppress]] for friend function templates with namespace-scope forward-declarations (#187043)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 19 07:32:29 PDT 2026
Author: Balázs Benics
Date: 2026-03-19T14:32:25Z
New Revision: ef4f87425c51e22c66bf42c5ad5e8c41b0e15eae
URL: https://github.com/llvm/llvm-project/commit/ef4f87425c51e22c66bf42c5ad5e8c41b0e15eae
DIFF: https://github.com/llvm/llvm-project/commit/ef4f87425c51e22c66bf42c5ad5e8c41b0e15eae.diff
LOG: [analyzer] Fix [[clang::suppress]] for friend function templates with namespace-scope forward-declarations (#187043)
When a friend function template is defined inline inside a
[[clang::suppress]]-annotated class but was forward-declared at
namespace scope, the instantiation's lexical DeclContext was the
namespace (from the forward-declaration), not the class.
The lexical parent chain walk in BugSuppression::isSuppressed therefore
never reached the class and suppression did not apply.
Fix by extending preferTemplateDefinitionForTemplateSpecializations to
handle FunctionDecl instances: calling getTemplateInstantiationPattern()
that maps the instantiation back to the primary template FunctionDecl,
whose lexical DC is the class where the friend was defined inline.
So the existing parent-chain walk then finds the suppression attribute.
Assisted-By: claude
Added:
Modified:
clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
clang/test/Analysis/clang-suppress/friends.cpp
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp b/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
index 0e7b5b77a7e54..b06026984775a 100644
--- a/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
@@ -229,9 +229,26 @@ template <class T> static const T *chooseDefinitionRedecl(const T *Tmpl) {
// attribute.
//
// The function handles specializations (and partial specializations) of
-// class templates. For any other decl, it returns the input unchagned.
+// class and function templates.
+// For any other decl, it returns the input unchagned.
static const Decl *
preferTemplateDefinitionForTemplateSpecializations(const Decl *D) {
+ // For function template specializations (including instantiated friend
+ // function templates), map back to the primary template's FunctionDecl so
+ // that the lexical parent chain walk reaches the class where the template
+ // was defined inline.
+ //
+ // This handles the case where a friend function template is defined inline
+ // inside a [[clang::suppress]]-annotated class but was pre-declared at
+ // namespace scope. In that case the instantiation's lexical DC is the
+ // namespace (from the pre-declaration), not the class. Walking back to the
+ // primary template FunctionDecl — whose lexical DC IS the class — lets the
+ // existing parent-chain walk find the suppression attribute.
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ if (const FunctionDecl *Pattern = FD->getTemplateInstantiationPattern())
+ return Pattern;
+ }
+
const auto *SpecializationDecl = dyn_cast<ClassTemplateSpecializationDecl>(D);
if (!SpecializationDecl)
return D;
diff --git a/clang/test/Analysis/clang-suppress/friends.cpp b/clang/test/Analysis/clang-suppress/friends.cpp
index acd3193eca0e2..1f89567d399bd 100644
--- a/clang/test/Analysis/clang-suppress/friends.cpp
+++ b/clang/test/Analysis/clang-suppress/friends.cpp
@@ -153,8 +153,7 @@ extern void b3_suppressed(T);
struct [[clang::suppress]] B3_Suppressed {
template <typename T>
friend void b3_suppressed(T) {
- // FIXME: This should be suppressed.
- clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ clang_analyzer_warnIfReached(); // no-warning
}
};
template <typename T>
@@ -280,8 +279,7 @@ template <typename T> void e3_multi_redecl(T);
struct [[clang::suppress]] E3_Suppressed {
template <typename T>
friend void e3_multi_redecl(T) {
- // FIXME: This should be suppressed.
- clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ clang_analyzer_warnIfReached(); // no-warning
}
};
void test_E3() {
@@ -325,8 +323,7 @@ template <typename U>
struct [[clang::suppress]] E6_SuppressedTmpl {
template <typename T>
friend void e6_combined(T) {
- // FIXME: This should be suppressed.
- clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ clang_analyzer_warnIfReached(); // no-warning
}
};
void test_E6() {
@@ -356,8 +353,7 @@ template <typename T> void e8_fwd_multi(T);
struct [[clang::suppress]] E8_Suppressed {
template <typename T>
friend void e8_fwd_multi(T) {
- // FIXME: This should be suppressed.
- clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ clang_analyzer_warnIfReached(); // no-warning
}
};
void test_E8() {
More information about the cfe-commits
mailing list