https://github.com/zwuis updated https://github.com/llvm/llvm-project/pull/89713
>From f6fd1e5e5f42b3c72cb5aeaf9e6d4e91d5424bee Mon Sep 17 00:00:00 2001 From: YanzuoLiu <zw...@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 <zw...@outlook.com> Date: Wed, 24 Apr 2024 17:21:14 +0800 Subject: [PATCH 2/5] Apply suggestion from cor3ntin Co-authored-by: cor3ntin <corentinja...@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 <zw...@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 <zw...@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 <zw...@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); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits