Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -656,6 +656,13 @@ llvm_unreachable("unknown code completion context"); } +static bool isInjectedClass(const NamedDecl &D) { + if (auto *R = dyn_cast_or_null(&D)) + if (R->isInjectedClassName()) + return true; + return false; +} + // Some member calls are blacklisted because they're so rarely useful. static bool isBlacklistedMember(const NamedDecl &D) { // Destructor completion is rarely useful, and works inconsistently. @@ -663,9 +670,8 @@ if (D.getKind() == Decl::CXXDestructor) return true; // Injected name may be useful for A::foo(), but who writes A::A::foo()? - if (auto *R = dyn_cast_or_null(&D)) - if (R->isInjectedClassName()) - return true; + if (isInjectedClass(D)) + return true; // Explicit calls to operators are also rare. auto NameKind = D.getDeclName().getNameKind(); if (NameKind == DeclarationName::CXXOperatorName || @@ -744,6 +750,11 @@ !Context.getBaseType().isNull() // is this a member-access context? && isBlacklistedMember(*Result.Declaration)) continue; + // Skip injected class name when no class scope is not explicitly set. + // E.g. show injected A::A in `using A::A^` but not in "A^". + if (Result.Declaration && !Context.getCXXScopeSpecifier().hasValue() && + isInjectedClass(*Result.Declaration)) + continue; // We choose to never append '::' to completion results in clangd. Result.StartsNestedNameSpecifier = false; Results.push_back(Result); Index: unittests/clangd/CodeCompleteTests.cpp =================================================================== --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -416,6 +416,11 @@ Has("X")); } +TEST(CompletionTest, SkipInjectedWhenUnqualified) { + EXPECT_THAT(completions("struct X { void f() { X^ }};").Completions, + ElementsAre(Named("X"), Named("~X"))); +} + TEST(CompletionTest, Snippets) { clangd::CodeCompleteOptions Opts; auto Results = completions(