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

Reply via email to