[clang] 818de32 - Warning for incorrect use of 'pure' attribute (#78200)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 20 09:37:40 PST 2024
Author: kelbon
Date: 2024-01-20T12:37:35-05:00
New Revision: 818de32f31e8075657dd27938e4aeb1a46f3f631
URL: https://github.com/llvm/llvm-project/commit/818de32f31e8075657dd27938e4aeb1a46f3f631
DIFF: https://github.com/llvm/llvm-project/commit/818de32f31e8075657dd27938e4aeb1a46f3f631.diff
LOG: Warning for incorrect use of 'pure' attribute (#78200)
This adds a warning when applying the `pure` attribute along with the `const` attribute, or when applying the `pure` attribute to a function with a `void` return type (including constructors and destructors).
Fixes https://github.com/llvm/llvm-project/issues/77482
Added:
clang/test/Sema/incorrect_pure.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaDecl.cpp
clang/test/Analysis/call-invalidation.cpp
clang/test/CodeGen/function-attributes.c
clang/test/CodeGen/pragma-weak.c
clang/test/Import/attr/Inputs/S.cpp
clang/test/Import/attr/test.cpp
clang/test/Index/attributes.c
clang/test/Interpreter/disambiguate-decl-stmt.cpp
clang/test/Sema/attr-print.c
clang/test/SemaCXX/attr-print.cpp
clang/test/SemaCXX/cxx0x-cursory-default-delete.cpp
clang/test/SemaCXX/cxx11-attr-print.cpp
clang/test/SemaCXX/warn-unused-value-cxx11.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4c69bdf131aaa53..8bb26fcae18d6b6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -581,7 +581,7 @@ Improvements to Clang's diagnostics
and template friend declarations with a constraint that depends on a template parameter from an
enclosing template must be a definition.
- Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template<class T> void T();``.
-
+- Clang now diagnoses incorrect usage of ``const`` and ``pure`` attributes, so ``-Wignored-attributes`` diagnoses more cases.
- Clang now emits more descriptive diagnostics for 'unusual' expressions (e.g. incomplete index
expressions on matrix types or builtin functions without an argument list) as placement-args
to new-expressions.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 23692758e9b8a57..dd24767b62f3cae 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_const_attr_with_pure_attr : Warning<
+ "'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'; attribute ignored">,
+ InGroup<IgnoredAttributes>;
+
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 cbb8ed8ce34d1bd..8dff2cdc063df32 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11801,6 +11801,32 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
OldDecl, Previous);
}
+static void CheckConstPureAttributesUsage(Sema &S, FunctionDecl *NewFD) {
+ bool IsPure = NewFD->hasAttr<PureAttr>();
+ bool IsConst = NewFD->hasAttr<ConstAttr>();
+
+ // If there are no pure or const attributes, there's nothing to check.
+ if (!IsPure && !IsConst)
+ return;
+
+ // If the function is marked both pure and const, we retain the const
+ // attribute because it makes stronger guarantees than the pure attribute, and
+ // we drop the pure attribute explicitly to prevent later confusion about
+ // semantics.
+ if (IsPure && IsConst) {
+ S.Diag(NewFD->getLocation(), diag::warn_const_attr_with_pure_attr);
+ NewFD->dropAttrs<PureAttr>();
+ }
+
+ // Constructors and destructors are functions which return void, so are
+ // handled here as well.
+ if (NewFD->getReturnType()->isVoidType()) {
+ S.Diag(NewFD->getLocation(), diag::warn_pure_function_returns_void)
+ << IsConst;
+ NewFD->dropAttrs<PureAttr, ConstAttr>();
+ }
+}
+
/// Perform semantic checking of a new function declaration.
///
/// Performs semantic analysis of the new function declaration
@@ -11898,6 +11924,8 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
NewFD->setInvalidDecl();
}
+ CheckConstPureAttributesUsage(*this, NewFD);
+
// 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/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/function-attributes.c b/clang/test/CodeGen/function-attributes.c
index 845f3baf7a4eecf..bb8670a8530e092 100644
--- a/clang/test/CodeGen/function-attributes.c
+++ b/clang/test/CodeGen/function-attributes.c
@@ -57,9 +57,9 @@ int f12(int arg) {
return arg ? 0 : f10_t();
}
-// CHECK: define{{.*}} void @f13() [[NUW_OS_RN:#[0-9]+]]
-void f13(void) __attribute__((pure)) __attribute__((const));
-void f13(void){}
+// CHECK: define{{.*}} i32 @f13() [[NUW_OS_RN:#[0-9]+]]
+int f13(void) __attribute__((const));
+int f13(void){ return 0; }
// [irgen] clang isn't setting the optsize bit on functions
diff --git a/clang/test/CodeGen/pragma-weak.c b/clang/test/CodeGen/pragma-weak.c
index 52328bf9ff1be75..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 void (), 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)) void __xxx(void) { }
-// CHECK: void @__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/Import/attr/Inputs/S.cpp b/clang/test/Import/attr/Inputs/S.cpp
index 28d70c544a7ca76..cf9af91838e8e48 100644
--- a/clang/test/Import/attr/Inputs/S.cpp
+++ b/clang/test/Import/attr/Inputs/S.cpp
@@ -1,4 +1,4 @@
-extern void f() __attribute__((const));
+extern char f() __attribute__((const));
struct S {
struct {
diff --git a/clang/test/Import/attr/test.cpp b/clang/test/Import/attr/test.cpp
index c9b2d6ed3433ae4..9e2d6ee1cbac90d 100644
--- a/clang/test/Import/attr/test.cpp
+++ b/clang/test/Import/attr/test.cpp
@@ -14,13 +14,13 @@
// CHECK-NEXT: LoopHintAttr
// CHECK-SAME: line:10:9
-extern void f() __attribute__((const));
+extern char f() __attribute__((const));
struct S;
void stmt();
void expr() {
- f();
+ (void)f();
struct S s;
}
diff --git a/clang/test/Index/attributes.c b/clang/test/Index/attributes.c
index a5d10a18359143d..0a9b0bf50aabed2 100644
--- a/clang/test/Index/attributes.c
+++ b/clang/test/Index/attributes.c
@@ -4,8 +4,8 @@ struct __attribute__((packed)) Test2 {
char a;
};
-void pure_fn() __attribute__((pure));
-void const_fn() __attribute__((const));
+char pure_fn() __attribute__((pure));
+char const_fn() __attribute__((const));
void noduplicate_fn() __attribute__((noduplicate));
enum __attribute((flag_enum)) FlagEnum {
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 {};
diff --git a/clang/test/Sema/attr-print.c b/clang/test/Sema/attr-print.c
index ffa27de4a5d306b..8492356e5d2e57d 100644
--- a/clang/test/Sema/attr-print.c
+++ b/clang/test/Sema/attr-print.c
@@ -9,11 +9,11 @@ __declspec(align(4)) int y;
// CHECK: short arr[3] __attribute__((aligned));
short arr[3] __attribute__((aligned));
-// CHECK: void foo(void) __attribute__((const));
-void foo(void) __attribute__((const));
+// CHECK: int foo(void) __attribute__((const));
+int foo(void) __attribute__((const));
-// CHECK: void bar(void) __attribute__((__const));
-void bar(void) __attribute__((__const));
+// CHECK: int bar(void) __attribute__((__const));
+int bar(void) __attribute__((__const));
// CHECK: int * __ptr32 p32;
int * __ptr32 p32;
diff --git a/clang/test/Sema/incorrect_pure.cpp b/clang/test/Sema/incorrect_pure.cpp
new file mode 100644
index 000000000000000..69ae41c421300e8
--- /dev/null
+++ b/clang/test/Sema/incorrect_pure.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+[[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}}
+
+struct A {
+ [[gnu::pure]] A(); // expected-warning{{'pure' attribute on function returning 'void'; 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'; attribute ignored}}
+
+ [[gnu::const]] [[gnu::pure]] int m(); // expected-warning{{'const' attribute imposes more restrictions; 'pure' attribute ignored}}
+};
diff --git a/clang/test/SemaCXX/attr-print.cpp b/clang/test/SemaCXX/attr-print.cpp
index dff290be696be24..c7c58a25504eedd 100644
--- a/clang/test/SemaCXX/attr-print.cpp
+++ b/clang/test/SemaCXX/attr-print.cpp
@@ -6,15 +6,15 @@ int x __attribute__((aligned(4)));
// CHECK: __declspec(align(4)) int y;
__declspec(align(4)) int y;
-// CHECK: void foo() __attribute__((const));
-void foo() __attribute__((const));
+// CHECK: int foo() __attribute__((const));
+int foo() __attribute__((const));
-// CHECK: void bar() __attribute__((__const));
-void bar() __attribute__((__const));
+// CHECK: int bar() __attribute__((__const));
+int bar() __attribute__((__const));
// FIXME: Print this with correct format.
-// CHECK: void foo1() __attribute__((noinline)) __attribute__((pure));
-void foo1() __attribute__((noinline, pure));
+// CHECK: int foo1() __attribute__((noinline)) __attribute__((pure));
+int foo1() __attribute__((noinline, pure));
// CHECK: typedef int Small1 __attribute__((mode(byte)));
typedef int Small1 __attribute__((mode(byte)));
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}}
diff --git a/clang/test/SemaCXX/cxx11-attr-print.cpp b/clang/test/SemaCXX/cxx11-attr-print.cpp
index 7095c462031d932..c988972aeb1a5ab 100644
--- a/clang/test/SemaCXX/cxx11-attr-print.cpp
+++ b/clang/test/SemaCXX/cxx11-attr-print.cpp
@@ -30,11 +30,11 @@ alignas(4) int cxx11_alignas;
// CHECK: int c11_alignas _Alignas(int);
_Alignas(int) int c11_alignas;
-// CHECK: void foo() __attribute__((const));
-void foo() __attribute__((const));
+// CHECK: int foo() __attribute__((const));
+int foo() __attribute__((const));
-// CHECK: void bar() __attribute__((__const));
-void bar() __attribute__((__const));
+// CHECK: int bar() __attribute__((__const));
+int bar() __attribute__((__const));
// FIXME: It's unfortunate that the string literal prints with the below three
// cases given that the string is only exposed via the [[nodiscard]] spelling.
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.
More information about the cfe-commits
mailing list