hokein created this revision.
hokein added reviewers: sammccall, ilya-biryukov.
Herald added a subscriber: klimek.

Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D42073

Files:
  clangd/CodeComplete.cpp
  unittests/clangd/CodeCompleteTests.cpp

Index: unittests/clangd/CodeCompleteTests.cpp
===================================================================
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -58,6 +58,7 @@
 using ::testing::Each;
 using ::testing::ElementsAre;
 using ::testing::Not;
+using ::testing::Field;
 using ::testing::UnorderedElementsAre;
 
 class IgnoreDiagnostics : public DiagnosticsConsumer {
@@ -481,6 +482,21 @@
   EXPECT_EQ(1, Results.activeParameter);
 }
 
+class IndexRequestCollector : public SymbolIndex {
+public:
+  bool
+  fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
+            llvm::function_ref<void(const Symbol &)> Callback) const override {
+    Requests.push_back(Req);
+    return false;
+  }
+
+  const std::vector<FuzzyFindRequest> allRequests() const { return Requests; }
+
+private:
+  mutable std::vector<FuzzyFindRequest> Requests;
+};
+
 std::unique_ptr<SymbolIndex> simpleIndexFromSymbols(
     std::vector<std::pair<std::string, index::SymbolKind>> Symbols) {
   SymbolSlab::Builder Slab;
@@ -660,6 +676,48 @@
                                             Doc("Doooc"), Detail("void"))));
 }
 
+TEST(CompletionTest, AllVisibleScopesChain) {
+  clangd::CodeCompleteOptions Opts;
+  IndexRequestCollector Requests;
+  Opts.Index = &Requests;
+
+  auto Results = completions(R"cpp(
+      namespace ns1 {}
+      namespace ns2 {} // ignore
+      namespace ns3 {}
+
+      namespace ns4 { using namespace ns3; }
+      using namespace ns1;
+      namespace ns5 { using namespace ns4; }
+      namespace ns5 { bar::^ }
+  )cpp",
+                             Opts);
+
+  EXPECT_THAT(
+      Requests.allRequests(),
+      ElementsAre(Field(&FuzzyFindRequest::Scopes,
+                        UnorderedElementsAre("bar", "ns4::bar", "ns3::bar",
+                                             "ns5::bar", "ns1::bar"))));
+}
+
+TEST(CompletionTest, AllVisibleScopesNested) {
+  clangd::CodeCompleteOptions Opts;
+  IndexRequestCollector Requests;
+  Opts.Index = &Requests;
+
+  auto Results = completions(R"cpp(
+      namespace ns1 { namespace ns4 {}  }
+      namespace ns1 { namespace ns2 { namespace ns3 { bar::^ } } }
+  )cpp",
+                             Opts);
+
+  EXPECT_THAT(
+      Requests.allRequests(),
+      ElementsAre(Field(&FuzzyFindRequest::Scopes,
+                        UnorderedElementsAre("bar", "ns1::bar", "ns1::ns2::bar",
+                                             "ns1::ns2::ns3::bar"))));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clangd/CodeComplete.cpp
===================================================================
--- clangd/CodeComplete.cpp
+++ clangd/CodeComplete.cpp
@@ -23,6 +23,7 @@
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Sema/Lookup.h"
 #include "clang/Sema/Sema.h"
 #include "llvm/Support/Format.h"
 #include <queue>
@@ -258,16 +259,34 @@
   }
 };
 
-/// \brief Information about the scope specifier in the qualified-id code
-/// completion (e.g. "ns::ab?").
-struct SpecifiedScope {
-  /// The scope specifier as written. For example, for completion "ns::ab?", the
-  /// written scope specifier is "ns".
-  std::string Written;
-  // If this scope specifier is recognized in Sema (e.g. as a namespace
-  // context), this will be set to the fully qualfied name of the corresponding
-  // context.
-  std::string Resolved;
+/// \brief Scopes being queried in indexes for the qualified-id code completion
+/// (e.g. "ns::ab?").
+struct QueryScopes {
+  /// All scopes being queried in indexes. Each scope should meet the
+  /// restrictions described in FuzzyFindRequest::Scopes.
+  ///
+  /// The scopes are constructed from the written scope specifier as well as all
+  /// namespace scopes which are visible to the qualified-id completion token.
+  std::vector<std::string> Scopes;
+};
+
+class VisibleNamespaceFinder : public VisibleDeclConsumer {
+public:
+  void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx,
+                 bool InBaseClass) override {}
+
+  void BeginVisitContext(DeclContext *Ctx) override {
+    // We only interest in namespace declarations.
+    if (const auto *NN = dyn_cast<NamespaceDecl>(Ctx))
+      VisibleNamespaces.insert(NN->getQualifiedNameAsString());
+  }
+
+  const std::set<std::string> &getVisibleNamespaces() const {
+    return VisibleNamespaces;
+  }
+
+private:
+  std::set<std::string> VisibleNamespaces;
 };
 
 /// \brief Information from sema about (parital) symbol names to be completed.
@@ -278,10 +297,11 @@
   std::string Filter;
 
   /// This is set if the completion is for qualified IDs, e.g. "abc::x^".
-  llvm::Optional<SpecifiedScope> SSInfo;
+  llvm::Optional<QueryScopes> QScopes;
 };
 
-SpecifiedScope extraCompletionScope(Sema &S, const CXXScopeSpec &SS);
+QueryScopes extraCompletionScope(
+    Sema &S, const CodeCompletionContext::QualifiedCompletionContext &QCC);
 
 class CompletionItemsCollector : public CodeCompleteConsumer {
 public:
@@ -298,8 +318,11 @@
                                   CodeCompletionResult *Results,
                                   unsigned NumResults) override final {
     FuzzyMatcher Filter(S.getPreprocessor().getCodeCompletionFilter());
-    if (auto SS = Context.getCXXScopeSpecifier())
-      CompletedName.SSInfo = extraCompletionScope(S, **SS);
+    if (auto QualifiedCompletionContext =
+            Context.getQualifiedCompletionContext()) {
+      CompletedName.QScopes =
+          extraCompletionScope(S, **QualifiedCompletionContext);
+    }
 
     CompletedName.Filter = S.getPreprocessor().getCodeCompletionFilter();
     std::priority_queue<CompletionCandidate> Candidates;
@@ -557,7 +580,6 @@
 }
 
 CompletionItem indexCompletionItem(const Symbol &Sym, llvm::StringRef Filter,
-                                   const SpecifiedScope &SSInfo,
                                    llvm::StringRef DebuggingLabel = "") {
   CompletionItem Item;
   Item.kind = toCompletionItemKind(Sym.SymInfo.Kind);
@@ -597,38 +619,53 @@
 }
 
 void completeWithIndex(const Context &Ctx, const SymbolIndex &Index,
-                       llvm::StringRef Code, const SpecifiedScope &SSInfo,
+                       llvm::StringRef Code, const QueryScopes &QScopes,
                        llvm::StringRef Filter, CompletionList *Items,
                        llvm::StringRef DebuggingLabel = "") {
   FuzzyFindRequest Req;
   Req.Query = Filter;
-  // FIXME(ioeric): add more possible scopes based on using namespaces and
-  // containing namespaces.
-  StringRef Scope = SSInfo.Resolved.empty() ? SSInfo.Written : SSInfo.Resolved;
-  Req.Scopes = {Scope.trim(':').str()};
-
+  Req.Scopes = QScopes.Scopes, log(Ctx, "Query scopes: [");
+  for (auto &R : Req.Scopes)
+    log(Ctx, R);
+  log(Ctx, "]");
   Items->isIncomplete |= !Index.fuzzyFind(Ctx, Req, [&](const Symbol &Sym) {
-    Items->items.push_back(
-        indexCompletionItem(Sym, Filter, SSInfo, DebuggingLabel));
+    Items->items.push_back(indexCompletionItem(Sym, Filter, DebuggingLabel));
   });
 }
 
-SpecifiedScope extraCompletionScope(Sema &S, const CXXScopeSpec &SS) {
-  SpecifiedScope Info;
+QueryScopes extraCompletionScope(
+    Sema &S, const CodeCompletionContext::QualifiedCompletionContext &QCC) {
+  QueryScopes Info;
   auto &SM = S.getSourceManager();
-  auto SpecifierRange = SS.getRange();
-  Info.Written = Lexer::getSourceText(
-      CharSourceRange::getCharRange(SpecifierRange), SM, clang::LangOptions());
+  const auto &SS = QCC.ScopeSpecifier;
   if (SS.isValid()) {
+    // FIXME: because of Sema typo correction, the namespace decl returned from
+    // Sema might not perfectly match the written qualifier. For example, a user
+    // types "clangd::" while there is a mere `namespace clang {}` in the
+    // current file, we will get `clang` namespace declaration here. We might
+    // want to disable the typo correction in Sema during code completion.
+    //
+    // FIXME: Add possible namespace scopes which are visible in DC.
     DeclContext *DC = S.computeDeclContext(SS);
-    if (auto *NS = llvm::dyn_cast<NamespaceDecl>(DC)) {
-      Info.Resolved = NS->getQualifiedNameAsString();
+    if (auto *NS = dyn_cast<NamespaceDecl>(DC)) {
+      Info.Scopes.push_back(NS->getQualifiedNameAsString());
     } else if (llvm::dyn_cast<TranslationUnitDecl>(DC) != nullptr) {
-      Info.Resolved = "::";
-      // Sema does not include the suffix "::" in the range of SS, so we add
-      // it back here.
-      Info.Written = "::";
+      // No explicit "::" for global namespace.
+      Info.Scopes.push_back("");
     }
+  } else {
+    VisibleNamespaceFinder Finder;
+    S.LookupVisibleDecls(QCC.ClosestScope,
+                         clang::Sema::LookupNameKind::LookupOrdinaryName,
+                         Finder, true);
+    auto SpecifierRange = SS.getRange();
+    StringRef WrittenScope =
+        Lexer::getSourceText(CharSourceRange::getCharRange(SpecifierRange), SM,
+                             clang::LangOptions())
+            .trim(':');
+    Info.Scopes.push_back(WrittenScope);
+    for (auto &It : Finder.getVisibleNamespaces())
+      Info.Scopes.push_back(It + "::" + WrittenScope.str());
   }
   return Info;
 }
@@ -668,8 +705,8 @@
   // Got scope specifier (ns::f^) for code completion from sema, try to query
   // global symbols from indexes.
   // FIXME: merge with Sema results, and respect limits.
-  if (CompletedName.SSInfo && Opts.Index)
-    completeWithIndex(Ctx, *Opts.Index, Contents, *CompletedName.SSInfo,
+  if (CompletedName.QScopes && Opts.Index)
+    completeWithIndex(Ctx, *Opts.Index, Contents, *CompletedName.QScopes,
                       CompletedName.Filter, &Results, /*DebuggingLabel=*/"I");
   return Results;
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to