[clang-tools-extra] 1cfa469 - [clang] Add missing information to AST for calling explicit object member functions (#220463)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 11 06:30:26 PDT 2026
Author: Yanzuo Liu
Date: 2026-09-11T13:30:20Z
New Revision: 1cfa469ad1b9a00500e94eac8ec65b1f0041f3ce
URL: https://github.com/llvm/llvm-project/commit/1cfa469ad1b9a00500e94eac8ec65b1f0041f3ce
DIFF: https://github.com/llvm/llvm-project/commit/1cfa469ad1b9a00500e94eac8ec65b1f0041f3ce.diff
LOG: [clang] Add missing information to AST for calling explicit object member functions (#220463)
For `E1.E2()` and `E1->E2()`,
1. A `MemberExpr` is created to represent `E1.E2` and `E1->E2`.
If overload resolution is performed, it is created using information
from a `UnresolvedMemberExpr`.
2. If `E2` is an explicit object member function, a `DeclRefExpr` is
created to represent `E2` using information from the `MemberExpr`.
This PR adds missed information to the `DeclRefExpr`.
Fixes #218829.
Added:
clang/test/AST/ast-print-deducing-this.cpp
Modified:
clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
clang/docs/ReleaseNotes.md
clang/lib/Sema/SemaOverload.cpp
clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
index 23f0ec4468450..c9519c2e91b68 100644
--- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
@@ -1668,5 +1668,16 @@ TEST(WalkAST, ObjCStringLiteral) {
{"-x", "objective-c"});
}
+TEST(WalkAST, GH218829) {
+ testWalk("struct $explicit^S1 {};",
+ R"cpp(
+struct S2 {
+ template <typename T> void f(this const S2 &);
+};
+void h(S2 s) { s.f<^S1>(); }
+)cpp",
+ {"-std=c++23"});
+}
+
} // namespace
} // namespace clang::include_cleaner
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ef694e1d0f5cc..6a2201012693e 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -684,6 +684,9 @@ features cannot lower the translation-unit ABI level;
- `FunctionDecl::getReturnTypeSourceRange()` now returns correct source
location of a trailing return type. (#GH162649)
+- Added missed information to the AST node representing the member function
+ when calling a explicit object member function. (#GH218829)
+
#### Miscellaneous Bug Fixes
#### Miscellaneous Clang Crashes Fixed
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 5d008cb2fd780..963f0e93bd651 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -63,9 +63,12 @@ static bool functionHasPassObjectSizeParams(const FunctionDecl *FD) {
/// A convenience routine for creating a decayed reference to a function.
static ExprResult CreateFunctionRefExpr(
- Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, const Expr *Base,
- bool HadMultipleCandidates, SourceLocation Loc = SourceLocation(),
- const DeclarationNameLoc &LocInfo = DeclarationNameLoc()) {
+ Sema &S, NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc,
+ FunctionDecl *Fn, NamedDecl *FoundDecl, const Expr *Base,
+ bool HadMultipleCandidates, const DeclarationNameInfo &NameInfo,
+ const TemplateArgumentListInfo *TemplateArgs) {
+ SourceLocation Loc = NameInfo.getLoc();
+
if (S.DiagnoseUseOfDecl(FoundDecl, Loc))
return ExprError();
// If FoundDecl is
diff erent from Fn (such as if one is a template
@@ -76,8 +79,10 @@ static ExprResult CreateFunctionRefExpr(
// being used.
if (FoundDecl != Fn && S.DiagnoseUseOfDecl(Fn, Loc))
return ExprError();
- DeclRefExpr *DRE = new (S.Context)
- DeclRefExpr(S.Context, Fn, false, Fn->getType(), VK_LValue, Loc, LocInfo);
+ auto *DRE = DeclRefExpr::Create(S.Context, QualifierLoc, TemplateKWLoc, Fn,
+ /*RefersToEnclosingVariableOrCapture=*/false,
+ NameInfo, Fn->getType(), VK_LValue, FoundDecl,
+ TemplateArgs);
if (HadMultipleCandidates)
DRE->setHadMultipleCandidates(true);
@@ -92,6 +97,23 @@ static ExprResult CreateFunctionRefExpr(
CK_FunctionToPointerDecay);
}
+static ExprResult CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn,
+ NamedDecl *FoundDecl, const Expr *Base,
+ bool HadMultipleCandidates,
+ const DeclarationNameInfo &NameInfo) {
+ return CreateFunctionRefExpr(S, /*QualifierLoc=*/{}, /*TemplateKWLoc=*/{}, Fn,
+ FoundDecl, Base, HadMultipleCandidates, NameInfo,
+ /*TemplateArgs=*/nullptr);
+}
+
+static ExprResult CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn,
+ NamedDecl *FoundDecl, const Expr *Base,
+ bool HadMultipleCandidates,
+ SourceLocation Loc) {
+ return CreateFunctionRefExpr(S, Fn, FoundDecl, Base, HadMultipleCandidates,
+ DeclarationNameInfo(Fn->getDeclName(), Loc));
+}
+
static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
bool InOverloadResolution,
StandardConversionSequence &SCS,
@@ -16234,9 +16256,9 @@ ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
// Build the actual expression node.
DeclarationNameInfo OpLocInfo(OpName, LLoc);
OpLocInfo.setCXXOperatorNameRange(SourceRange(LLoc, RLoc));
- ExprResult FnExpr = CreateFunctionRefExpr(
- *this, FnDecl, Best->FoundDecl, Base, HadMultipleCandidates,
- OpLocInfo.getLoc(), OpLocInfo.getInfo());
+ ExprResult FnExpr =
+ CreateFunctionRefExpr(*this, FnDecl, Best->FoundDecl, Base,
+ HadMultipleCandidates, OpLocInfo);
if (FnExpr.isInvalid())
return ExprError();
@@ -16570,10 +16592,18 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
NewArgs))
return ExprError();
+ // FIXME: avoid copy.
+ TemplateArgumentListInfo TemplateArgsBuffer, *TemplateArgs = nullptr;
+ if (MemExpr->hasExplicitTemplateArgs()) {
+ MemExpr->copyTemplateArgumentsInto(TemplateArgsBuffer);
+ TemplateArgs = &TemplateArgsBuffer;
+ }
+
// Build the actual expression node.
- ExprResult FnExpr =
- CreateFunctionRefExpr(*this, Method, FoundDecl, MemExpr,
- HadMultipleCandidates, MemExpr->getExprLoc());
+ ExprResult FnExpr = CreateFunctionRefExpr(
+ *this, MemExpr->getQualifierLoc(), MemExpr->getTemplateKeywordLoc(),
+ Method, FoundDecl, MemExpr, HadMultipleCandidates,
+ MemExpr->getMemberNameInfo(), TemplateArgs);
if (FnExpr.isInvalid())
return ExprError();
@@ -16862,10 +16892,8 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
DeclarationNameInfo OpLocInfo(
Context.DeclarationNames.getCXXOperatorName(OO_Call), LParenLoc);
OpLocInfo.setCXXOperatorNameRange(SourceRange(LParenLoc, RParenLoc));
- ExprResult NewFn = CreateFunctionRefExpr(*this, Method, Best->FoundDecl,
- Obj, HadMultipleCandidates,
- OpLocInfo.getLoc(),
- OpLocInfo.getInfo());
+ ExprResult NewFn = CreateFunctionRefExpr(*this, Method, Best->FoundDecl, Obj,
+ HadMultipleCandidates, OpLocInfo);
if (NewFn.isInvalid())
return true;
@@ -17094,10 +17122,8 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R,
}
FunctionDecl *FD = Best->Function;
- ExprResult Fn = CreateFunctionRefExpr(*this, FD, Best->FoundDecl,
- nullptr, HadMultipleCandidates,
- SuffixInfo.getLoc(),
- SuffixInfo.getInfo());
+ ExprResult Fn = CreateFunctionRefExpr(*this, FD, Best->FoundDecl, nullptr,
+ HadMultipleCandidates, SuffixInfo);
if (Fn.isInvalid())
return true;
diff --git a/clang/test/AST/ast-print-deducing-this.cpp b/clang/test/AST/ast-print-deducing-this.cpp
new file mode 100644
index 0000000000000..1fba787288cce
--- /dev/null
+++ b/clang/test/AST/ast-print-deducing-this.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -std=c++23 -ast-print %s | FileCheck %s --match-full-lines
+
+struct S {
+ void f(this const S &);
+ template <typename> void g(this const S &);
+};
+
+struct Ptr1 {
+ const S *operator->() const;
+};
+
+struct Ptr2 {
+ const S *operator->(this const Ptr2 &);
+};
+
+// FIXME: Should output the syntax of calling member functions.
+void h(S s, S *ptr, Ptr1 ptr1, Ptr2 ptr2) {
+ s.f();
+ // CHECK: f(s);
+ s.S::f();
+ // CHECK: S::f(s);
+ s.g<S>();
+ // CHECK: g<S>(s);
+ s.template g<S>();
+ // CHECK: template g<S>(s);
+ s.S::g<S>();
+ // CHECK: S::g<S>(s);
+ s.S::template g<S>();
+ // CHECK: S::template g<S>(s);
+
+ ptr->f();
+ // CHECK: f(*ptr);
+ ptr->S::f();
+ // CHECK: S::f(*ptr);
+ ptr->g<S>();
+ // CHECK: g<S>(*ptr);
+ ptr->template g<S>();
+ // CHECK: template g<S>(*ptr);
+ ptr->S::g<S>();
+ // CHECK: S::g<S>(*ptr);
+ ptr->S::template g<S>();
+ // CHECK: S::template g<S>(*ptr);
+
+ ptr1->f();
+ // CHECK: f(*ptr1);
+ ptr1->S::f();
+ // CHECK: S::f(*ptr1);
+ ptr1->g<S>();
+ // CHECK: g<S>(*ptr1);
+ ptr1->template g<S>();
+ // CHECK: template g<S>(*ptr1);
+ ptr1->S::g<S>();
+ // CHECK: S::g<S>(*ptr1);
+ ptr1->S::template g<S>();
+ // CHECK: S::template g<S>(*ptr1);
+
+ ptr2->f();
+ // CHECK: f(*ptr2);
+ ptr2->S::f();
+ // CHECK: S::f(*ptr2);
+ ptr2->g<S>();
+ // CHECK: g<S>(*ptr2);
+ ptr2->template g<S>();
+ // CHECK: template g<S>(*ptr2);
+ ptr2->S::g<S>();
+ // CHECK: S::g<S>(*ptr2);
+ ptr2->S::template g<S>();
+ // CHECK: S::template g<S>(*ptr2);
+}
diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
index d66d4fdcc9483..fc66b78b85508 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
@@ -87,7 +87,7 @@ void skep2<KN<2>>(K<2>);
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
-// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
+// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const' (FunctionTemplate {{.*}} 'operator()')
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<2>' lvalue ParmVar {{.*}} 'k' 'K<2>'
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
@@ -104,7 +104,7 @@ void skep2<KN<2>>(K<2>);
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
-// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
+// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const' (FunctionTemplate {{.*}} 'operator()')
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'K<2>' lvalue ImplicitParam {{.*}} 'k' 'K<2>'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<2>
@@ -147,7 +147,7 @@ void skep3<KN<3>>(K<3> k) {
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
-// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
+// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const' (FunctionTemplate {{.*}} 'operator()')
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<3>' lvalue ParmVar {{.*}} 'k' 'K<3>'
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
@@ -164,7 +164,7 @@ void skep3<KN<3>>(K<3> k) {
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
-// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
+// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const' (FunctionTemplate {{.*}} 'operator()')
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'K<3>' lvalue ImplicitParam {{.*}} 'k' 'K<3>'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<3>
@@ -181,7 +181,7 @@ void skep4(K<4> k, int p1, int p2) {
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)(int, int) const' <FunctionToPointerDecay>
-// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int) const'
+// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int) const' (FunctionTemplate {{.*}} 'operator()')
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'K<4>' lvalue ParmVar {{.*}} 'k' 'K<4>'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
@@ -210,7 +210,7 @@ void skep4(K<4> k, int p1, int p2) {
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)(int, int) const' <FunctionToPointerDecay>
-// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int) const'
+// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int) const' (FunctionTemplate {{.*}} 'operator()')
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<4>' lvalue ImplicitParam {{.*}} 'k' 'K<4>'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
@@ -269,7 +269,7 @@ void skep5(int unused1, K<5> k, int unused2, int p, int unused3) {
// CHECK-NEXT: | | | `-IntegerLiteral {{.*}} 'int' 4
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)(int, int, int, int, int, int, (lambda {{.*}}) const' <FunctionToPointerDecay>
-// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void (int, int, int, int, int, int, (lambda {{.*}})) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int, int, int, int, int, (lambda {{.*}})) const'
+// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void (int, int, int, int, int, int, (lambda {{.*}})) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int, int, int, int, int, (lambda {{.*}})) const' (FunctionTemplate {{.*}} 'operator()')
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const K<5>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<5>' lvalue ImplicitParam {{.*}} 'k' 'K<5>'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
More information about the cfe-commits
mailing list