tom-anders updated this revision to Diff 489391. tom-anders added a comment.
Factor out logic into helper function Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D141800/new/ https://reviews.llvm.org/D141800 Files: clang-tools-extra/clangd/CodeComplete.cpp clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3063,6 +3063,25 @@ AllOf(qualifier(""), scope("na::"), named("ClangdA")))); } +// https://github.com/clangd/clangd/issues/1361 +TEST(CompletionTest, ScopedEnumUsingDecl) { + clangd::CodeCompleteOptions Opts = {}; + Opts.AllScopes = true; + + auto Results = completions( + R"cpp( + namespace ns { enum class Scoped { FooBar }; } + using ns::Scoped; + void f() { + Foo^ + } + )cpp", + {enmConstant("ns::Scoped::FooBar")}, Opts); + EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf( + qualifier("Scoped::"), named("FooBar"), + kind(CompletionItemKind::EnumMember)))); +} + TEST(CompletionTest, AllScopesCompletion) { clangd::CodeCompleteOptions Opts = {}; Opts.AllScopes = true; Index: clang-tools-extra/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/clangd/CodeComplete.cpp +++ clang-tools-extra/clangd/CodeComplete.cpp @@ -311,7 +311,8 @@ // computed from the first candidate, in the constructor. // Others vary per candidate, so add() must be called for remaining candidates. struct CodeCompletionBuilder { - CodeCompletionBuilder(ASTContext *ASTCtx, const CompletionCandidate &C, + CodeCompletionBuilder(ASTContext *ASTCtx, DeclContext *SemaDeclCtx, + const CompletionCandidate &C, CodeCompletionString *SemaCCS, llvm::ArrayRef<std::string> QueryScopes, const IncludeInserter &Includes, @@ -374,6 +375,8 @@ ShortestQualifier = Qualifier; } Completion.RequiredQualifier = std::string(ShortestQualifier); + + stripNamespaceForEnumConstantIfUsingDecl(*C.IndexResult, SemaDeclCtx); } } if (C.IdentifierResult) { @@ -430,6 +433,30 @@ }); } + // With all-scopes-completion, we can complete enum constants of scoped + // enums, in which case the completion might not be visible to Sema. + // So, if there's a using declaration for the enum class, manually + // drop the qualifiers. + void stripNamespaceForEnumConstantIfUsingDecl(const Symbol &IndexResult, + DeclContext *SemaDeclCtx) { + if (IndexResult.SymInfo.Kind != index::SymbolKind::EnumConstant) + return; + for (auto *Ctx = SemaDeclCtx; Ctx; Ctx = Ctx->getParent()) + for (auto *D : Ctx->decls()) { + const auto *UD = dyn_cast<UsingShadowDecl>(D); + if (!UD) + continue; + const auto *ED = dyn_cast<EnumDecl>(UD->getTargetDecl()); + if (!ED || !ED->isScoped()) + continue; + auto EnumName = printQualifiedName(*ED) + "::"; + if (EnumName == IndexResult.Scope) { + Completion.RequiredQualifier = ED->getName().str() + "::"; + return; + } + } + } + void add(const CompletionCandidate &C, CodeCompletionString *SemaCCS) { assert(bool(C.SemaResult) == bool(SemaCCS)); Bundled.emplace_back(); @@ -1963,7 +1990,8 @@ : nullptr; if (!Builder) Builder.emplace(Recorder ? &Recorder->CCSema->getASTContext() : nullptr, - Item, SemaCCS, QueryScopes, *Inserter, FileName, + Recorder ? Recorder->CCSema->CurContext : nullptr, Item, + SemaCCS, QueryScopes, *Inserter, FileName, CCContextKind, Opts, IsUsingDeclaration, NextTokenKind); else Builder->add(Item, SemaCCS);
Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -3063,6 +3063,25 @@ AllOf(qualifier(""), scope("na::"), named("ClangdA")))); } +// https://github.com/clangd/clangd/issues/1361 +TEST(CompletionTest, ScopedEnumUsingDecl) { + clangd::CodeCompleteOptions Opts = {}; + Opts.AllScopes = true; + + auto Results = completions( + R"cpp( + namespace ns { enum class Scoped { FooBar }; } + using ns::Scoped; + void f() { + Foo^ + } + )cpp", + {enmConstant("ns::Scoped::FooBar")}, Opts); + EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf( + qualifier("Scoped::"), named("FooBar"), + kind(CompletionItemKind::EnumMember)))); +} + TEST(CompletionTest, AllScopesCompletion) { clangd::CodeCompleteOptions Opts = {}; Opts.AllScopes = true; Index: clang-tools-extra/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/clangd/CodeComplete.cpp +++ clang-tools-extra/clangd/CodeComplete.cpp @@ -311,7 +311,8 @@ // computed from the first candidate, in the constructor. // Others vary per candidate, so add() must be called for remaining candidates. struct CodeCompletionBuilder { - CodeCompletionBuilder(ASTContext *ASTCtx, const CompletionCandidate &C, + CodeCompletionBuilder(ASTContext *ASTCtx, DeclContext *SemaDeclCtx, + const CompletionCandidate &C, CodeCompletionString *SemaCCS, llvm::ArrayRef<std::string> QueryScopes, const IncludeInserter &Includes, @@ -374,6 +375,8 @@ ShortestQualifier = Qualifier; } Completion.RequiredQualifier = std::string(ShortestQualifier); + + stripNamespaceForEnumConstantIfUsingDecl(*C.IndexResult, SemaDeclCtx); } } if (C.IdentifierResult) { @@ -430,6 +433,30 @@ }); } + // With all-scopes-completion, we can complete enum constants of scoped + // enums, in which case the completion might not be visible to Sema. + // So, if there's a using declaration for the enum class, manually + // drop the qualifiers. + void stripNamespaceForEnumConstantIfUsingDecl(const Symbol &IndexResult, + DeclContext *SemaDeclCtx) { + if (IndexResult.SymInfo.Kind != index::SymbolKind::EnumConstant) + return; + for (auto *Ctx = SemaDeclCtx; Ctx; Ctx = Ctx->getParent()) + for (auto *D : Ctx->decls()) { + const auto *UD = dyn_cast<UsingShadowDecl>(D); + if (!UD) + continue; + const auto *ED = dyn_cast<EnumDecl>(UD->getTargetDecl()); + if (!ED || !ED->isScoped()) + continue; + auto EnumName = printQualifiedName(*ED) + "::"; + if (EnumName == IndexResult.Scope) { + Completion.RequiredQualifier = ED->getName().str() + "::"; + return; + } + } + } + void add(const CompletionCandidate &C, CodeCompletionString *SemaCCS) { assert(bool(C.SemaResult) == bool(SemaCCS)); Bundled.emplace_back(); @@ -1963,7 +1990,8 @@ : nullptr; if (!Builder) Builder.emplace(Recorder ? &Recorder->CCSema->getASTContext() : nullptr, - Item, SemaCCS, QueryScopes, *Inserter, FileName, + Recorder ? Recorder->CCSema->CurContext : nullptr, Item, + SemaCCS, QueryScopes, *Inserter, FileName, CCContextKind, Opts, IsUsingDeclaration, NextTokenKind); else Builder->add(Item, SemaCCS);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits