Skip to content

Commit c1cdb39

Browse files
committed
[clang] Heuristic resolution for explicit object parameter
Assume `self` parameter is of the parent record type
1 parent bcf09c1 commit c1cdb39

File tree

3 files changed

+86
-2
lines changed

3 files changed

+86
-2
lines changed

clang-tools-extra/clangd/unittests/RenameTests.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
#include "clang/Tooling/Core/Replacement.h"
1818
#include "llvm/ADT/STLExtras.h"
1919
#include "llvm/Support/MemoryBuffer.h"
20-
#include <algorithm>
2120
#include "gmock/gmock.h"
2221
#include "gtest/gtest.h"
2322

23+
#include <algorithm>
24+
2425
namespace clang {
2526
namespace clangd {
2627
namespace {
@@ -2468,6 +2469,46 @@ TEST(CrossFileRenameTests, adjustmentCost) {
24682469
}
24692470
}
24702471

2472+
TEST(RenameTest, RenameWithExplicitObjectPararameter) {
2473+
Annotations Test = {R"cpp(
2474+
struct Foo {
2475+
int [[memb^er]] {};
2476+
auto&& getter1(this auto&& self) {
2477+
auto local = [&] {
2478+
return self.[[memb^er]];
2479+
}();
2480+
return local + self.[[memb^er]];
2481+
}
2482+
auto&& getter2(this Foo&& self) {
2483+
return self.[[memb^er]];
2484+
}
2485+
int normal() {
2486+
return [[memb^er]];
2487+
}
2488+
};
2489+
)cpp"};
2490+
2491+
auto TU = TestTU::withCode(Test.code());
2492+
TU.ExtraArgs.push_back("-std=c++23");
2493+
auto AST = TU.build();
2494+
2495+
llvm::StringRef NewName = "m_member";
2496+
auto Index = TU.index();
2497+
2498+
for (const auto &RenamePos : Test.points()) {
2499+
auto RenameResult = rename({RenamePos, NewName, AST, testPath(TU.Filename),
2500+
getVFSFromAST(AST), Index.get()});
2501+
2502+
ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
2503+
auto Res = RenameResult.get();
2504+
2505+
ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
2506+
ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
2507+
EXPECT_EQ(applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
2508+
expectedResult(Test, NewName));
2509+
}
2510+
}
2511+
24712512
} // namespace
24722513
} // namespace clangd
24732514
} // namespace clang

clang/lib/Sema/HeuristicResolver.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "clang/AST/TemplateBase.h"
1515
#include "clang/AST/Type.h"
1616
#include "llvm/ADT/identity.h"
17+
#include <optional>
1718

1819
namespace clang {
1920

@@ -301,9 +302,34 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
301302
return {};
302303
}
303304

305+
// check if member expr is in the context of an explicit object method
306+
// If so, it's safe to assume the templated arg is of type of the record
307+
const auto ExplicitMemberHeuristic =
308+
[&](const Expr *Base) -> std::optional<QualType> {
309+
if (auto *DeclRef = dyn_cast_if_present<DeclRefExpr>(Base)) {
310+
auto *PrDecl = dyn_cast_if_present<ParmVarDecl>(DeclRef->getDecl());
311+
312+
if (PrDecl && PrDecl->isExplicitObjectParameter()) {
313+
auto CxxRecord = dyn_cast_if_present<CXXRecordDecl>(
314+
PrDecl->getDeclContext()->getParent());
315+
316+
if (CxxRecord) {
317+
return Ctx.getTypeDeclType(dyn_cast<TypeDecl>(CxxRecord));
318+
}
319+
}
320+
}
321+
322+
return std::nullopt;
323+
};
324+
304325
// Try resolving the member inside the expression's base type.
305326
Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
306327
QualType BaseType = ME->getBaseType();
328+
329+
if (auto Type = ExplicitMemberHeuristic(Base)) {
330+
BaseType = *Type;
331+
}
332+
307333
BaseType = simplifyType(BaseType, Base, ME->isArrow());
308334
return resolveDependentMember(BaseType, ME->getMember(), NoFilter);
309335
}

clang/unittests/Sema/HeuristicResolverTest.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ template <typename InputNode, typename ParamT, typename InputMatcher,
4141
typename... OutputMatchers>
4242
void expectResolution(llvm::StringRef Code, ResolveFnT<ParamT> ResolveFn,
4343
const InputMatcher &IM, const OutputMatchers &...OMS) {
44-
auto TU = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"});
44+
auto TU = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++23"});
4545
auto &Ctx = TU->getASTContext();
4646
auto InputMatches = match(IM, Ctx);
4747
ASSERT_EQ(1u, InputMatches.size());
@@ -449,6 +449,23 @@ TEST(HeuristicResolver, MemberExpr_DefaultTemplateArgument_Recursive) {
449449
cxxMethodDecl(hasName("foo")).bind("output"));
450450
}
451451

452+
TEST(HeuristicResolver, MemberExpr_ExplicitObjectParameter) {
453+
std::string Code = R"cpp(
454+
struct Foo {
455+
int m_int;
456+
457+
int bar(this auto&& self) {
458+
return self.m_int;
459+
}
460+
};
461+
)cpp";
462+
// Test resolution of "m_int" in "self.m_int()".
463+
expectResolution(
464+
Code, &HeuristicResolver::resolveMemberExpr,
465+
cxxDependentScopeMemberExpr(hasMemberName("m_int")).bind("input"),
466+
fieldDecl(hasName("m_int")).bind("output"));
467+
}
468+
452469
TEST(HeuristicResolver, DeclRefExpr_StaticMethod) {
453470
std::string Code = R"cpp(
454471
template <typename T>

0 commit comments

Comments
 (0)