https://github.com/MythreyaK updated 
https://github.com/llvm/llvm-project/pull/155143

>From c1cdb3909c0bb55ce21d339f087c1e38616fec3f Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <g...@mythreya.dev>
Date: Sat, 23 Aug 2025 23:57:18 -0700
Subject: [PATCH 1/5] [clang] Heuristic resolution for explicit object
 parameter

Assume `self` parameter is of the parent record type
---
 .../clangd/unittests/RenameTests.cpp          | 43 ++++++++++++++++++-
 clang/lib/Sema/HeuristicResolver.cpp          | 26 +++++++++++
 .../unittests/Sema/HeuristicResolverTest.cpp  | 19 +++++++-
 3 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp 
b/clang-tools-extra/clangd/unittests/RenameTests.cpp
index 2cb0722f7f285..4701ae07d0d12 100644
--- a/clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -17,10 +17,11 @@
 #include "clang/Tooling/Core/Replacement.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/MemoryBuffer.h"
-#include <algorithm>
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
+#include <algorithm>
+
 namespace clang {
 namespace clangd {
 namespace {
@@ -2468,6 +2469,46 @@ TEST(CrossFileRenameTests, adjustmentCost) {
   }
 }
 
+TEST(RenameTest, RenameWithExplicitObjectPararameter) {
+  Annotations Test = {R"cpp(
+    struct Foo {
+      int [[memb^er]] {};
+      auto&& getter1(this auto&& self) {
+        auto local = [&] {
+          return self.[[memb^er]];
+        }();
+        return local + self.[[memb^er]];
+      }
+      auto&& getter2(this Foo&& self) {
+        return self.[[memb^er]];
+      }
+      int normal() {
+        return [[memb^er]];
+      }
+    };
+  )cpp"};
+
+  auto TU = TestTU::withCode(Test.code());
+  TU.ExtraArgs.push_back("-std=c++23");
+  auto AST = TU.build();
+
+  llvm::StringRef NewName = "m_member";
+  auto Index = TU.index();
+
+  for (const auto &RenamePos : Test.points()) {
+    auto RenameResult = rename({RenamePos, NewName, AST, testPath(TU.Filename),
+                                getVFSFromAST(AST), Index.get()});
+
+    ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
+    auto Res = RenameResult.get();
+
+    ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
+    ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
+    
EXPECT_EQ(applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
+              expectedResult(Test, NewName));
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
diff --git a/clang/lib/Sema/HeuristicResolver.cpp 
b/clang/lib/Sema/HeuristicResolver.cpp
index 933841beeac3d..20f7d0fca2066 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -14,6 +14,7 @@
 #include "clang/AST/TemplateBase.h"
 #include "clang/AST/Type.h"
 #include "llvm/ADT/identity.h"
+#include <optional>
 
 namespace clang {
 
@@ -301,9 +302,34 @@ std::vector<const NamedDecl *> 
HeuristicResolverImpl::resolveMemberExpr(
     return {};
   }
 
+  // check if member expr is in the context of an explicit object method
+  // If so, it's safe to assume the templated arg is of type of the record
+  const auto ExplicitMemberHeuristic =
+      [&](const Expr *Base) -> std::optional<QualType> {
+    if (auto *DeclRef = dyn_cast_if_present<DeclRefExpr>(Base)) {
+      auto *PrDecl = dyn_cast_if_present<ParmVarDecl>(DeclRef->getDecl());
+
+      if (PrDecl && PrDecl->isExplicitObjectParameter()) {
+        auto CxxRecord = dyn_cast_if_present<CXXRecordDecl>(
+            PrDecl->getDeclContext()->getParent());
+
+        if (CxxRecord) {
+          return Ctx.getTypeDeclType(dyn_cast<TypeDecl>(CxxRecord));
+        }
+      }
+    }
+
+    return std::nullopt;
+  };
+
   // Try resolving the member inside the expression's base type.
   Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
   QualType BaseType = ME->getBaseType();
+
+  if (auto Type = ExplicitMemberHeuristic(Base)) {
+    BaseType = *Type;
+  }
+
   BaseType = simplifyType(BaseType, Base, ME->isArrow());
   return resolveDependentMember(BaseType, ME->getMember(), NoFilter);
 }
diff --git a/clang/unittests/Sema/HeuristicResolverTest.cpp 
b/clang/unittests/Sema/HeuristicResolverTest.cpp
index 7df25e01e66d4..21aca7a3489b8 100644
--- a/clang/unittests/Sema/HeuristicResolverTest.cpp
+++ b/clang/unittests/Sema/HeuristicResolverTest.cpp
@@ -41,7 +41,7 @@ template <typename InputNode, typename ParamT, typename 
InputMatcher,
           typename... OutputMatchers>
 void expectResolution(llvm::StringRef Code, ResolveFnT<ParamT> ResolveFn,
                       const InputMatcher &IM, const OutputMatchers &...OMS) {
-  auto TU = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"});
+  auto TU = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++23"});
   auto &Ctx = TU->getASTContext();
   auto InputMatches = match(IM, Ctx);
   ASSERT_EQ(1u, InputMatches.size());
@@ -449,6 +449,23 @@ TEST(HeuristicResolver, 
MemberExpr_DefaultTemplateArgument_Recursive) {
       cxxMethodDecl(hasName("foo")).bind("output"));
 }
 
+TEST(HeuristicResolver, MemberExpr_ExplicitObjectParameter) {
+  std::string Code = R"cpp(
+    struct Foo {
+      int m_int;
+
+      int bar(this auto&& self) {
+        return self.m_int;
+      }
+    };
+  )cpp";
+  // Test resolution of "m_int" in "self.m_int()".
+  expectResolution(
+      Code, &HeuristicResolver::resolveMemberExpr,
+      cxxDependentScopeMemberExpr(hasMemberName("m_int")).bind("input"),
+      fieldDecl(hasName("m_int")).bind("output"));
+}
+
 TEST(HeuristicResolver, DeclRefExpr_StaticMethod) {
   std::string Code = R"cpp(
     template <typename T>

>From 0d482894a4e8bb045351e0c1724a6782caee87da Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <g...@mythreya.dev>
Date: Sun, 24 Aug 2025 00:46:19 -0700
Subject: [PATCH 2/5] code review

---
 clang/lib/Sema/HeuristicResolver.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/HeuristicResolver.cpp 
b/clang/lib/Sema/HeuristicResolver.cpp
index 20f7d0fca2066..ed817ac3691ad 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -305,7 +305,7 @@ std::vector<const NamedDecl *> 
HeuristicResolverImpl::resolveMemberExpr(
   // check if member expr is in the context of an explicit object method
   // If so, it's safe to assume the templated arg is of type of the record
   const auto ExplicitMemberHeuristic =
-      [&](const Expr *Base) -> std::optional<QualType> {
+      [&](const Expr *Base) -> QualType {
     if (auto *DeclRef = dyn_cast_if_present<DeclRefExpr>(Base)) {
       auto *PrDecl = dyn_cast_if_present<ParmVarDecl>(DeclRef->getDecl());
 
@@ -319,15 +319,15 @@ std::vector<const NamedDecl *> 
HeuristicResolverImpl::resolveMemberExpr(
       }
     }
 
-    return std::nullopt;
+    return {};
   };
 
   // Try resolving the member inside the expression's base type.
   Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
   QualType BaseType = ME->getBaseType();
 
-  if (auto Type = ExplicitMemberHeuristic(Base)) {
-    BaseType = *Type;
+  if (auto Type = ExplicitMemberHeuristic(Base); !Type.isNull()) {
+    BaseType = Type;
   }
 
   BaseType = simplifyType(BaseType, Base, ME->isArrow());

>From cd3601aaa400e4926058f3e33cf17c787ff47614 Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <g...@mythreya.dev>
Date: Sun, 24 Aug 2025 00:55:05 -0700
Subject: [PATCH 3/5] remove cxxdecl check

---
 clang/lib/Sema/HeuristicResolver.cpp | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Sema/HeuristicResolver.cpp 
b/clang/lib/Sema/HeuristicResolver.cpp
index ed817ac3691ad..4130e9b6686ec 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -304,18 +304,14 @@ std::vector<const NamedDecl *> 
HeuristicResolverImpl::resolveMemberExpr(
 
   // check if member expr is in the context of an explicit object method
   // If so, it's safe to assume the templated arg is of type of the record
-  const auto ExplicitMemberHeuristic =
-      [&](const Expr *Base) -> QualType {
+  const auto ExplicitMemberHeuristic = [&](const Expr *Base) -> QualType {
     if (auto *DeclRef = dyn_cast_if_present<DeclRefExpr>(Base)) {
       auto *PrDecl = dyn_cast_if_present<ParmVarDecl>(DeclRef->getDecl());
 
       if (PrDecl && PrDecl->isExplicitObjectParameter()) {
-        auto CxxRecord = dyn_cast_if_present<CXXRecordDecl>(
-            PrDecl->getDeclContext()->getParent());
-
-        if (CxxRecord) {
-          return Ctx.getTypeDeclType(dyn_cast<TypeDecl>(CxxRecord));
-        }
+        // get the parent, a cxxrecord
+        return Ctx.getTypeDeclType(
+            dyn_cast<TypeDecl>(PrDecl->getDeclContext()->getParent()));
       }
     }
 

>From ea15161d0bd504ad5a67e22caedf122a589ef790 Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <g...@mythreya.dev>
Date: Sun, 24 Aug 2025 15:15:22 -0700
Subject: [PATCH 4/5] code review

---
 clang-tools-extra/clangd/unittests/RenameTests.cpp | 2 +-
 clang/lib/Sema/HeuristicResolver.cpp               | 8 +++++---
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp 
b/clang-tools-extra/clangd/unittests/RenameTests.cpp
index 4701ae07d0d12..1b930811108bf 100644
--- a/clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -2483,7 +2483,7 @@ TEST(RenameTest, RenameWithExplicitObjectPararameter) {
         return self.[[memb^er]];
       }
       int normal() {
-        return [[memb^er]];
+        return this->[[mem^ber]] + [[memb^er]];
       }
     };
   )cpp"};
diff --git a/clang/lib/Sema/HeuristicResolver.cpp 
b/clang/lib/Sema/HeuristicResolver.cpp
index 4130e9b6686ec..210df82c9bb85 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -321,12 +321,14 @@ std::vector<const NamedDecl *> 
HeuristicResolverImpl::resolveMemberExpr(
   // Try resolving the member inside the expression's base type.
   Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
   QualType BaseType = ME->getBaseType();
+  BaseType = simplifyType(BaseType, Base, ME->isArrow());
 
-  if (auto Type = ExplicitMemberHeuristic(Base); !Type.isNull()) {
-    BaseType = Type;
+  if (BaseType->isUndeducedAutoType() || BaseType->isTemplateTypeParmType()) {
+    if (auto Type = ExplicitMemberHeuristic(Base); !Type.isNull()) {
+      BaseType = Type;
+    }
   }
 
-  BaseType = simplifyType(BaseType, Base, ME->isArrow());
   return resolveDependentMember(BaseType, ME->getMember(), NoFilter);
 }
 

>From c2b8ac474f14bbc7a106de059a1786a3bb153ee9 Mon Sep 17 00:00:00 2001
From: Mythreya Kuricheti <g...@mythreya.dev>
Date: Sun, 24 Aug 2025 15:45:57 -0700
Subject: [PATCH 5/5] add null-check

---
 clang/lib/Sema/HeuristicResolver.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/HeuristicResolver.cpp 
b/clang/lib/Sema/HeuristicResolver.cpp
index 210df82c9bb85..c89ea61760a76 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -323,7 +323,8 @@ std::vector<const NamedDecl *> 
HeuristicResolverImpl::resolveMemberExpr(
   QualType BaseType = ME->getBaseType();
   BaseType = simplifyType(BaseType, Base, ME->isArrow());
 
-  if (BaseType->isUndeducedAutoType() || BaseType->isTemplateTypeParmType()) {
+  if (!BaseType.isNull() &&
+      (BaseType->isUndeducedAutoType() || BaseType->isTemplateTypeParmType())) 
{
     if (auto Type = ExplicitMemberHeuristic(Base); !Type.isNull()) {
       BaseType = Type;
     }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to