llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-tools-extra

Author: Mythreya Kuricheti (MythreyaK)

<details>
<summary>Changes</summary>

Fixes clangd/clangd#<!-- -->2323. 

Assumes `self` is of the record type in the declaration. 

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

---
Full diff: https://github.com/llvm/llvm-project/pull/155143.diff


3 Files Affected:

- (modified) clang-tools-extra/clangd/unittests/RenameTests.cpp (+42-1) 
- (modified) clang/lib/Sema/HeuristicResolver.cpp (+26) 
- (modified) clang/unittests/Sema/HeuristicResolverTest.cpp (+18-1) 


``````````diff
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>

``````````

</details>


https://github.com/llvm/llvm-project/pull/155143
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to