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 <[email protected]> Date: Fri, 13 Mar 2026 13:21:47 +0000 Subject: [PATCH 1/2] [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?= <[email protected]> Date: Mon, 16 Mar 2026 15:16:18 +0000 Subject: [PATCH 2/2] 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 <[email protected]> Co-authored-by: Balázs Benics <[email protected]> --- .../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 { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
