https://github.com/MythreyaK created https://github.com/llvm/llvm-project/pull/155143
Fixes clangd/clangd#2323. Assumes `self` is of the record type in the declaration. ```cpp struct Foo { int [[memb^er]] {}; auto&& getter1(this auto&& self) { // assume `self` is is `Foo` return self.member; }; ``` >From 69ff01b37ffcbeb5973d94c1d0ec260e0e63c3a8 Mon Sep 17 00:00:00 2001 From: Mythreya Kuricheti <g...@mythreya.dev> Date: Sat, 23 Aug 2025 23:03:48 -0700 Subject: [PATCH] [clang] Heuristic resolution for explicit object parameter --- .../clangd/unittests/RenameTests.cpp | 43 ++++++++++++++++++- clang/lib/Sema/HeuristicResolver.cpp | 19 ++++++++ .../unittests/Sema/HeuristicResolverTest.cpp | 19 +++++++- 3 files changed, 79 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..352ae20c0a06e 100644 --- a/clang/lib/Sema/HeuristicResolver.cpp +++ b/clang/lib/Sema/HeuristicResolver.cpp @@ -301,6 +301,25 @@ 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 + if (auto *BaseExpr = ME->getBase()) { + if (auto *DeclRef = llvm::dyn_cast_if_present<DeclRefExpr>(BaseExpr)) { + auto *PrDecl = llvm::dyn_cast_if_present<ParmVarDecl>(DeclRef->getDecl()); + + if (PrDecl->isExplicitObjectParameter()) { + auto DeclCtx = PrDecl->getDeclContext(); + + if (auto CxxRecord = llvm::dyn_cast_if_present<CXXRecordDecl>( + DeclCtx->getParent())) { + + auto Type = Ctx.getTypeDeclType(llvm::dyn_cast<TypeDecl>(CxxRecord)); + return resolveDependentMember(Type, ME->getMember(), NoFilter); + } + } + } + } + // Try resolving the member inside the expression's base type. Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase(); QualType BaseType = ME->getBaseType(); 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 cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits