diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3453,6 +3453,19 @@ } } +TEST(CompletionTest, UndeducedType) { + clangd::CodeCompleteOptions Opts; + const std::string Code = R"cpp( +struct Base { Base foo(); }; + +template struct Foo : Base { + void bar() { this->foo().^foo(); } +}; + )cpp"; + + EXPECT_THAT(completions(Code, {}, Opts).Completions, + Contains(Labeled("foo()"))); +} } // namespace } // namespace clangd } // namespace clang diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -5411,9 +5411,21 @@ Base = Base->getPointeeType(); // could handle unique_ptr etc here? RecordDecl *RD = Base.isNull() ? nullptr : getAsRecordDecl(Base); if (RD && RD->isCompleteDefinition()) { - for (const auto *Member : RD->lookup(CDSME->getMember())) - if (const ValueDecl *VD = llvm::dyn_cast(Member)) - return VD->getType().getNonReferenceType(); + // For c++ records perform a dependent name lookup, which also takes care + // of the bases. + if (auto *CXXRD = llvm::dyn_cast(RD)) { + for (const auto *Member : CXXRD->lookupDependentName( + CDSME->getMember(), [](const NamedDecl *Member) { + return llvm::isa(Member); + })) { + return llvm::cast(Member)->getType().getNonReferenceType(); + } + } else { + for (const auto *Member : RD->lookup(CDSME->getMember())) { + if (const ValueDecl *VD = llvm::dyn_cast(Member)) + return VD->getType().getNonReferenceType(); + } + } } } return Unresolved;