[clang] Warning for incorrect useof 'pure' attribute (PR #78200)

via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 17 08:28:37 PST 2024


https://github.com/kelbon updated https://github.com/llvm/llvm-project/pull/78200

>From b080d04eb30254502ccd5d59d76b5197db1fa88d Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Mon, 15 Jan 2024 22:24:34 +0400
Subject: [PATCH 1/8] add warning and test

---
 clang/include/clang/Basic/DiagnosticGroups.td    | 1 +
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 7 +++++++
 clang/lib/Sema/SemaDecl.cpp                      | 7 +++++++
 clang/test/Sema/incorrect_pure.cpp               | 7 +++++++
 4 files changed, 22 insertions(+)
 create mode 100644 clang/test/Sema/incorrect_pure.cpp

diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 6765721ae7002c1..9fcf2be2e45458e 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -414,6 +414,7 @@ def : DiagGroup<"c++2a-compat", [CXX20Compat]>;
 def : DiagGroup<"c++2a-compat-pedantic", [CXX20CompatPedantic]>;
 
 def ExitTimeDestructors : DiagGroup<"exit-time-destructors">;
+def IncorrectAttributeUsage : DiagGroup<"incorrect-attribute-usage">;
 def FlexibleArrayExtensions : DiagGroup<"flexible-array-extensions">;
 def FourByteMultiChar : DiagGroup<"four-char-constants">;
 def GlobalConstructors : DiagGroup<"global-constructors"> {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 414779a7970ab8e..0ad3ea64503d81e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -692,6 +692,13 @@ def warn_maybe_falloff_nonvoid_function : Warning<
 def warn_falloff_nonvoid_function : Warning<
   "non-void function does not return a value">,
   InGroup<ReturnType>;
+def warn_pure_attr_on_cxx_constructor : Warning<
+  "constructor cannot be 'pure' (undefined behavior)">,
+  InGroup<IncorrectAttributeUsage>;
+def warn_pure_function_returns_void : Warning<
+  "'pure' attribute on function returning 'void'">,
+  InGroup<IncorrectAttributeUsage>;
+
 def err_maybe_falloff_nonvoid_block : Error<
   "non-void block does not return a value in all control paths">;
 def err_falloff_nonvoid_block : Error<
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 4e7049571eeb7a3..e340028703b3b31 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11889,6 +11889,13 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
     NewFD->setInvalidDecl();
   }
 
+  if (NewFD->hasAttr<PureAttr>() || NewFD->hasAttr<ConstAttr>()) {
+    if (isa_and_nonnull<CXXConstructorDecl>(NewFD))
+      Diag(NewFD->getLocation(), diag::warn_pure_attr_on_cxx_constructor);
+    else if (NewFD->getReturnType()->isVoidType())
+      Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void);
+  }
+
   // C++11 [dcl.constexpr]p8:
   //   A constexpr specifier for a non-static member function that is not
   //   a constructor declares that member function to be const.
diff --git a/clang/test/Sema/incorrect_pure.cpp b/clang/test/Sema/incorrect_pure.cpp
new file mode 100644
index 000000000000000..ce02309f0863863
--- /dev/null
+++ b/clang/test/Sema/incorrect_pure.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+[[gnu::pure]] void foo(); // expected-warning{{'pure' attribute on function returning 'void'}}
+
+struct A {
+    [[gnu::pure]] A(); // expected-warning{{constructor cannot be 'pure' (undefined behavior)}}
+};

>From d43afccb027ea0e02c97ab9fbe55a1ad6c9d71dd Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Mon, 15 Jan 2024 22:52:23 +0400
Subject: [PATCH 2/8] use precondition: NewFD is not null

---
 clang/lib/Sema/SemaDecl.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e340028703b3b31..dcbc5c3c842cca3 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11890,7 +11890,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
   }
 
   if (NewFD->hasAttr<PureAttr>() || NewFD->hasAttr<ConstAttr>()) {
-    if (isa_and_nonnull<CXXConstructorDecl>(NewFD))
+    if (isa<CXXConstructorDecl>(NewFD))
       Diag(NewFD->getLocation(), diag::warn_pure_attr_on_cxx_constructor);
     else if (NewFD->getReturnType()->isVoidType())
       Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void);

>From 950ca9de1c05d561a1123c088455a3e21bd9795b Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Tue, 16 Jan 2024 00:03:47 +0400
Subject: [PATCH 3/8] fix old incorrect test

---
 clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp b/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
index 9d68a0e5d358f67..6ae146f0d08c7d3 100644
--- a/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
+++ b/clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
@@ -194,7 +194,7 @@ struct except_spec_d_match : except_spec_a, except_spec_b {
 // gcc-compatibility: allow attributes on default definitions
 // (but not normal definitions)
 struct S { S(); };
-S::S() __attribute((pure)) = default;
+S::S() __attribute((noreturn)) = default;
 
 using size_t = decltype(sizeof(0));
 void *operator new(size_t) = delete; // expected-error {{deleted definition must be first declaration}} expected-note {{implicit}}

>From 74bf7b1840cf2b20baae3348c0e46b44a04a5408 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Tue, 16 Jan 2024 11:29:49 +0400
Subject: [PATCH 4/8] fix another old test

---
 clang/test/SemaCXX/warn-unused-value-cxx11.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaCXX/warn-unused-value-cxx11.cpp b/clang/test/SemaCXX/warn-unused-value-cxx11.cpp
index a6f41c3fd6b3b2a..687278a98f4e1a4 100644
--- a/clang/test/SemaCXX/warn-unused-value-cxx11.cpp
+++ b/clang/test/SemaCXX/warn-unused-value-cxx11.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -Wunused-value %s
 
-void f() __attribute__((const));
+int f() __attribute__((const));
 
 namespace PR18571 {
 // Unevaluated contexts should not trigger unused result warnings.

>From bde11e037554481f90852e2ecd72fea9a3faffe6 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Tue, 16 Jan 2024 14:52:19 +0400
Subject: [PATCH 5/8] fix all old tests

---
 clang/test/Analysis/call-invalidation.cpp         | 8 ++++----
 clang/test/CodeGen/pragma-weak.c                  | 6 +++---
 clang/test/Interpreter/disambiguate-decl-stmt.cpp | 4 ++--
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/clang/test/Analysis/call-invalidation.cpp b/clang/test/Analysis/call-invalidation.cpp
index ef6505e19cf803f..727217f228b0542 100644
--- a/clang/test/Analysis/call-invalidation.cpp
+++ b/clang/test/Analysis/call-invalidation.cpp
@@ -90,8 +90,8 @@ void testConstReferenceStruct() {
 }
 
 
-void usePointerPure(int * const *) __attribute__((pure));
-void usePointerConst(int * const *) __attribute__((const));
+int usePointerPure(int * const *) __attribute__((pure));
+int usePointerConst(int * const *) __attribute__((const));
 
 void testPureConst() {
   extern int global;
@@ -104,11 +104,11 @@ void testPureConst() {
   clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
   clang_analyzer_eval(global == -5); // expected-warning{{TRUE}}
 
-  usePointerPure(&p);
+  (void)usePointerPure(&p);
   clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
   clang_analyzer_eval(global == -5); // expected-warning{{TRUE}}
 
-  usePointerConst(&p);
+  (void)usePointerConst(&p);
   clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
   clang_analyzer_eval(global == -5); // expected-warning{{TRUE}}
 
diff --git a/clang/test/CodeGen/pragma-weak.c b/clang/test/CodeGen/pragma-weak.c
index 52328bf9ff1be75..d4011628bda7f12 100644
--- a/clang/test/CodeGen/pragma-weak.c
+++ b/clang/test/CodeGen/pragma-weak.c
@@ -16,7 +16,7 @@
 // CHECK-DAG: @declfirstattr = weak{{.*}} alias void (), ptr @__declfirstattr
 // CHECK-DAG: @mix2 = weak{{.*}} alias void (), ptr @__mix2
 // CHECK-DAG: @a1 = weak{{.*}} alias void (), ptr @__a1
-// CHECK-DAG: @xxx = weak{{.*}} alias void (), ptr @__xxx
+// CHECK-DAG: @xxx = weak{{.*}} alias int (), ptr @__xxx
 // CHECK-DAG: @undecfunc_alias1 = weak{{.*}} alias void (), ptr @undecfunc
 // CHECK-DAG: @undecfunc_alias2 = weak{{.*}} alias void (), ptr @undecfunc
 // CHECK-DAG: @undecfunc_alias3 = weak{{.*}} alias void (), ptr @undecfunc
@@ -137,8 +137,8 @@ void __a1(void) {}
 // CHECK: define{{.*}} void @__a1() [[NI:#[0-9]+]]
 
 #pragma weak xxx = __xxx
-__attribute((pure,noinline,const)) void __xxx(void) { }
-// CHECK: void @__xxx() [[RN:#[0-9]+]]
+__attribute((pure,noinline,const)) int __xxx(void) { return 0; }
+// CHECK: int @__xxx() [[RN:#[0-9]+]]
 
 ///////////// PR28611: Try multiple aliases of same undeclared symbol or alias
 #pragma weak undecfunc_alias1 = undecfunc
diff --git a/clang/test/Interpreter/disambiguate-decl-stmt.cpp b/clang/test/Interpreter/disambiguate-decl-stmt.cpp
index a49d7013c540ac2..1f4d5e267288bc3 100644
--- a/clang/test/Interpreter/disambiguate-decl-stmt.cpp
+++ b/clang/test/Interpreter/disambiguate-decl-stmt.cpp
@@ -95,10 +95,10 @@ Ns::Ns::Fs();
 Ns::Ns::Ns();
 
 struct Attrs1 { Attrs1(); };
-Attrs1::Attrs1() __attribute((pure)) = default;
+Attrs1::Attrs1() __attribute((noreturn)) = default;
 
 struct Attrs2 { Attrs2(); };
-__attribute((pure)) Attrs2::Attrs2() = default;
+__attribute((noreturn)) Attrs2::Attrs2() = default;
 
 // Extra semicolon
 namespace N {};

>From 35f854e8238207fb5463e9990f2cac27907f8886 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Wed, 17 Jan 2024 15:00:39 +0400
Subject: [PATCH 6/8] release notes, update warning message, handle pure &&
 const declarations

---
 clang/docs/ReleaseNotes.rst                   |  3 +-
 clang/include/clang/Basic/DiagnosticGroups.td |  1 -
 .../clang/Basic/DiagnosticSemaKinds.td        | 11 ++++---
 clang/lib/Sema/SemaDecl.cpp                   | 32 +++++++++++++++----
 clang/test/CodeGen/pragma-weak.c              |  6 ++--
 clang/test/Sema/incorrect_pure.cpp            |  8 ++++-
 6 files changed, 45 insertions(+), 16 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7e130304c5c08f8..def67063ad2d135 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -560,7 +560,8 @@ Improvements to Clang's diagnostics
 - Clang now diagnoses unexpanded packs within the template argument lists of function template specializations.
 - Clang now diagnoses attempts to bind a bitfield to an NTTP of a reference type as erroneous
   converted constant expression and not as a reference to subobject.
-
+- Clang now diagnoses incorrect usage of 'const' and 'pure' attributes (``-Wincorrect-pure-usage``),
+  also ``-Wignored-attributes`` diagnoses more cases.
 
 Improvements to Clang's time-trace
 ----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 9fcf2be2e45458e..6765721ae7002c1 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -414,7 +414,6 @@ def : DiagGroup<"c++2a-compat", [CXX20Compat]>;
 def : DiagGroup<"c++2a-compat-pedantic", [CXX20CompatPedantic]>;
 
 def ExitTimeDestructors : DiagGroup<"exit-time-destructors">;
-def IncorrectAttributeUsage : DiagGroup<"incorrect-attribute-usage">;
 def FlexibleArrayExtensions : DiagGroup<"flexible-array-extensions">;
 def FourByteMultiChar : DiagGroup<"four-char-constants">;
 def GlobalConstructors : DiagGroup<"global-constructors"> {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0ad3ea64503d81e..f71501994973573 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -692,12 +692,15 @@ def warn_maybe_falloff_nonvoid_function : Warning<
 def warn_falloff_nonvoid_function : Warning<
   "non-void function does not return a value">,
   InGroup<ReturnType>;
+def warn_const_attr_with_pure_attr : Warning<
+  "'const' attribute imposes greater restrictions than 'pure', 'pure' attribute ignored">,
+  InGroup<IgnoredAttributes>;
 def warn_pure_attr_on_cxx_constructor : Warning<
-  "constructor cannot be 'pure' (undefined behavior)">,
-  InGroup<IncorrectAttributeUsage>;
+  "constructor cannot be '%select{pure|const}0', attribute ignored">,
+  InGroup<IgnoredAttributes>;
 def warn_pure_function_returns_void : Warning<
-  "'pure' attribute on function returning 'void'">,
-  InGroup<IncorrectAttributeUsage>;
+  "'%select{pure|const}0' attribute on function returning 'void'">,
+  InGroup<DiagGroup<"incorrect-pure-usage">>;
 
 def err_maybe_falloff_nonvoid_block : Error<
   "non-void block does not return a value in all control paths">;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index dcbc5c3c842cca3..a0083c841c765fc 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11792,6 +11792,31 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
                                          OldDecl, Previous);
 }
 
+static void CheckFunctionDeclarationAttributesUsage(Sema &S, FunctionDecl *NewFD) {
+  const bool is_pure = NewFD->hasAttr<PureAttr>();
+  const bool is_const = NewFD->hasAttr<ConstAttr>();
+
+  if (is_pure && is_const) {
+    S.Diag(NewFD->getLocation(), diag::warn_const_attr_with_pure_attr);
+    NewFD->dropAttr<PureAttr>();
+  }
+  if (is_pure || is_const) {
+    if (isa<CXXConstructorDecl>(NewFD)) {
+      // 'pure' constructor is an insidious error:
+      // while constructor semantically 'returns' object
+      // but it should modify memory by implicit 'this' pointer
+      S.Diag(NewFD->getLocation(), diag::warn_pure_attr_on_cxx_constructor)
+        << (is_const ? 1 : 0);
+      // ignore attributes is this case (do not produce UB)
+      NewFD->dropAttr<PureAttr>();
+      NewFD->dropAttr<ConstAttr>();
+    } else if (NewFD->getReturnType()->isVoidType()) {
+      S.Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void)
+        << (is_const ? 1 : 0);
+    }
+  }
+}
+
 /// Perform semantic checking of a new function declaration.
 ///
 /// Performs semantic analysis of the new function declaration
@@ -11889,12 +11914,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
     NewFD->setInvalidDecl();
   }
 
-  if (NewFD->hasAttr<PureAttr>() || NewFD->hasAttr<ConstAttr>()) {
-    if (isa<CXXConstructorDecl>(NewFD))
-      Diag(NewFD->getLocation(), diag::warn_pure_attr_on_cxx_constructor);
-    else if (NewFD->getReturnType()->isVoidType())
-      Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void);
-  }
+  CheckFunctionDeclarationAttributesUsage(*this, NewFD);
 
   // C++11 [dcl.constexpr]p8:
   //   A constexpr specifier for a non-static member function that is not
diff --git a/clang/test/CodeGen/pragma-weak.c b/clang/test/CodeGen/pragma-weak.c
index d4011628bda7f12..ea508a485ef6121 100644
--- a/clang/test/CodeGen/pragma-weak.c
+++ b/clang/test/CodeGen/pragma-weak.c
@@ -16,7 +16,7 @@
 // CHECK-DAG: @declfirstattr = weak{{.*}} alias void (), ptr @__declfirstattr
 // CHECK-DAG: @mix2 = weak{{.*}} alias void (), ptr @__mix2
 // CHECK-DAG: @a1 = weak{{.*}} alias void (), ptr @__a1
-// CHECK-DAG: @xxx = weak{{.*}} alias int (), ptr @__xxx
+// CHECK-DAG: @xxx = weak{{.*}} alias i32 (), ptr @__xxx
 // CHECK-DAG: @undecfunc_alias1 = weak{{.*}} alias void (), ptr @undecfunc
 // CHECK-DAG: @undecfunc_alias2 = weak{{.*}} alias void (), ptr @undecfunc
 // CHECK-DAG: @undecfunc_alias3 = weak{{.*}} alias void (), ptr @undecfunc
@@ -137,8 +137,8 @@ void __a1(void) {}
 // CHECK: define{{.*}} void @__a1() [[NI:#[0-9]+]]
 
 #pragma weak xxx = __xxx
-__attribute((pure,noinline,const)) int __xxx(void) { return 0; }
-// CHECK: int @__xxx() [[RN:#[0-9]+]]
+__attribute((noinline,const)) int __xxx(void) { return 0; }
+// CHECK: i32 @__xxx() [[RN:#[0-9]+]]
 
 ///////////// PR28611: Try multiple aliases of same undeclared symbol or alias
 #pragma weak undecfunc_alias1 = undecfunc
diff --git a/clang/test/Sema/incorrect_pure.cpp b/clang/test/Sema/incorrect_pure.cpp
index ce02309f0863863..a0908907bc3769a 100644
--- a/clang/test/Sema/incorrect_pure.cpp
+++ b/clang/test/Sema/incorrect_pure.cpp
@@ -2,6 +2,12 @@
 
 [[gnu::pure]] void foo(); // expected-warning{{'pure' attribute on function returning 'void'}}
 
+[[gnu::const]] void bar(); // expected-warning{{'const' attribute on function returning 'void'}}
 struct A {
-    [[gnu::pure]] A(); // expected-warning{{constructor cannot be 'pure' (undefined behavior)}}
+    [[gnu::pure]] A(); // expected-warning{{constructor cannot be 'pure', attribute ignored"}}
+
+    [[gnu::const]] A(int); // expected-warning{{constructor cannot be 'const', attribute ignored"}}
+    [[gnu::pure]] ~A(); // expected-warning{{'pure' attribute on function returning 'void'}}
+
+    [[gnu::const]] [[gnu::pure]] int m(); // expected-warning{{'const' attribute imposes greater restrictions than 'pure', 'pure' attribute ignored}}
 };

>From 88dc26f44c786113f8a0cacb48bc050c019e5705 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Wed, 17 Jan 2024 15:18:05 +0400
Subject: [PATCH 7/8] format

---
 clang/lib/Sema/SemaDecl.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a0083c841c765fc..0e33504701edb4c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11792,7 +11792,8 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
                                          OldDecl, Previous);
 }
 
-static void CheckFunctionDeclarationAttributesUsage(Sema &S, FunctionDecl *NewFD) {
+static void CheckFunctionDeclarationAttributesUsage(Sema &S,
+                                                    FunctionDecl *NewFD) {
   const bool is_pure = NewFD->hasAttr<PureAttr>();
   const bool is_const = NewFD->hasAttr<ConstAttr>();
 
@@ -11806,13 +11807,13 @@ static void CheckFunctionDeclarationAttributesUsage(Sema &S, FunctionDecl *NewFD
       // while constructor semantically 'returns' object
       // but it should modify memory by implicit 'this' pointer
       S.Diag(NewFD->getLocation(), diag::warn_pure_attr_on_cxx_constructor)
-        << (is_const ? 1 : 0);
+          << (is_const ? 1 : 0);
       // ignore attributes is this case (do not produce UB)
       NewFD->dropAttr<PureAttr>();
       NewFD->dropAttr<ConstAttr>();
     } else if (NewFD->getReturnType()->isVoidType()) {
       S.Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void)
-        << (is_const ? 1 : 0);
+          << (is_const ? 1 : 0);
     }
   }
 }

>From 0cc046cf509c32de5619792e45ada7ea37942685 Mon Sep 17 00:00:00 2001
From: Kelbon Nik <kelbonage at gmail.com>
Date: Wed, 17 Jan 2024 20:28:23 +0400
Subject: [PATCH 8/8] handle constructors, functions and destructors same way
 as gcc does

---
 clang/docs/ReleaseNotes.rst                   |  2 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |  9 +++-----
 clang/lib/Sema/SemaDecl.cpp                   | 22 +++++++------------
 clang/test/Sema/incorrect_pure.cpp            | 11 +++++-----
 4 files changed, 18 insertions(+), 26 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index def67063ad2d135..755352bd7ba63c8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -560,7 +560,7 @@ Improvements to Clang's diagnostics
 - Clang now diagnoses unexpanded packs within the template argument lists of function template specializations.
 - Clang now diagnoses attempts to bind a bitfield to an NTTP of a reference type as erroneous
   converted constant expression and not as a reference to subobject.
-- Clang now diagnoses incorrect usage of 'const' and 'pure' attributes (``-Wincorrect-pure-usage``),
+- Clang now diagnoses incorrect usage of ``const`` and ``pure`` attributes (``-Wincorrect-pure-usage``),
   also ``-Wignored-attributes`` diagnoses more cases.
 
 Improvements to Clang's time-trace
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f71501994973573..7b603273a878b50 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -693,14 +693,11 @@ def warn_falloff_nonvoid_function : Warning<
   "non-void function does not return a value">,
   InGroup<ReturnType>;
 def warn_const_attr_with_pure_attr : Warning<
-  "'const' attribute imposes greater restrictions than 'pure', 'pure' attribute ignored">,
-  InGroup<IgnoredAttributes>;
-def warn_pure_attr_on_cxx_constructor : Warning<
-  "constructor cannot be '%select{pure|const}0', attribute ignored">,
+  "'const' attribute imposes more restrictions, 'pure' attribute ignored">,
   InGroup<IgnoredAttributes>;
 def warn_pure_function_returns_void : Warning<
-  "'%select{pure|const}0' attribute on function returning 'void'">,
-  InGroup<DiagGroup<"incorrect-pure-usage">>;
+  "'%select{pure|const}0' attribute on function returning 'void', attribute ignored">,
+  InGroup<IgnoredAttributes>;
 
 def err_maybe_falloff_nonvoid_block : Error<
   "non-void block does not return a value in all control paths">;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 0e33504701edb4c..041ef468cfe11db 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11794,26 +11794,20 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
 
 static void CheckFunctionDeclarationAttributesUsage(Sema &S,
                                                     FunctionDecl *NewFD) {
-  const bool is_pure = NewFD->hasAttr<PureAttr>();
-  const bool is_const = NewFD->hasAttr<ConstAttr>();
+  bool IsPure = NewFD->hasAttr<PureAttr>();
+  bool IsConst = NewFD->hasAttr<ConstAttr>();
 
-  if (is_pure && is_const) {
+  if (IsPure && IsConst) {
     S.Diag(NewFD->getLocation(), diag::warn_const_attr_with_pure_attr);
     NewFD->dropAttr<PureAttr>();
   }
-  if (is_pure || is_const) {
-    if (isa<CXXConstructorDecl>(NewFD)) {
-      // 'pure' constructor is an insidious error:
-      // while constructor semantically 'returns' object
-      // but it should modify memory by implicit 'this' pointer
-      S.Diag(NewFD->getLocation(), diag::warn_pure_attr_on_cxx_constructor)
-          << (is_const ? 1 : 0);
-      // ignore attributes is this case (do not produce UB)
+  if (IsPure || IsConst) {
+    // constructors and destructors also functions which returns void
+    if (NewFD->getReturnType()->isVoidType()) {
+      S.Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void)
+          << IsConst;
       NewFD->dropAttr<PureAttr>();
       NewFD->dropAttr<ConstAttr>();
-    } else if (NewFD->getReturnType()->isVoidType()) {
-      S.Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void)
-          << (is_const ? 1 : 0);
     }
   }
 }
diff --git a/clang/test/Sema/incorrect_pure.cpp b/clang/test/Sema/incorrect_pure.cpp
index a0908907bc3769a..d4257003848bbb3 100644
--- a/clang/test/Sema/incorrect_pure.cpp
+++ b/clang/test/Sema/incorrect_pure.cpp
@@ -1,13 +1,14 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 
-[[gnu::pure]] void foo(); // expected-warning{{'pure' attribute on function returning 'void'}}
+[[gnu::pure]] void foo(); // expected-warning{{'pure' attribute on function returning 'void', attribute ignored}}
+
+[[gnu::const]] void bar(); // expected-warning{{'const' attribute on function returning 'void', attribute ignored}}
 
-[[gnu::const]] void bar(); // expected-warning{{'const' attribute on function returning 'void'}}
 struct A {
-    [[gnu::pure]] A(); // expected-warning{{constructor cannot be 'pure', attribute ignored"}}
+    [[gnu::pure]] A(); // expected-warning{{'pure' attribute on function returning 'void', attribute ignored}}
 
-    [[gnu::const]] A(int); // expected-warning{{constructor cannot be 'const', attribute ignored"}}
+    [[gnu::const]] A(int); // expected-warning{{'const' attribute on function returning 'void', attribute ignored}}
     [[gnu::pure]] ~A(); // expected-warning{{'pure' attribute on function returning 'void'}}
 
-    [[gnu::const]] [[gnu::pure]] int m(); // expected-warning{{'const' attribute imposes greater restrictions than 'pure', 'pure' attribute ignored}}
+    [[gnu::const]] [[gnu::pure]] int m(); // expected-warning{{'const' attribute imposes more restrictions, 'pure' attribute ignored}}
 };



More information about the cfe-commits mailing list