diff --git a/clang-tools-extra/clangd/HeuristicResolver.cpp b/clang-tools-extra/clangd/HeuristicResolver.cpp --- a/clang-tools-extra/clangd/HeuristicResolver.cpp +++ b/clang-tools-extra/clangd/HeuristicResolver.cpp @@ -16,6 +16,7 @@ // Convenience lambdas for use as the 'Filter' parameter of // HeuristicResolver::resolveDependentMember(). +const auto NoFilter = [](const NamedDecl *D) { return true; }; const auto NonStaticFilter = [](const NamedDecl *D) { return D->isCXXInstanceMember(); }; @@ -90,6 +91,28 @@ std::vector HeuristicResolver::resolveMemberExpr( const CXXDependentScopeMemberExpr *ME) const { + // If the expression has a qualifier, first try resolving the member + // inside the qualifier's type. + // Note that we cannot use a NonStaticFilter in either case, for a couple + // of reasons: + // 1. It's valid to access a static member using instance member syntax, + // e.g. `instance.static_member`. + // 2. We can sometimes get a CXXDependentScopeMemberExpr for static + // member syntax too, e.g. if `X::static_member` occurs inside + // an instance method, it's represented as a CXXDependentScopeMemberExpr + // with `this` as the base expression as `X` as the qualifier + // (which could be valid if `X` names a base class after instantiation). + if (NestedNameSpecifier *NNS = ME->getQualifier()) { + if (const Type *QualifierType = resolveNestedNameSpecifierToType(NNS)) { + auto Decls = + resolveDependentMember(QualifierType, ME->getMember(), NoFilter); + if (!Decls.empty()) + return Decls; + } + } + + // If that didn't yield any results, try resolving the member inside + // the expression's base type. const Type *BaseType = ME->getBaseType().getTypePtrOrNull(); if (ME->isArrow()) { BaseType = getPointeeType(BaseType); @@ -105,7 +128,7 @@ BaseType = resolveExprToType(Base); } } - return resolveDependentMember(BaseType, ME->getMember(), NonStaticFilter); + return resolveDependentMember(BaseType, ME->getMember(), NoFilter); } std::vector HeuristicResolver::resolveDeclRefExpr( diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -285,7 +285,6 @@ void bar(A a, T t) { nonmember($par1[[t]]); a.member($par2[[t]]); - // FIXME: This one does not work yet. A::static_member($par3[[t]]); // We don't want to arbitrarily pick between // "anInt" or "aDouble", so just show no hint. @@ -294,7 +293,8 @@ }; )cpp", ExpectedHint{"par1: ", "par1"}, - ExpectedHint{"par2: ", "par2"}); + ExpectedHint{"par2: ", "par2"}, + ExpectedHint{"par3: ", "par3"}); } TEST(ParameterHints, VariadicFunction) {