[clang] [analyzer][NFC] Reorg and add clang::suppress tests (PR #186447)
Balázs Benics via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 16 08:24:44 PDT 2026
https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/186447
>From efd45080697d53d287966048c24091f38e3729ea Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Fri, 13 Mar 2026 13:21:47 +0000
Subject: [PATCH 1/4] [analyzer][NFC] Reorg and add clang::suppress tests
Assisted-by: claude
---
.../class-template-specializations.cpp | 348 +++++++++++++++++
.../test/Analysis/clang-suppress/classes.cpp | 75 ++++
.../clang-suppress/diagnostic-identifiers.cpp | 115 ++++++
.../test/Analysis/clang-suppress/friends.cpp | 366 ++++++++++++++++++
.../clang-suppress/function-templates.cpp | 93 +++++
.../test/Analysis/clang-suppress/lambdas.cpp | 238 ++++++++++++
clang/test/Analysis/clang-suppress/macros.cpp | 186 +++++++++
.../Analysis/clang-suppress/namespaces.cpp | 35 ++
.../Analysis/clang-suppress/statements.cpp | 158 ++++++++
.../clang-suppress/template-methods.cpp | 132 +++++++
clang/test/Analysis/suppression-attr.cpp | 91 -----
11 files changed, 1746 insertions(+), 91 deletions(-)
create mode 100644 clang/test/Analysis/clang-suppress/class-template-specializations.cpp
create mode 100644 clang/test/Analysis/clang-suppress/classes.cpp
create mode 100644 clang/test/Analysis/clang-suppress/diagnostic-identifiers.cpp
create mode 100644 clang/test/Analysis/clang-suppress/friends.cpp
create mode 100644 clang/test/Analysis/clang-suppress/function-templates.cpp
create mode 100644 clang/test/Analysis/clang-suppress/lambdas.cpp
create mode 100644 clang/test/Analysis/clang-suppress/macros.cpp
create mode 100644 clang/test/Analysis/clang-suppress/namespaces.cpp
create mode 100644 clang/test/Analysis/clang-suppress/statements.cpp
create mode 100644 clang/test/Analysis/clang-suppress/template-methods.cpp
delete mode 100644 clang/test/Analysis/suppression-attr.cpp
diff --git a/clang/test/Analysis/clang-suppress/class-template-specializations.cpp b/clang/test/Analysis/clang-suppress/class-template-specializations.cpp
new file mode 100644
index 0000000000000..1d1202ca70ff7
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/class-template-specializations.cpp
@@ -0,0 +1,348 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// ============================================================================
+// Group A: Basic class template — attribute on primary
+// ============================================================================
+
+// Placeholder types for triggering instantiations.
+// - Type{A,B} should match an unconstrained template type parameter.
+// - Specialized{A,B} should match some specialization pattern.
+struct TypeA{};
+struct TypeB{};
+struct SpecializedA{};
+struct SpecializedB{};
+
+template <typename T>
+class [[clang::suppress]] A_Primary {
+public:
+ void inline_method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+ void outline_method();
+ static void static_inline() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+ static void static_outline();
+};
+
+template <typename T>
+void A_Primary<T>::outline_method() {
+ // Out-of-line: lexical context is namespace.
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+template <typename T>
+void A_Primary<T>::static_outline() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+void test_A() {
+ A_Primary<TypeA>().inline_method();
+ A_Primary<TypeA>().outline_method();
+ A_Primary<TypeA>::static_inline();
+ A_Primary<TypeA>::static_outline();
+ // Different instantiation.
+ A_Primary<TypeB>().inline_method();
+}
+
+// ============================================================================
+// Group B: Explicit full specialization — attribute isolation
+// ============================================================================
+
+// --- B1: attribute on primary only ---
+
+template <typename T>
+class [[clang::suppress]] B1_AttrOnPrimary {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+// Explicit specialization is independent — NOT suppressed.
+template <>
+struct B1_AttrOnPrimary<SpecializedA> {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_B1() {
+ B1_AttrOnPrimary<TypeA>().method(); // suppressed (primary)
+ B1_AttrOnPrimary<SpecializedA>().method(); // warns (spec, no attr)
+}
+
+// --- B2: attribute on specialization only ---
+
+template <typename T>
+struct B2_AttrOnSpec {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+template <>
+class [[clang::suppress]] B2_AttrOnSpec<SpecializedA> {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+void test_B2() {
+ B2_AttrOnSpec<TypeA>().method(); // warns (primary, no attr)
+ B2_AttrOnSpec<SpecializedA>().method(); // suppressed (spec has attr)
+}
+
+// --- B3: attribute on both primary and specialization ---
+
+template <typename T>
+class [[clang::suppress]] B3_AttrOnBoth {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+template <>
+class [[clang::suppress]] B3_AttrOnBoth<SpecializedA> {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+void test_B3() {
+ B3_AttrOnBoth<TypeA>().method(); // suppressed
+ B3_AttrOnBoth<SpecializedA>().method(); // suppressed
+}
+
+// ============================================================================
+// Group C: Partial specializations
+// ============================================================================
+
+// --- C1: attribute on partial specialization only ---
+
+template <typename T, typename U>
+struct C1_AttrOnPartial {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+template <typename T>
+class [[clang::suppress]] C1_AttrOnPartial<T, SpecializedA> {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+void test_C1() {
+ C1_AttrOnPartial<TypeA, TypeA>().method(); // warns (primary, no attr)
+ C1_AttrOnPartial<TypeA, SpecializedA>().method(); // suppressed (partial spec)
+}
+
+// --- C2: attribute on primary, partial spec has none ---
+
+template <typename T, typename U>
+class [[clang::suppress]] C2_AttrOnPrimary {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+template <typename T>
+struct C2_AttrOnPrimary<T, SpecializedA> {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_C2() {
+ C2_AttrOnPrimary<TypeA, TypeA>().method(); // suppressed (primary)
+ C2_AttrOnPrimary<TypeA, SpecializedA>().method(); // warns (partial spec, no attr)
+}
+
+// --- C3: two partial specializations, only one suppressed ---
+
+template <typename T, typename U>
+struct C3_TwoPartials {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+template <typename T>
+class [[clang::suppress]] C3_TwoPartials<T, SpecializedA> {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+template <typename T>
+struct C3_TwoPartials<T, SpecializedB> {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_C3() {
+ C3_TwoPartials<TypeA, TypeA>().method(); // warns (primary)
+ C3_TwoPartials<TypeA, SpecializedA>().method(); // suppressed (first partial)
+ C3_TwoPartials<TypeA, SpecializedB>().method(); // warns (second partial, no attr)
+}
+
+// ============================================================================
+// Group D: Forward-declared class template (chooseDefinitionRedecl path)
+// ============================================================================
+
+// The template is forward-declared, then defined. chooseDefinitionRedecl()
+// must find the definition among the redeclarations.
+
+// --- D1: Forward-declared without attribute, defined with attribute ---
+template <typename T>
+class D1_ForwardDeclared;
+
+template <typename T>
+class [[clang::suppress]] D1_ForwardDeclared {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+void test_D1() {
+ D1_ForwardDeclared<TypeA>().method();
+}
+
+// --- D2: Forward-declared without attribute, defined without attribute ---
+template <typename T>
+struct D2_ForwardDeclared_NoAttr;
+
+template <typename T>
+struct D2_ForwardDeclared_NoAttr {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_D2() {
+ D2_ForwardDeclared_NoAttr<TypeA>().method();
+}
+
+// ============================================================================
+// Group E: Specialization with out-of-line (OOL) methods
+// ============================================================================
+
+template <typename T>
+class [[clang::suppress]] E_SpecWithOOL {
+public:
+ void inline_method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+ void outline_method();
+};
+
+template <typename T>
+void E_SpecWithOOL<T>::outline_method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+// Explicit specialization with attribute and out-of-line method.
+template <>
+class [[clang::suppress]] E_SpecWithOOL<SpecializedA> {
+public:
+ void inline_method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+ void outline_method();
+};
+
+// Out-of-line for the specialization — not suppressed.
+void E_SpecWithOOL<SpecializedA>::outline_method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+void test_E() {
+ E_SpecWithOOL<TypeA>().inline_method();
+ E_SpecWithOOL<TypeA>().outline_method();
+ E_SpecWithOOL<SpecializedA>().inline_method();
+ E_SpecWithOOL<SpecializedA>().outline_method();
+}
+
+// ============================================================================
+// Group F: Nested class inside class template specialization
+// ============================================================================
+
+template <typename T>
+class [[clang::suppress]] F_Outer {
+public:
+ struct Inner {
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+ };
+};
+
+template <typename T>
+struct F_Outer_NoAttr {
+ struct Inner {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+ };
+};
+
+void test_F() {
+ F_Outer<TypeA>::Inner().method();
+ F_Outer_NoAttr<TypeA>::Inner().method();
+}
+
+// ============================================================================
+// Group G: Class template with default template arguments
+// ============================================================================
+
+template <typename T, typename U = TypeA>
+class [[clang::suppress]] G_WithDefault {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+template <typename T, typename U = TypeA>
+struct G_WithDefault_NoAttr {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_G() {
+ G_WithDefault<TypeA>().method(); // uses default U=TypeA
+ G_WithDefault<TypeA, TypeB>().method(); // explicit U=TypeB
+ G_WithDefault_NoAttr<TypeA>().method(); // uses default U=TypeA
+}
+
+// ============================================================================
+// Group H: Explicit instantiation directive
+// ============================================================================
+
+template <typename T>
+class [[clang::suppress]] H_ExplicitInst {
+public:
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+// Explicit instantiation.
+template class H_ExplicitInst<SpecializedA>;
+
+void test_H() {
+ H_ExplicitInst<SpecializedA>().method();
+}
diff --git a/clang/test/Analysis/clang-suppress/classes.cpp b/clang/test/Analysis/clang-suppress/classes.cpp
new file mode 100644
index 0000000000000..4c285880c86ea
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/classes.cpp
@@ -0,0 +1,75 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// Systematic tests for [[clang::suppress]] on non-template classes and methods.
+
+// ============================================================================
+// Group A: Attribute on class — inline method suppressed, out-of-line not
+// ============================================================================
+
+// Placeholder type for triggering instantiations.
+struct Type{};
+
+class [[clang::suppress]] SuppressedClass {
+ void foo() {
+ clang_analyzer_warnIfReached(); // no-warning: inline method in suppressed class
+ }
+
+ void bar();
+};
+
+void SuppressedClass::bar() {
+ clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+// ============================================================================
+// Group B: Attribute on method declaration vs definition
+// ============================================================================
+
+class SuppressedMethodClass {
+ // Attribute on the inline definition — suppressed.
+ [[clang::suppress]] void foo() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+
+ // Attribute on the in-class declaration only — NOT honored at out-of-line def.
+ [[clang::suppress]] void bar1();
+
+ // No attribute on the in-class declaration.
+ void bar2();
+};
+
+void SuppressedMethodClass::bar1() {
+ clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+// Attribute on the out-of-line definition — suppressed.
+[[clang::suppress]]
+void SuppressedMethodClass::bar2() {
+ clang_analyzer_warnIfReached(); // no-warning
+}
+
+// ============================================================================
+// Group C: Template member function with early instantiation
+// ============================================================================
+
+// The suppression mechanism walks the lexical DeclContext chain to find
+// suppression attributes. This test verifies that the walk follows template
+// instantiation patterns (not just primary templates) when the instantiation
+// point precedes the definition.
+
+struct Clazz {
+ template <typename T>
+ static void templated_memfn();
+};
+
+// This must come before the 'templated_memfn' is defined!
+void instantiate() {
+ Clazz::templated_memfn<Type>();
+}
+
+template <typename T>
+void Clazz::templated_memfn() {
+ [[clang::suppress]] clang_analyzer_warnIfReached(); // no-warning
+}
diff --git a/clang/test/Analysis/clang-suppress/diagnostic-identifiers.cpp b/clang/test/Analysis/clang-suppress/diagnostic-identifiers.cpp
new file mode 100644
index 0000000000000..293f6fe12021c
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/diagnostic-identifiers.cpp
@@ -0,0 +1,115 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// Tests for [[clang::suppress]] with diagnostic identifier arguments.
+
+// ============================================================================
+// Group A: Bare [[clang::suppress]] vs. with identifier
+// ============================================================================
+
+void bare_suppress() {
+ [[clang::suppress]] {
+ clang_analyzer_warnIfReached(); // no-warning: bare suppress works
+ }
+}
+
+void suppress_with_identifier() {
+ // FIXME: This should suppress debug.ExprInspection warnings, but currently
+ // any identifier makes the suppression a no-op.
+ [[clang::suppress("debug.ExprInspection")]] {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+}
+
+void suppress_with_wrong_identifier() {
+ // Even with the wrong checker name, the current behavior is the same:
+ // any identifier makes the suppression a no-op.
+ [[clang::suppress("alpha.SomeOtherChecker")]] {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+}
+
+// ============================================================================
+// Group B: Identifier on declarations
+// ============================================================================
+
+[[clang::suppress]] void decl_bare_suppress() {
+ clang_analyzer_warnIfReached(); // no-warning
+}
+
+// FIXME: Should suppress, but currently identifiers disable suppression.
+[[clang::suppress("debug.ExprInspection")]] void decl_with_identifier() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+// ============================================================================
+// Group C: Identifier on class
+// ============================================================================
+
+struct [[clang::suppress]] C_BareSuppressedClass {
+ void method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+// FIXME: Should suppress, but identifiers disable suppression.
+struct [[clang::suppress("core")]] C_IdentifierSuppressedClass {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+// ============================================================================
+// Group D: Multiple identifiers
+// ============================================================================
+
+void multiple_identifiers() {
+ // FIXME: Multiple identifiers — currently treated as a no-op.
+ [[clang::suppress("core.NullDereference", "core.DivideZero")]] {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+}
+
+// ============================================================================
+// Group E: Empty string identifier
+// ============================================================================
+
+void empty_string_identifier() {
+ // An empty string is still a non-empty identifier list.
+ [[clang::suppress("")]] {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+}
+
+// ============================================================================
+// Group F: Mixed — bare suppress and identifier suppress in same function
+// ============================================================================
+
+void mixed_suppressions() {
+ [[clang::suppress]] {
+ clang_analyzer_warnIfReached(); // no-warning: bare suppress works
+ }
+
+ // FIXME: Should suppress too, but identifiers disable it.
+ [[clang::suppress("debug.ExprInspection")]] {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+}
+
+// ============================================================================
+// Group G: Identifier on namespace
+// ============================================================================
+
+namespace [[clang::suppress]] G_BareNS {
+ void func() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+} // namespace G_BareNS
+
+// FIXME: Should suppress, but identifiers disable it.
+namespace [[clang::suppress("core")]] G_IdentifierNS {
+ void func() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+} // namespace G_IdentifierNS
diff --git a/clang/test/Analysis/clang-suppress/friends.cpp b/clang/test/Analysis/clang-suppress/friends.cpp
new file mode 100644
index 0000000000000..430699aa2d8b2
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/friends.cpp
@@ -0,0 +1,366 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// Systematic tests for [[clang::suppress]] on classes with friend declarations.
+//
+// Pruned matrix of valid combinations:
+// Axis 1: fwd-decl at namespace scope (yes / no)
+// Axis 2: body location (inline / out-of-line)
+// Axis 3: template (yes / no)
+//
+// Each case has a suppressed variant (class has [[clang::suppress]])
+// and an unsuppressed variant (class without it).
+
+// Placeholder types for triggering instantiations.
+// - Type{A,B} should match an unconstrained template type parameter.
+// - Specialized should match some specialization pattern.
+struct TypeA{};
+struct TypeB{};
+struct Specialized{};
+
+// ============================================================================
+// Group A: Non-template friend functions
+// ============================================================================
+
+// --- A1: no fwd-decl, inline body ---
+
+struct [[clang::suppress]] A1_Suppressed {
+ friend void a1_suppressed(A1_Suppressed) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+struct A1_Unsuppressed {
+ friend void a1_unsuppressed(A1_Unsuppressed) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+void test_A1() {
+ a1_suppressed(A1_Suppressed{});
+ a1_unsuppressed(A1_Unsuppressed{});
+}
+
+// --- A2: no fwd-decl, out-of-line body ---
+
+struct [[clang::suppress]] A2_Suppressed {
+ friend void a2_suppressed(A2_Suppressed);
+};
+void a2_suppressed(A2_Suppressed) {
+ // Out-of-line: lexical parent is the namespace, NOT the class.
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+struct A2_Unsuppressed {
+ friend void a2_unsuppressed(A2_Unsuppressed);
+};
+void a2_unsuppressed(A2_Unsuppressed) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+void test_A2() {
+ a2_suppressed(A2_Suppressed{});
+ a2_unsuppressed(A2_Unsuppressed{});
+}
+
+// --- A3: fwd-decl, inline body ---
+
+extern void a3_suppressed();
+struct [[clang::suppress]] A3_Suppressed {
+ friend void a3_suppressed() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+extern void a3_unsuppressed();
+struct A3_Unsuppressed {
+ friend void a3_unsuppressed() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+void test_A3() {
+ a3_suppressed();
+ a3_unsuppressed();
+}
+
+// --- A4: fwd-decl, out-of-line body ---
+
+extern void a4_suppressed();
+struct [[clang::suppress]] A4_Suppressed {
+ friend void a4_suppressed();
+};
+void a4_suppressed() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+extern void a4_unsuppressed();
+struct A4_Unsuppressed {
+ friend void a4_unsuppressed();
+};
+void a4_unsuppressed() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+void test_A4() {
+ a4_suppressed();
+ a4_unsuppressed();
+}
+
+// ============================================================================
+// Group B: Friend function templates (primary template)
+// ============================================================================
+
+// --- B1: no fwd-decl, inline body ---
+
+struct [[clang::suppress]] B1_Suppressed {
+ template <typename T>
+ friend void b1_suppressed(B1_Suppressed, T) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+struct B1_Unsuppressed {
+ template <typename T>
+ friend void b1_unsuppressed(B1_Unsuppressed, T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+void test_B1() {
+ b1_suppressed(B1_Suppressed{}, TypeA{});
+ b1_unsuppressed(B1_Unsuppressed{}, TypeA{});
+}
+
+// --- B2: no fwd-decl, out-of-line body ---
+
+struct [[clang::suppress]] B2_Suppressed {
+ template <typename T>
+ friend void b2_suppressed(B2_Suppressed, T);
+};
+template <typename T>
+void b2_suppressed(B2_Suppressed, T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+struct B2_Unsuppressed {
+ template <typename T>
+ friend void b2_unsuppressed(B2_Unsuppressed, T);
+};
+template <typename T>
+void b2_unsuppressed(B2_Unsuppressed, T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+void test_B2() {
+ b2_suppressed(B2_Suppressed{}, TypeA{});
+ b2_unsuppressed(B2_Unsuppressed{}, TypeA{});
+}
+
+// --- B3: fwd-decl, inline body ---
+
+template <typename T>
+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}}
+ }
+};
+template <typename T>
+extern void b3_unsuppressed(T);
+struct B3_Unsuppressed {
+ template <typename T>
+ friend void b3_unsuppressed(T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+void test_B3() {
+ b3_suppressed(TypeA{});
+ b3_unsuppressed(TypeA{});
+}
+
+// --- B4: fwd-decl, out-of-line body ---
+
+template <typename T>
+extern void b4_suppressed(T);
+struct [[clang::suppress]] B4_Suppressed {
+ template <typename T>
+ friend void b4_suppressed(T);
+};
+template <typename T>
+void b4_suppressed(T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+template <typename T>
+extern void b4_unsuppressed(T);
+struct B4_Unsuppressed {
+ template <typename T>
+ friend void b4_unsuppressed(T);
+};
+template <typename T>
+void b4_unsuppressed(T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+void test_B4() {
+ b4_suppressed(TypeA{});
+ b4_unsuppressed(TypeA{});
+}
+
+// ============================================================================
+// Group C: Friend function template explicit specializations
+// ============================================================================
+
+// --- C1: primary inline in suppressed class, explicit spec defined out-of-line ---
+// The explicit specialization is NOT defined inside the class, so it should
+// NOT be suppressed.
+
+struct [[clang::suppress]] C1_Suppressed {
+ template <typename T>
+ friend void c1_suppressed(C1_Suppressed, T) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+template <>
+void c1_suppressed(C1_Suppressed, Specialized) {
+ // Explicit specialization defined outside the class — not suppressed.
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+void test_C1() {
+ c1_suppressed(C1_Suppressed{}, TypeA{}); // uses primary (suppressed)
+ c1_suppressed(C1_Suppressed{}, Specialized{}); // uses explicit spec (not suppressed)
+}
+
+// ============================================================================
+// Group D: Friend classes (declared, not defined inline — C++ forbids
+// defining a type in a friend declaration)
+// ============================================================================
+
+// --- D1: friend class only declared, defined outside ---
+
+struct [[clang::suppress]] D1_Suppressed {
+ friend struct D1_FriendOuter;
+};
+struct D1_FriendOuter {
+ void method() {
+ // Defined outside the suppressed class — not suppressed.
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+void test_D1() {
+ D1_FriendOuter{}.method();
+}
+
+// ============================================================================
+// Group E: Edge cases
+// ============================================================================
+
+// --- E1: friend function in suppressed CLASS TEMPLATE (not just suppressed class) ---
+
+template <typename U>
+struct [[clang::suppress]] E1_SuppressedTmpl {
+ friend void e1_friend(E1_SuppressedTmpl) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+void test_E1() {
+ e1_friend(E1_SuppressedTmpl<TypeA>{});
+}
+
+// --- E2: friend function template in a nested suppressed class ---
+// The friend needs a parameter of the nested class type for ADL lookup.
+
+struct Outer_E2 {
+ struct [[clang::suppress]] Inner_E2 {
+ template <typename T>
+ friend void e2_inner_friend(Inner_E2, T) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+ };
+};
+void test_E2() {
+ e2_inner_friend(Outer_E2::Inner_E2{}, TypeA{});
+}
+
+// --- E3: multiple redeclarations at namespace scope before friend decl ---
+
+template <typename T> void e3_multi_redecl(T);
+template <typename T> void e3_multi_redecl(T);
+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}}
+ }
+};
+void test_E3() {
+ e3_multi_redecl(TypeA{});
+}
+
+// --- E4: friend in anonymous namespace ---
+
+namespace {
+struct [[clang::suppress]] E4_AnonSuppressed {
+ friend void e4_anon_friend(E4_AnonSuppressed) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+} // namespace
+void test_E4() {
+ e4_anon_friend(E4_AnonSuppressed{});
+}
+
+// --- E5: suppression on the friend declaration itself, not on the class ---
+// Friend functions need a parameter for ADL visibility.
+
+struct E5_ClassNotSuppressed {
+ [[clang::suppress]] friend void e5_suppressed(E5_ClassNotSuppressed) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+ friend void e5_unsuppressed(E5_ClassNotSuppressed) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+void test_E5() {
+ e5_suppressed(E5_ClassNotSuppressed{});
+ e5_unsuppressed(E5_ClassNotSuppressed{});
+}
+
+// --- E6: friend function template in suppressed class template, with fwd-decl ---
+// Combines class template + function template + fwd-decl.
+
+template <typename T> void e6_combined(T);
+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}}
+ }
+};
+void test_E6() {
+ // Instantiate the class template to make the friend visible.
+ E6_SuppressedTmpl<TypeA> e6; // This line is IMPORTANT!
+ (void)e6;
+ e6_combined(TypeA{});
+}
+
+// --- E7: friend function template instantiated with multiple different types ---
+// Ensure suppression applies to ALL instantiations, not just one.
+
+struct [[clang::suppress]] E7_Suppressed {
+ template <typename T>
+ friend void e7_multi_inst(E7_Suppressed, T) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+void test_E7() {
+ e7_multi_inst(E7_Suppressed{}, TypeA{});
+ e7_multi_inst(E7_Suppressed{}, TypeB{});
+}
+
+// --- E8: friend function template with fwd-decl, instantiated with multiple types ---
+
+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}}
+ }
+};
+void test_E8() {
+ e8_fwd_multi(TypeA{});
+ e8_fwd_multi(TypeB{});
+}
diff --git a/clang/test/Analysis/clang-suppress/function-templates.cpp b/clang/test/Analysis/clang-suppress/function-templates.cpp
new file mode 100644
index 0000000000000..dda7700fe7ae1
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/function-templates.cpp
@@ -0,0 +1,93 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// Systematic tests for [[clang::suppress]] on function templates and their
+// explicit specializations.
+
+// Placeholder types for triggering instantiations.
+// - Type should match an unconstrained template type parameter.
+// - Specialized should match a specialization pattern.
+struct Type{};
+struct Specialized{};
+
+// ============================================================================
+// Group A: Attribute on forward declaration only — NOT honored at definition
+// ============================================================================
+
+template <typename T> [[clang::suppress]] void FunctionTemplateSuppressed(T);
+template <typename T>
+void FunctionTemplateSuppressed(T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+template <typename T>
+void FunctionTemplateUnsuppressed(T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+void test_fwd_decl_attr() {
+ FunctionTemplateSuppressed(Type{});
+ FunctionTemplateUnsuppressed(Type{});
+}
+
+// ============================================================================
+// Group B: Explicit full function specialization — attribute on specialization
+// ============================================================================
+
+// Only the Specialized specialization is suppressed.
+template <typename T>
+void ExplicitSpecAttrOnSpec(T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+template <>
+[[clang::suppress]] void ExplicitSpecAttrOnSpec(Specialized) {
+ clang_analyzer_warnIfReached(); // no-warning
+}
+
+void test_attr_on_spec() {
+ ExplicitSpecAttrOnSpec(Type{}); // warns (primary)
+ ExplicitSpecAttrOnSpec(Specialized{}); // suppressed (explicit specialization)
+}
+
+// ============================================================================
+// Group C: Explicit full function specialization — attribute on primary
+// ============================================================================
+
+// Only the primary template is suppressed.
+template <typename T>
+[[clang::suppress]] void ExplicitSpecAttrOnPrimary(T) {
+ clang_analyzer_warnIfReached(); // no-warning
+}
+
+template <>
+void ExplicitSpecAttrOnPrimary(Specialized) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+void test_attr_on_primary() {
+ ExplicitSpecAttrOnPrimary(Type{}); // suppressed (primary)
+ ExplicitSpecAttrOnPrimary(Specialized{}); // warns (explicit specialization)
+}
+
+// ============================================================================
+// Group D: Variadic template with suppress + explicit specialization override
+// ============================================================================
+
+template <typename... Args>
+[[clang::suppress]] void Variadic_Suppressed(Args...) {
+ clang_analyzer_warnIfReached(); // no-warning
+}
+
+// Variadic template function specialization — NOT suppressed.
+template <>
+void Variadic_Suppressed(Type, Specialized) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+void test_variadic() {
+ Variadic_Suppressed();
+ Variadic_Suppressed(Type{});
+ Variadic_Suppressed(Type{}, Specialized{});
+}
diff --git a/clang/test/Analysis/clang-suppress/lambdas.cpp b/clang/test/Analysis/clang-suppress/lambdas.cpp
new file mode 100644
index 0000000000000..864069f957487
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/lambdas.cpp
@@ -0,0 +1,238 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// Systematic tests for [[clang::suppress]] interaction with lambdas.
+
+// Placeholder type for triggering instantiations.
+struct Type{};
+
+// ============================================================================
+// Group A: Lambda in suppressed statement block
+// ============================================================================
+
+void lambda_in_suppressed_block() {
+ [[clang::suppress]] {
+ auto lam = []() {
+ clang_analyzer_warnIfReached(); // no-warning
+ };
+ lam();
+ }
+}
+
+void lambda_in_unsuppressed_block() {
+ auto lam = []() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ };
+ lam();
+}
+
+// ============================================================================
+// Group B: Lambda in suppressed class method
+// ============================================================================
+
+struct [[clang::suppress]] B_SuppressedClass {
+ void method_with_lambda() {
+ auto lam = []() {
+ clang_analyzer_warnIfReached(); // no-warning
+ };
+ lam();
+ }
+};
+
+struct B_UnsuppressedClass {
+ void method_with_lambda() {
+ auto lam = []() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ };
+ lam();
+ }
+};
+
+void test_B() {
+ B_SuppressedClass().method_with_lambda();
+ B_UnsuppressedClass().method_with_lambda();
+}
+
+// ============================================================================
+// Group C: Nested lambdas
+// ============================================================================
+
+void nested_lambda_suppressed() {
+ [[clang::suppress]] {
+ auto outer = []() {
+ auto inner = []() {
+ clang_analyzer_warnIfReached(); // no-warning
+ };
+ return inner();
+ };
+ return outer(); // no-warning
+ }
+}
+
+void nested_lambda_unsuppressed() {
+ auto outer = []() {
+ auto inner = []() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ };
+ inner();
+ };
+ outer();
+}
+
+// ============================================================================
+// Group D: Lambda with captures
+// ============================================================================
+
+int lambda_with_ref_capture_suppressed() {
+ int *x = 0;
+ [[clang::suppress]] {
+ auto lam = [&x]() {
+ return *x;
+ };
+ return lam(); // no-warning
+ }
+}
+
+int lambda_with_ref_capture_unsuppressed() {
+ int *x = 0;
+ auto lam = [&x]() {
+ return *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+ };
+ return lam();
+}
+
+int lambda_capture_by_value_suppressed() {
+ int *x = 0;
+ [[clang::suppress]] {
+ auto lam = [x]() {
+ return *x;
+ };
+ return lam(); // no-warning
+ }
+}
+
+// ============================================================================
+// Group E: Lambda in suppressed namespace
+// ============================================================================
+
+namespace [[clang::suppress]] SuppressedNS {
+ void func_with_lambda() {
+ auto lam = []() {
+ clang_analyzer_warnIfReached(); // no-warning
+ };
+ lam();
+ }
+} // namespace SuppressedNS
+
+// ============================================================================
+// Group F: Suppressed lambda, unsuppressed enclosing
+// ============================================================================
+
+void selective_suppression_unsup() {
+ auto unsuppressed_lam = []() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ };
+ unsuppressed_lam();
+}
+
+void selective_suppression_sup() {
+ [[clang::suppress]] auto suppressed_lam = []() {
+ clang_analyzer_warnIfReached(); // no-warning
+ };
+ suppressed_lam();
+}
+
+// ============================================================================
+// Group G: Lambda in template function
+// ============================================================================
+
+template <typename T>
+[[clang::suppress]] void tmpl_func_with_lambda(T) {
+ auto lam = []() {
+ clang_analyzer_warnIfReached(); // no-warning
+ };
+ lam();
+}
+
+template <typename T>
+void tmpl_func_with_lambda_unsuppressed(T) {
+ auto lam = []() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ };
+ lam();
+}
+
+void test_G() {
+ tmpl_func_with_lambda(Type{});
+ tmpl_func_with_lambda_unsuppressed(Type{});
+}
+
+// ============================================================================
+// Group H: Lambda in suppressed class template
+// ============================================================================
+
+template <typename T>
+struct [[clang::suppress]] H_SuppressedTmpl {
+ void method() {
+ auto lam = []() {
+ clang_analyzer_warnIfReached(); // no-warning
+ };
+ lam();
+ }
+};
+
+template <typename T>
+struct H_UnsuppressedTmpl {
+ void method() {
+ auto lam = []() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ };
+ lam();
+ }
+};
+
+void test_H() {
+ H_SuppressedTmpl<Type>().method();
+ H_UnsuppressedTmpl<Type>().method();
+}
+
+// ============================================================================
+// Group I: Immediately-invoked lambda expression
+// ============================================================================
+
+int iile_suppressed() {
+ [[clang::suppress]] {
+ return []() {
+ int *x = 0;
+ return *x;
+ }(); // no-warning
+ }
+}
+
+int iile_unsuppressed() {
+ return []() {
+ int *x = 0;
+ return *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+ }();
+}
+
+// ============================================================================
+// Group J: Generic lambda (C++14)
+// ============================================================================
+
+void generic_lambda_suppressed() {
+ [[clang::suppress]] {
+ auto lam = [](auto) {
+ clang_analyzer_warnIfReached(); // no-warning
+ };
+ lam(Type{});
+ }
+}
+
+void generic_lambda_unsuppressed() {
+ auto lam = [](auto) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ };
+ lam(Type{});
+}
diff --git a/clang/test/Analysis/clang-suppress/macros.cpp b/clang/test/Analysis/clang-suppress/macros.cpp
new file mode 100644
index 0000000000000..7458484a33b0a
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/macros.cpp
@@ -0,0 +1,186 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// Systematic tests for [[clang::suppress]] interaction with macros.
+//
+// The fullyContains() function compares source ranges using
+// SourceManager::isBeforeInTranslationUnit, which handles macro
+// expansion locations. These tests verify that suppression works
+// correctly when bugs are reported inside macro expansions.
+
+// Placeholder type for triggering instantiations.
+struct Type{};
+
+// ============================================================================
+// Group A: Bug inside macro, suppression outside (using warnIfReached)
+// ============================================================================
+
+#define WARN clang_analyzer_warnIfReached()
+
+void macro_in_suppressed_block() {
+ [[clang::suppress]] {
+ WARN; // no-warning
+ }
+}
+
+void macro_in_unsuppressed_block() {
+ WARN; // expected-warning{{REACHABLE}}
+}
+
+// ============================================================================
+// Group B: Function-like macro with expression
+// ============================================================================
+
+#define DO_WARN() clang_analyzer_warnIfReached()
+
+void funclike_macro_suppressed() {
+ [[clang::suppress]] {
+ DO_WARN(); // no-warning
+ }
+}
+
+void funclike_macro_unsuppressed() {
+ DO_WARN(); // expected-warning{{REACHABLE}}
+}
+
+// ============================================================================
+// Group C: Nested macros
+// ============================================================================
+
+#define INNER_WARN() clang_analyzer_warnIfReached()
+#define OUTER_WARN() INNER_WARN()
+
+void nested_macro_suppressed() {
+ [[clang::suppress]] {
+ OUTER_WARN(); // no-warning
+ }
+}
+
+void nested_macro_unsuppressed() {
+ OUTER_WARN(); // expected-warning{{REACHABLE}}
+}
+
+// ============================================================================
+// Group D: Macro defining entire function body
+// ============================================================================
+
+#define BUGGY_BODY { clang_analyzer_warnIfReached(); }
+
+[[clang::suppress]] void func_with_macro_body()
+ BUGGY_BODY // no-warning
+
+void func_with_macro_body_unsuppressed()
+ BUGGY_BODY // expected-warning{{REACHABLE}}
+
+// ============================================================================
+// Group E: Macro in suppressed class method
+// ============================================================================
+
+struct [[clang::suppress]] MacroInSuppressedClass {
+ void method() {
+ WARN; // no-warning
+ }
+};
+
+struct MacroInUnsuppressedClass {
+ void method() {
+ WARN; // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_E() {
+ MacroInSuppressedClass().method();
+ MacroInUnsuppressedClass().method();
+}
+
+// ============================================================================
+// Group F: Macro expanding to suppression attribute + code
+// ============================================================================
+
+#define SUPPRESS_AND_WARN [[clang::suppress]] clang_analyzer_warnIfReached()
+
+void macro_suppression_wrapper() {
+ SUPPRESS_AND_WARN; // no-warning
+}
+
+// ============================================================================
+// Group G: Macro in template context
+// ============================================================================
+
+template <typename T>
+struct [[clang::suppress]] MacroInTemplate {
+ void method() {
+ WARN; // no-warning
+ }
+};
+
+template <typename T>
+struct MacroInTemplate_NoAttr {
+ void method() {
+ WARN; // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_G() {
+ MacroInTemplate<Type>().method();
+ MacroInTemplate_NoAttr<Type>().method();
+}
+
+// ============================================================================
+// Group H: Null dereference through direct null, suppressed at statement level
+// ============================================================================
+
+int macro_deref_suppressed() {
+ int *p = 0;
+ [[clang::suppress]] return *p; // no-warning
+}
+
+int macro_deref_unsuppressed() {
+ int *p = 0;
+ return *p; // expected-warning{{Dereference of null pointer (loaded from variable 'p')}}
+}
+
+// ============================================================================
+// Group I: Stringification and token pasting (shouldn't affect suppression)
+// ============================================================================
+
+#define STRINGIFY(x) #x
+#define CONCAT(a, b) a##b
+
+void stringify_suppressed() {
+ [[clang::suppress]] {
+ const char *s = STRINGIFY(hello);
+ (void)s;
+ int CONCAT(var, 1) = 0;
+ clang_analyzer_warnIfReached(); // no-warning
+ (void)var1;
+ }
+}
+
+void stringify_unsuppressed() {
+ const char *s = STRINGIFY(hello);
+ (void)s;
+ int CONCAT(var, 1) = 0;
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ (void)var1;
+}
+
+// ============================================================================
+// Group J: Multi-line macro with warnIfReached
+// ============================================================================
+
+#define MULTI_LINE_WARN \
+ do { \
+ clang_analyzer_warnIfReached(); \
+ } while (0)
+
+void multiline_macro_suppressed() {
+ [[clang::suppress]] {
+ MULTI_LINE_WARN; // no-warning
+ }
+}
+
+void multiline_macro_unsuppressed() {
+ MULTI_LINE_WARN; // expected-warning{{REACHABLE}}
+}
diff --git a/clang/test/Analysis/clang-suppress/namespaces.cpp b/clang/test/Analysis/clang-suppress/namespaces.cpp
new file mode 100644
index 0000000000000..aa1d270ad405a
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/namespaces.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
+
+// Systematic tests for [[clang::suppress]] on namespaces.
+
+// ============================================================================
+// Group A: Attributed namespace suppresses inline definitions
+// ============================================================================
+
+namespace [[clang::suppress]]
+suppressed_namespace {
+ int foo() {
+ int *x = 0;
+ return *x; // no-warning: inside attributed namespace
+ }
+
+ int ool_foo();
+}
+
+// Out-of-line definition in an attributed namespace is NOT suppressed.
+int suppressed_namespace::ool_foo() {
+ int *x = 0;
+ return *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+}
+
+// ============================================================================
+// Group B: Reopened namespace (without attribute) is NOT suppressed
+// ============================================================================
+
+// Another instance of the same namespace — the attribute does not carry over.
+namespace suppressed_namespace {
+ int bar() {
+ int *x = 0;
+ return *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
+ }
+}
diff --git a/clang/test/Analysis/clang-suppress/statements.cpp b/clang/test/Analysis/clang-suppress/statements.cpp
new file mode 100644
index 0000000000000..be3e1fd0f1832
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/statements.cpp
@@ -0,0 +1,158 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// ============================================================================
+// Group A: Compound statement (block)
+// ============================================================================
+
+void suppress_compound_suppressed() {
+ [[clang::suppress]] {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+}
+
+void suppress_compound_unsuppressed() {
+ {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+}
+
+// ============================================================================
+// Group B: If statement
+// ============================================================================
+
+void suppress_if(bool coin) {
+ [[clang::suppress]] if (coin) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+void suppress_if_else(bool coin) {
+ [[clang::suppress]] if (coin) {
+ clang_analyzer_warnIfReached(); // no-warning
+ } else {
+ clang_analyzer_warnIfReached(); // no-warning: entire if-else is suppressed
+ }
+}
+
+void unsuppressed_if_else(bool coin) {
+ if (coin) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ } else {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+}
+
+// ============================================================================
+// Group C: Loop statements
+// ============================================================================
+
+void suppress_for(int n) {
+ [[clang::suppress]] for (int i = 0; i < n; ++i) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+}
+
+void suppress_while(int n) {
+ [[clang::suppress]] while (--n) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+}
+
+void suppress_do_while(int n) {
+ [[clang::suppress]] do {
+ clang_analyzer_warnIfReached(); // no-warning
+ } while (--n);
+}
+
+void unsuppressed_for(int n) {
+ for (int i = 0; i < n; ++i) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+}
+
+void suppress_range_for() {
+ int arr[] = {1, 2, 3};
+ [[clang::suppress]] for (int x : arr) {
+ clang_analyzer_warnIfReached(); // no-warning
+ (void)x;
+ }
+}
+
+// ============================================================================
+// Group D: Switch statement
+// ============================================================================
+
+int suppress_switch(int n) {
+ [[clang::suppress]] switch (n) {
+ case 1:
+ return clang_analyzer_warnIfReached(), 1; // no-warning
+ default:
+ break;
+ }
+ return 0;
+}
+
+int unsuppressed_switch(int n) {
+ switch (n) {
+ case 1:
+ return clang_analyzer_warnIfReached(), 1; // expected-warning{{REACHABLE}}
+ default:
+ break;
+ }
+ return 0;
+}
+
+// ============================================================================
+// Group E: Return statement
+// ============================================================================
+
+int suppress_return() {
+ [[clang::suppress]] return clang_analyzer_warnIfReached(), 1; // no-warning
+}
+
+int unsuppressed_return() {
+ return clang_analyzer_warnIfReached(), 1; // expected-warning{{REACHABLE}}
+}
+
+// ============================================================================
+// Group F: Expression statement
+// ============================================================================
+
+void suppress_expr_stmt() {
+ [[clang::suppress]] clang_analyzer_warnIfReached(); // no-warning
+}
+
+void unsuppressed_expr_stmt() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+// ============================================================================
+// Group G: Nested suppressed blocks
+// ============================================================================
+
+void nested_suppression() {
+ [[clang::suppress]] {
+ [[clang::suppress]] {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+ }
+}
+
+// ============================================================================
+// Group H: Suppression on single statement within method
+// ============================================================================
+
+struct H_ClassWithMethods {
+ void method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ [[clang::suppress]] clang_analyzer_warnIfReached(); // no-warning
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_H() {
+ H_ClassWithMethods().method();
+}
diff --git a/clang/test/Analysis/clang-suppress/template-methods.cpp b/clang/test/Analysis/clang-suppress/template-methods.cpp
new file mode 100644
index 0000000000000..fb692aa7f0d46
--- /dev/null
+++ b/clang/test/Analysis/clang-suppress/template-methods.cpp
@@ -0,0 +1,132 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_warnIfReached();
+
+// Systematic tests for [[clang::suppress]] on template methods inside
+// non-template and template classes.
+
+// Placeholder types for triggering instantiations.
+// - Type{A,B} should match an unconstrained template type parameter.
+struct TypeA{};
+struct TypeB{};
+
+// ============================================================================
+// Group A: Non-template class with suppressed/unsuppressed template methods
+// ============================================================================
+
+struct NonTemplateClassWithTemplatedMethod {
+ template <typename T>
+ [[clang::suppress]] void suppressed(T) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+
+ template <typename T>
+ void unsuppressed(T) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_nontpl_class() {
+ NonTemplateClassWithTemplatedMethod().suppressed(TypeA{});
+ NonTemplateClassWithTemplatedMethod().unsuppressed(TypeA{});
+}
+
+// ============================================================================
+// Group B: Template class with template methods — inline
+// ============================================================================
+
+template <typename T>
+struct TemplateClassWithTemplateMethod {
+ template <typename U>
+ [[clang::suppress]] void suppressed(U) {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+
+ template <typename U>
+ void unsuppressed(U) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+
+ template <typename U>
+ [[clang::suppress]] void suppress_at_decl_outline(U);
+
+ template <typename U>
+ void suppress_at_def_outline(U);
+};
+
+// ============================================================================
+// Group C: Template class with template methods — out-of-line
+// ============================================================================
+
+// Attribute on declaration only — NOT honored at out-of-line definition.
+template <typename T>
+template <typename U>
+void TemplateClassWithTemplateMethod<T>::suppress_at_decl_outline(U) {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+// Attribute on out-of-line definition — suppressed.
+template <typename T>
+template <typename U>
+[[clang::suppress]] void TemplateClassWithTemplateMethod<T>::suppress_at_def_outline(U) {
+ clang_analyzer_warnIfReached(); // no-warning
+}
+
+void test_tpl_class_tpl_method() {
+ TemplateClassWithTemplateMethod<TypeA>().suppressed(TypeB{});
+ TemplateClassWithTemplateMethod<TypeA>().unsuppressed(TypeB{});
+ TemplateClassWithTemplateMethod<TypeA>().suppress_at_decl_outline(TypeB{});
+ TemplateClassWithTemplateMethod<TypeA>().suppress_at_def_outline(TypeB{});
+}
+
+// ============================================================================
+// Group D: Template-template parameters
+// ============================================================================
+
+// A simple "box" template used as a template-template argument.
+template <typename T>
+struct Box {
+ void get() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+// A version of Box that suppresses its own methods.
+template <typename T>
+class [[clang::suppress]] SuppressedBox {
+public:
+ void get() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+// Adaptor whose own methods are suppressed; the contained Box's methods are not.
+template <typename T, template <typename> class Container>
+class [[clang::suppress]] SuppressedAdaptor {
+public:
+ Container<T> data;
+
+ void adaptor_method() {
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+// Adaptor with no suppression; Box's own suppression is independent.
+template <typename T, template <typename> class Container>
+struct UnsuppressedAdaptor {
+ Container<T> data;
+
+ void adaptor_method() {
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+ }
+};
+
+void test_template_template() {
+ // SuppressedAdaptor<Box>: adaptor method suppressed; Box::get not affected.
+ SuppressedAdaptor<TypeA, Box>().adaptor_method(); // suppressed by adaptor's attr
+ SuppressedAdaptor<TypeA, Box>().data.get(); // warns — Box has no attr, different lexical context
+
+ // UnsuppressedAdaptor<SuppressedBox>: adaptor warns; SuppressedBox::get suppressed.
+ UnsuppressedAdaptor<TypeA, SuppressedBox>().adaptor_method(); // warns — adaptor has no attr
+ UnsuppressedAdaptor<TypeA, SuppressedBox>().data.get(); // suppressed by SuppressedBox's attr
+}
diff --git a/clang/test/Analysis/suppression-attr.cpp b/clang/test/Analysis/suppression-attr.cpp
deleted file mode 100644
index 9ba56d976fddb..0000000000000
--- a/clang/test/Analysis/suppression-attr.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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
- // 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 {
- int foo() {
- int *x = 0;
- return *x;
- }
-
- int foo_forward();
-}
-
-int suppressed_namespace::foo_forward() {
- int *x = 0;
- return *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
-}
-
-// Another instance of the same namespace.
-namespace suppressed_namespace {
- int bar() {
- int *x = 0;
- return *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
- }
-}
-
-void lambda() {
- [[clang::suppress]] {
- auto lam = []() {
- int *x = 0;
- return *x;
- };
- }
-}
-
-class [[clang::suppress]] SuppressedClass {
- int foo() {
- int *x = 0;
- return *x;
- }
-
- int bar();
-};
-
-int SuppressedClass::bar() {
- int *x = 0;
- return *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
-}
-
-class SuppressedMethodClass {
- [[clang::suppress]] int foo() {
- int *x = 0;
- return *x;
- }
-
- [[clang::suppress]] int bar1();
- int bar2();
-};
-
-int SuppressedMethodClass::bar1() {
- int *x = 0;
- return *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
-}
-
-[[clang::suppress]]
-int SuppressedMethodClass::bar2() {
- int *x = 0;
- return *x; // no-warning
-}
>From b932d955ffe21adf8a777d41e8f633ab34364d9a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20Benics?= <benicsbalazs at gmail.com>
Date: Mon, 16 Mar 2026 15:16:18 +0000
Subject: [PATCH 2/4] namespace -> TU in comments
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Donát Nagy <donat.nagy at ericsson.com>
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analysis/clang-suppress/class-template-specializations.cpp | 2 +-
clang/test/Analysis/clang-suppress/friends.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/Analysis/clang-suppress/class-template-specializations.cpp b/clang/test/Analysis/clang-suppress/class-template-specializations.cpp
index 1d1202ca70ff7..94f18a41164d6 100644
--- a/clang/test/Analysis/clang-suppress/class-template-specializations.cpp
+++ b/clang/test/Analysis/clang-suppress/class-template-specializations.cpp
@@ -29,7 +29,7 @@ class [[clang::suppress]] A_Primary {
template <typename T>
void A_Primary<T>::outline_method() {
- // Out-of-line: lexical context is namespace.
+ // Out-of-line: lexical context is the translation unit.
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
diff --git a/clang/test/Analysis/clang-suppress/friends.cpp b/clang/test/Analysis/clang-suppress/friends.cpp
index 430699aa2d8b2..acd3193eca0e2 100644
--- a/clang/test/Analysis/clang-suppress/friends.cpp
+++ b/clang/test/Analysis/clang-suppress/friends.cpp
@@ -46,7 +46,7 @@ struct [[clang::suppress]] A2_Suppressed {
friend void a2_suppressed(A2_Suppressed);
};
void a2_suppressed(A2_Suppressed) {
- // Out-of-line: lexical parent is the namespace, NOT the class.
+ // Out-of-line: lexical parent is the translation unit, NOT the class.
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
struct A2_Unsuppressed {
>From c6161179298864344885b348c194a3d1b7a391a3 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 16 Mar 2026 15:21:56 +0000
Subject: [PATCH 3/4] Split the template-methods.cpp Group B and C group
commonalities
---
.../clang-suppress/template-methods.cpp | 28 +++++++++++--------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/clang/test/Analysis/clang-suppress/template-methods.cpp b/clang/test/Analysis/clang-suppress/template-methods.cpp
index fb692aa7f0d46..7d18d7d68bc6e 100644
--- a/clang/test/Analysis/clang-suppress/template-methods.cpp
+++ b/clang/test/Analysis/clang-suppress/template-methods.cpp
@@ -46,7 +46,19 @@ struct TemplateClassWithTemplateMethod {
void unsuppressed(U) {
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
+};
+
+void test_tpl_class_tpl_inline_method() {
+ TemplateClassWithTemplateMethod<TypeA>().suppressed(TypeB{});
+ TemplateClassWithTemplateMethod<TypeA>().unsuppressed(TypeB{});
+}
+
+// ============================================================================
+// Group C: Template class with template methods — out-of-line
+// ============================================================================
+template <typename T>
+struct TemplateClassWithTemplateOOLMethod {
template <typename U>
[[clang::suppress]] void suppress_at_decl_outline(U);
@@ -54,29 +66,23 @@ struct TemplateClassWithTemplateMethod {
void suppress_at_def_outline(U);
};
-// ============================================================================
-// Group C: Template class with template methods — out-of-line
-// ============================================================================
-
// Attribute on declaration only — NOT honored at out-of-line definition.
template <typename T>
template <typename U>
-void TemplateClassWithTemplateMethod<T>::suppress_at_decl_outline(U) {
+void TemplateClassWithTemplateOOLMethod<T>::suppress_at_decl_outline(U) {
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
// Attribute on out-of-line definition — suppressed.
template <typename T>
template <typename U>
-[[clang::suppress]] void TemplateClassWithTemplateMethod<T>::suppress_at_def_outline(U) {
+[[clang::suppress]] void TemplateClassWithTemplateOOLMethod<T>::suppress_at_def_outline(U) {
clang_analyzer_warnIfReached(); // no-warning
}
-void test_tpl_class_tpl_method() {
- TemplateClassWithTemplateMethod<TypeA>().suppressed(TypeB{});
- TemplateClassWithTemplateMethod<TypeA>().unsuppressed(TypeB{});
- TemplateClassWithTemplateMethod<TypeA>().suppress_at_decl_outline(TypeB{});
- TemplateClassWithTemplateMethod<TypeA>().suppress_at_def_outline(TypeB{});
+void test_tpl_class_tpl_ool_method() {
+ TemplateClassWithTemplateOOLMethod<TypeA>().suppress_at_decl_outline(TypeB{});
+ TemplateClassWithTemplateOOLMethod<TypeA>().suppress_at_def_outline(TypeB{});
}
// ============================================================================
>From 6311ba9089d280ca971dec9fe310048ca7ee1e9a Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 16 Mar 2026 15:23:59 +0000
Subject: [PATCH 4/4] Add a distinglisher 'Inline' part to the class template
name
---
clang/test/Analysis/clang-suppress/template-methods.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/test/Analysis/clang-suppress/template-methods.cpp b/clang/test/Analysis/clang-suppress/template-methods.cpp
index 7d18d7d68bc6e..3e013b11ab928 100644
--- a/clang/test/Analysis/clang-suppress/template-methods.cpp
+++ b/clang/test/Analysis/clang-suppress/template-methods.cpp
@@ -36,7 +36,7 @@ void test_nontpl_class() {
// ============================================================================
template <typename T>
-struct TemplateClassWithTemplateMethod {
+struct TemplateClassWithTemplateInlineMethod {
template <typename U>
[[clang::suppress]] void suppressed(U) {
clang_analyzer_warnIfReached(); // no-warning
@@ -49,8 +49,8 @@ struct TemplateClassWithTemplateMethod {
};
void test_tpl_class_tpl_inline_method() {
- TemplateClassWithTemplateMethod<TypeA>().suppressed(TypeB{});
- TemplateClassWithTemplateMethod<TypeA>().unsuppressed(TypeB{});
+ TemplateClassWithTemplateInlineMethod<TypeA>().suppressed(TypeB{});
+ TemplateClassWithTemplateInlineMethod<TypeA>().unsuppressed(TypeB{});
}
// ============================================================================
More information about the cfe-commits
mailing list