[clang] [clang] pointer to member with qualified-id enclosed in parentheses in unevaluated context should be invalid (PR #89713)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 25 00:34:44 PDT 2024
https://github.com/zwuis updated https://github.com/llvm/llvm-project/pull/89713
>From f6fd1e5e5f42b3c72cb5aeaf9e6d4e91d5424bee Mon Sep 17 00:00:00 2001
From: YanzuoLiu <zwuis at outlook.com>
Date: Tue, 23 Apr 2024 14:56:12 +0800
Subject: [PATCH 1/5] Add missing check when making pointer to member
---
clang/lib/Sema/SemaExpr.cpp | 11 +++++++++++
.../CXX/expr/expr.unary/expr.unary.op/p3.cpp | 18 +++++++++++++++++-
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 5c861467bc1023..824667fb722365 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -14644,6 +14644,17 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
return QualType();
}
+ // C++11 [expr.unary.op] p4:
+ // A pointer to member is only formed when an explicit & is used and
+ // its operand is a qualified-id not enclosed in parentheses.
+ if (isa<ParenExpr>(OrigOp.get())) {
+ // `op->getEndLoc()` is the last part of the qualified-id.
+ // For example, "baz" in "foo::bar::baz".
+ Diag(op->getEndLoc(), diag::err_invalid_non_static_member_use)
+ << dcl->getDeclName() << op->getSourceRange();
+ return QualType();
+ }
+
while (cast<RecordDecl>(Ctx)->isAnonymousStructOrUnion())
Ctx = Ctx->getParent();
diff --git a/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp b/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp
index 08ab0ca56fb632..73d850a6839da7 100644
--- a/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp
+++ b/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only %s -verify
-// expected-no-diagnostics
namespace rdar10544564 {
// Check that we don't attempt to use an overloaded operator& when
@@ -27,3 +26,20 @@ namespace rdar10544564 {
X (Y::*func_mem_ptr1)() = &Y::memfunc1;
X (Y::*func_mem_ptr2)() = &Y::memfunc2;
}
+
+namespace test2 {
+ struct A {
+ int val;
+ void func() {}
+ };
+
+ void test() {
+ decltype(&(A::val)) ptr1; // expected-error {{invalid use of non-static data member 'val'}}
+ int A::* ptr2 = &(A::val); // expected-error {{invalid use of non-static data member 'val'}}
+
+ // FIXME: Error messages in these cases are less than clear, we can do
+ // better.
+ int size = sizeof(&(A::func)); // expected-error {{call to non-static member function without an object argument}}
+ void (A::* ptr3)() = &(A::func); // expected-error {{call to non-static member function without an object argument}}
+ }
+}
>From 22b18d32d79e5dcd0390aa17c454f373e565868a Mon Sep 17 00:00:00 2001
From: YanzuoLiu <zwuis at outlook.com>
Date: Wed, 24 Apr 2024 17:21:14 +0800
Subject: [PATCH 2/5] Apply suggestion from cor3ntin
Co-authored-by: cor3ntin <corentinjabot at gmail.com>
---
clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp b/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp
index 73d850a6839da7..3e99b333d0e584 100644
--- a/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp
+++ b/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp
@@ -27,7 +27,7 @@ namespace rdar10544564 {
X (Y::*func_mem_ptr2)() = &Y::memfunc2;
}
-namespace test2 {
+namespace GH40906 {
struct A {
int val;
void func() {}
>From 81fd55780b34c92566bdfb7fc1a2cde690675d66 Mon Sep 17 00:00:00 2001
From: YanzuoLiu <zwuis at outlook.com>
Date: Wed, 24 Apr 2024 17:39:45 +0800
Subject: [PATCH 3/5] Move tests to the right place
---
.../CXX/expr/expr.unary/expr.unary.op/p3.cpp | 18 +-----------------
.../CXX/expr/expr.unary/expr.unary.op/p4.cpp | 17 +++++++++++++++++
2 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp b/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp
index 3e99b333d0e584..08ab0ca56fb632 100644
--- a/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp
+++ b/clang/test/CXX/expr/expr.unary/expr.unary.op/p3.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only %s -verify
+// expected-no-diagnostics
namespace rdar10544564 {
// Check that we don't attempt to use an overloaded operator& when
@@ -26,20 +27,3 @@ namespace rdar10544564 {
X (Y::*func_mem_ptr1)() = &Y::memfunc1;
X (Y::*func_mem_ptr2)() = &Y::memfunc2;
}
-
-namespace GH40906 {
- struct A {
- int val;
- void func() {}
- };
-
- void test() {
- decltype(&(A::val)) ptr1; // expected-error {{invalid use of non-static data member 'val'}}
- int A::* ptr2 = &(A::val); // expected-error {{invalid use of non-static data member 'val'}}
-
- // FIXME: Error messages in these cases are less than clear, we can do
- // better.
- int size = sizeof(&(A::func)); // expected-error {{call to non-static member function without an object argument}}
- void (A::* ptr3)() = &(A::func); // expected-error {{call to non-static member function without an object argument}}
- }
-}
diff --git a/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp b/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp
index 537d676738bef8..a9efbb38859c5d 100644
--- a/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp
+++ b/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp
@@ -41,3 +41,20 @@ namespace test2 {
int (A::*ptr)(int) = &(A::foo); // expected-error {{cannot create a non-constant pointer to member function}}
}
}
+
+namespace GH40906 {
+ struct A {
+ int val;
+ void func() {}
+ };
+
+ void test() {
+ decltype(&(A::val)) ptr1; // expected-error {{invalid use of non-static data member 'val'}}
+ int A::* ptr2 = &(A::val); // expected-error {{invalid use of non-static data member 'val'}}
+
+ // FIXME: Error messages in these cases are less than clear, we can do
+ // better.
+ int size = sizeof(&(A::func)); // expected-error {{call to non-static member function without an object argument}}
+ void (A::* ptr3)() = &(A::func); // expected-error {{call to non-static member function without an object argument}}
+ }
+}
>From 5fb8cd8220dc9d8210517eadcd6b827a141fd78f Mon Sep 17 00:00:00 2001
From: YanzuoLiu <zwuis at outlook.com>
Date: Thu, 25 Apr 2024 11:32:02 +0800
Subject: [PATCH 4/5] Make diagnostic message more clear
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 +++
clang/lib/Sema/SemaExpr.cpp | 15 ++++++++++-----
.../test/CXX/expr/expr.unary/expr.unary.op/p4.cpp | 2 +-
3 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 63e951daec7477..3bec69d8a5285d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7511,6 +7511,9 @@ def err_nested_non_static_member_use : Error<
def warn_cxx98_compat_non_static_member_use : Warning<
"use of non-static data member %0 in an unevaluated context is "
"incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore;
+def err_form_ptr_to_member_from_parenthesized_expr : Error<
+ "cannot form pointer to member from a parenthesized expression; "
+ "did you mean to remove the parentheses?">;
def err_invalid_incomplete_type_use : Error<
"invalid use of incomplete type %0">;
def err_builtin_func_cast_more_than_one_arg : Error<
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 824667fb722365..f07a422041dae2 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -14648,11 +14648,16 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
// A pointer to member is only formed when an explicit & is used and
// its operand is a qualified-id not enclosed in parentheses.
if (isa<ParenExpr>(OrigOp.get())) {
- // `op->getEndLoc()` is the last part of the qualified-id.
- // For example, "baz" in "foo::bar::baz".
- Diag(op->getEndLoc(), diag::err_invalid_non_static_member_use)
- << dcl->getDeclName() << op->getSourceRange();
- return QualType();
+ SourceLocation LeftParenLoc = OrigOp.get()->getBeginLoc(),
+ RightParenLoc = OrigOp.get()->getEndLoc();
+
+ Diag(LeftParenLoc,
+ diag::err_form_ptr_to_member_from_parenthesized_expr)
+ << OrigOp.get()->getSourceRange()
+ << FixItHint::CreateRemoval(LeftParenLoc)
+ << FixItHint::CreateRemoval(RightParenLoc);
+
+ // Continuing might lead to better error recovery.
}
while (cast<RecordDecl>(Ctx)->isAnonymousStructOrUnion())
diff --git a/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp b/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp
index a9efbb38859c5d..162d59439d08ee 100644
--- a/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp
+++ b/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp
@@ -49,7 +49,7 @@ namespace GH40906 {
};
void test() {
- decltype(&(A::val)) ptr1; // expected-error {{invalid use of non-static data member 'val'}}
+ decltype(&(A::val)) ptr1; // expected-error {{cannot form pointer to member from a parenthesized expression; did you mean to remove the parentheses?}}
int A::* ptr2 = &(A::val); // expected-error {{invalid use of non-static data member 'val'}}
// FIXME: Error messages in these cases are less than clear, we can do
>From 97f0fbab28c56b466c16313bc55cabca156f02b0 Mon Sep 17 00:00:00 2001
From: YanzuoLiu <zwuis at outlook.com>
Date: Thu, 25 Apr 2024 15:34:22 +0800
Subject: [PATCH 5/5] Expand source range in diagnostic so that `&` can be
included
---
clang/lib/Sema/SemaExpr.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index f07a422041dae2..29aec8d373db9f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -14653,7 +14653,7 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
Diag(LeftParenLoc,
diag::err_form_ptr_to_member_from_parenthesized_expr)
- << OrigOp.get()->getSourceRange()
+ << SourceRange(OpLoc, RightParenLoc)
<< FixItHint::CreateRemoval(LeftParenLoc)
<< FixItHint::CreateRemoval(RightParenLoc);
More information about the cfe-commits
mailing list