Author: Mythreya Kuricheti
Date: 2025-08-26T23:54:50-04:00
New Revision: f44eaf47dc1357959d14cbf5f278c82e554689b2

URL: 
https://github.com/llvm/llvm-project/commit/f44eaf47dc1357959d14cbf5f278c82e554689b2
DIFF: 
https://github.com/llvm/llvm-project/commit/f44eaf47dc1357959d14cbf5f278c82e554689b2.diff

LOG: [clang][HeuristicResolver] Resolve explicit object parameter to enclosing 
record type (#155143)

Heuristically resolve the type of a `this auto` parameter to the record type
in the declaration. 

```cpp
struct Foo {
  int member {};
  auto&& getter1(this auto&& self) { // assume `self` is is `Foo`
    return self.member;
};
```

Fixes https://github.com/clangd/clangd/issues/2323

Added: 
    

Modified: 
    clang-tools-extra/clangd/unittests/RenameTests.cpp
    clang/lib/Sema/HeuristicResolver.cpp
    clang/unittests/Sema/HeuristicResolverTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp 
b/clang-tools-extra/clangd/unittests/RenameTests.cpp
index 2cb0722f7f285..5d2a77b62a219 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 {
@@ -861,6 +862,25 @@ TEST(RenameTest, WithinFileRename) {
 
         void func([[Fo^o]] *f) {}
       )cpp",
+
+      // rename with explicit object parameter
+      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 this->[[mem^ber]] + [[memb^er]];
+        }
+      };
+    )cpp",
   };
   llvm::StringRef NewName = "NewName";
   for (llvm::StringRef T : Tests) {
@@ -868,6 +888,7 @@ TEST(RenameTest, WithinFileRename) {
     Annotations Code(T);
     auto TU = TestTU::withCode(Code.code());
     TU.ExtraArgs.push_back("-xobjective-c++");
+    TU.ExtraArgs.push_back("-std=c++23");
     auto AST = TU.build();
     auto Index = TU.index();
     for (const auto &RenamePos : Code.points()) {

diff  --git a/clang/lib/Sema/HeuristicResolver.cpp 
b/clang/lib/Sema/HeuristicResolver.cpp
index 933841beeac3d..6d79f3feeaace 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -255,6 +255,21 @@ QualType HeuristicResolverImpl::simplifyType(QualType 
Type, const Expr *E,
         }
       }
     }
+    // Check if the expression refers to an explicit object parameter of
+    // templated type. If so, heuristically treat it as having the type of the
+    // enclosing class.
+    if (!T.Type.isNull() &&
+        (T.Type->isUndeducedAutoType() || T.Type->isTemplateTypeParmType())) {
+      if (auto *DRE = dyn_cast_if_present<DeclRefExpr>(T.E)) {
+        auto *PrDecl = dyn_cast<ParmVarDecl>(DRE->getDecl());
+        if (PrDecl && PrDecl->isExplicitObjectParameter()) {
+          const auto *Parent =
+              dyn_cast<TagDecl>(PrDecl->getDeclContext()->getParent());
+          return {Ctx.getCanonicalTagType(Parent)};
+        }
+      }
+    }
+
     return T;
   };
   // As an additional protection against infinite loops, bound the number of

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>


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to