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() {


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to