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 #include "gmock/gmock.h" #include "gtest/gtest.h" +#include + 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(T.E)) { + auto *PrDecl = dyn_cast(DRE->getDecl()); + if (PrDecl && PrDecl->isExplicitObjectParameter()) { + const auto *Parent = + dyn_cast(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 void expectResolution(llvm::StringRef Code, ResolveFnT 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