Skip to content

Commit f44eaf4

Browse files
authored
[clang][HeuristicResolver] Resolve explicit object parameter to enclosing record type (#155143)
Heuristically resolve the type of a `this auto` parameter to the record type in the declaration. ```cpp struct Foo { int member {}; auto&& getter1(this auto&& self) { // assume `self` is is `Foo` return self.member; }; ``` Fixes clangd/clangd#2323
1 parent 5d111a2 commit f44eaf4

File tree

3 files changed

+55
-2
lines changed

3 files changed

+55
-2
lines changed

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

Lines changed: 22 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 {
@@ -861,13 +862,33 @@ TEST(RenameTest, WithinFileRename) {
861862
862863
void func([[Fo^o]] *f) {}
863864
)cpp",
865+
866+
// rename with explicit object parameter
867+
R"cpp(
868+
struct Foo {
869+
int [[memb^er]] {};
870+
auto&& getter1(this auto&& self) {
871+
auto local = [&] {
872+
return self.[[memb^er]];
873+
}();
874+
return local + self.[[memb^er]];
875+
}
876+
auto&& getter2(this Foo&& self) {
877+
return self.[[memb^er]];
878+
}
879+
int normal() {
880+
return this->[[mem^ber]] + [[memb^er]];
881+
}
882+
};
883+
)cpp",
864884
};
865885
llvm::StringRef NewName = "NewName";
866886
for (llvm::StringRef T : Tests) {
867887
SCOPED_TRACE(T);
868888
Annotations Code(T);
869889
auto TU = TestTU::withCode(Code.code());
870890
TU.ExtraArgs.push_back("-xobjective-c++");
891+
TU.ExtraArgs.push_back("-std=c++23");
871892
auto AST = TU.build();
872893
auto Index = TU.index();
873894
for (const auto &RenamePos : Code.points()) {

clang/lib/Sema/HeuristicResolver.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,21 @@ QualType HeuristicResolverImpl::simplifyType(QualType Type, const Expr *E,
255255
}
256256
}
257257
}
258+
// Check if the expression refers to an explicit object parameter of
259+
// templated type. If so, heuristically treat it as having the type of the
260+
// enclosing class.
261+
if (!T.Type.isNull() &&
262+
(T.Type->isUndeducedAutoType() || T.Type->isTemplateTypeParmType())) {
263+
if (auto *DRE = dyn_cast_if_present<DeclRefExpr>(T.E)) {
264+
auto *PrDecl = dyn_cast<ParmVarDecl>(DRE->getDecl());
265+
if (PrDecl && PrDecl->isExplicitObjectParameter()) {
266+
const auto *Parent =
267+
dyn_cast<TagDecl>(PrDecl->getDeclContext()->getParent());
268+
return {Ctx.getCanonicalTagType(Parent)};
269+
}
270+
}
271+
}
272+
258273
return T;
259274
};
260275
// As an additional protection against infinite loops, bound the number of

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)