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 @@ -552,15 +552,25 @@ struct OtherClass { OtherClass() { Foo f; + Derived d; f.$canBeCall^ + ; // Prevent parsing as 'f.f' + f.Foo::$canBeCall^ &Foo::$canNotBeCall^ + ; + d.Foo::$canBeCall^ } }; int main() { Foo f; + Derived d; f.$canBeCall^ + ; // Prevent parsing as 'f.f' + f.Foo::$canBeCall^ &Foo::$canNotBeCall^ + ; + d.Foo::$canBeCall^ } )cpp"); auto TU = TestTU::withCode(Code.code()); 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 @@ -338,8 +338,11 @@ /// /// \param InBaseClass whether the result was found in a base /// class of the searched context. + /// + /// \param BaseType the type of expression that precedes the "." or "->" + /// in a member access expression. void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding, - bool InBaseClass); + bool InBaseClass, QualType BaseType); /// Add a new non-declaration result to this result set. void AddResult(Result R); @@ -1262,7 +1265,8 @@ } void ResultBuilder::AddResult(Result R, DeclContext *CurContext, - NamedDecl *Hiding, bool InBaseClass = false) { + NamedDecl *Hiding, bool InBaseClass = false, + QualType BaseType = QualType()) { if (R.Kind != Result::RK_Declaration) { // For non-declaration results, just add the result. Results.push_back(R); @@ -1380,9 +1384,7 @@ OverloadSet.Add(Method, Results.size()); } - // When completing a non-static member function (and not via - // dot/arrow member access) and we're not inside that class' scope, - // it can't be a call. + // Decide whether or not a non-static member function can be a call. if (CompletionContext.getKind() == clang::CodeCompletionContext::CCC_Symbol) { const NamedDecl *ND = R.getDeclaration(); if (const auto *FuncTmpl = dyn_cast(ND)) { @@ -1404,10 +1406,24 @@ return nullptr; }(); + // When completing a non-static member function (and not via + // dot/arrow member access) and we're not inside that class' scope, + // it can't be a call. R.FunctionCanBeCall = CurrentClassScope && (CurrentClassScope == Method->getParent() || CurrentClassScope->isDerivedFrom(Method->getParent())); + + // If the member access "." or "->" is followed by a qualified Id and the + // object type is derived from or the same as that of the Id, then + // the candidate functions should be perceived as calls. + if (const CXXRecordDecl *MaybeDerived = nullptr; + !BaseType.isNull() && + (MaybeDerived = BaseType->getAsCXXRecordDecl())) { + auto *MaybeBase = Method->getParent(); + R.FunctionCanBeCall = + MaybeDerived == MaybeBase || MaybeDerived->isDerivedFrom(MaybeBase); + } } } @@ -1683,7 +1699,7 @@ bool InBaseClass) override { ResultBuilder::Result Result(ND, Results.getBasePriority(ND), nullptr, false, IsAccessible(ND, Ctx), FixIts); - Results.AddResult(Result, InitialLookupCtx, Hiding, InBaseClass); + Results.AddResult(Result, InitialLookupCtx, Hiding, InBaseClass, BaseType); } void EnteredContext(DeclContext *Ctx) override { diff --git a/clang/unittests/Sema/CodeCompleteTest.cpp b/clang/unittests/Sema/CodeCompleteTest.cpp --- a/clang/unittests/Sema/CodeCompleteTest.cpp +++ b/clang/unittests/Sema/CodeCompleteTest.cpp @@ -214,15 +214,25 @@ struct OtherClass { OtherClass() { Foo f; + Derived d; f.$canBeCall^ + ; // Prevent parsing as 'f.f' + f.Foo::$canBeCall^ &Foo::$cannotBeCall^ + ; + d.Foo::$canBeCall^ } }; int main() { Foo f; + Derived d; f.$canBeCall^ + ; // Prevent parsing as 'f.f' + f.Foo::$canBeCall^ &Foo::$cannotBeCall^ + ; + d.Foo::$canBeCall^ } )cpp");