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/4] [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/4] 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/4] 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/4] 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); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits