[clang-tools-extra] r321083 - [clangd] Index-based code completion.

Eric Liu via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 19 08:50:37 PST 2017


Author: ioeric
Date: Tue Dec 19 08:50:37 2017
New Revision: 321083

URL: http://llvm.org/viewvc/llvm-project?rev=321083&view=rev
Log:
[clangd] Index-based code completion.

Summary: Use symbol index to populate completion results for qualfified IDs e.g. "nx::A^".

Reviewers: ilya-biryukov, sammccall

Reviewed By: ilya-biryukov, sammccall

Subscribers: rwols, klimek, mgorny, cfe-commits, sammccall

Differential Revision: https://reviews.llvm.org/D41281

Modified:
    clang-tools-extra/trunk/clangd/CodeComplete.cpp
    clang-tools-extra/trunk/clangd/CodeComplete.h
    clang-tools-extra/trunk/clangd/index/FileIndex.cpp
    clang-tools-extra/trunk/clangd/index/FileIndex.h
    clang-tools-extra/trunk/clangd/index/Index.h
    clang-tools-extra/trunk/clangd/index/MemIndex.cpp
    clang-tools-extra/trunk/clangd/index/MemIndex.h
    clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
    clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp

Modified: clang-tools-extra/trunk/clangd/CodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.cpp?rev=321083&r1=321082&r2=321083&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CodeComplete.cpp (original)
+++ clang-tools-extra/trunk/clangd/CodeComplete.cpp Tue Dec 19 08:50:37 2017
@@ -16,6 +16,8 @@
 
 #include "CodeComplete.h"
 #include "Compiler.h"
+#include "Logger.h"
+#include "index/Index.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
@@ -26,26 +28,28 @@ namespace clang {
 namespace clangd {
 namespace {
 
-CompletionItemKind getKindOfDecl(CXCursorKind CursorKind) {
+CompletionItemKind toCompletionItemKind(CXCursorKind CursorKind) {
   switch (CursorKind) {
   case CXCursor_MacroInstantiation:
   case CXCursor_MacroDefinition:
     return CompletionItemKind::Text;
   case CXCursor_CXXMethod:
+  case CXCursor_Destructor:
     return CompletionItemKind::Method;
   case CXCursor_FunctionDecl:
   case CXCursor_FunctionTemplate:
     return CompletionItemKind::Function;
   case CXCursor_Constructor:
-  case CXCursor_Destructor:
     return CompletionItemKind::Constructor;
   case CXCursor_FieldDecl:
     return CompletionItemKind::Field;
   case CXCursor_VarDecl:
   case CXCursor_ParmDecl:
     return CompletionItemKind::Variable;
-  case CXCursor_ClassDecl:
+  // FIXME(ioeric): use LSP struct instead of class when it is suppoted in the
+  // protocol.
   case CXCursor_StructDecl:
+  case CXCursor_ClassDecl:
   case CXCursor_UnionDecl:
   case CXCursor_ClassTemplate:
   case CXCursor_ClassTemplatePartialSpecialization:
@@ -58,6 +62,7 @@ CompletionItemKind getKindOfDecl(CXCurso
     return CompletionItemKind::Value;
   case CXCursor_EnumDecl:
     return CompletionItemKind::Enum;
+  // FIXME(ioeric): figure out whether reference is the right type for aliases.
   case CXCursor_TypeAliasDecl:
   case CXCursor_TypeAliasTemplateDecl:
   case CXCursor_TypedefDecl:
@@ -69,11 +74,12 @@ CompletionItemKind getKindOfDecl(CXCurso
   }
 }
 
-CompletionItemKind getKind(CodeCompletionResult::ResultKind ResKind,
-                           CXCursorKind CursorKind) {
+CompletionItemKind
+toCompletionItemKind(CodeCompletionResult::ResultKind ResKind,
+                     CXCursorKind CursorKind) {
   switch (ResKind) {
   case CodeCompletionResult::RK_Declaration:
-    return getKindOfDecl(CursorKind);
+    return toCompletionItemKind(CursorKind);
   case CodeCompletionResult::RK_Keyword:
     return CompletionItemKind::Keyword;
   case CodeCompletionResult::RK_Macro:
@@ -85,6 +91,59 @@ CompletionItemKind getKind(CodeCompletio
   llvm_unreachable("Unhandled CodeCompletionResult::ResultKind.");
 }
 
+CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
+  using SK = index::SymbolKind;
+  switch (Kind) {
+  case SK::Unknown:
+    return CompletionItemKind::Missing;
+  case SK::Module:
+  case SK::Namespace:
+  case SK::NamespaceAlias:
+    return CompletionItemKind::Module;
+  case SK::Macro:
+    return CompletionItemKind::Text;
+  case SK::Enum:
+    return CompletionItemKind::Enum;
+  // FIXME(ioeric): use LSP struct instead of class when it is suppoted in the
+  // protocol.
+  case SK::Struct:
+  case SK::Class:
+  case SK::Protocol:
+  case SK::Extension:
+  case SK::Union:
+    return CompletionItemKind::Class;
+  // FIXME(ioeric): figure out whether reference is the right type for aliases.
+  case SK::TypeAlias:
+  case SK::Using:
+    return CompletionItemKind::Reference;
+  case SK::Function:
+  // FIXME(ioeric): this should probably be an operator. This should be fixed
+  // when `Operator` is support type in the protocol.
+  case SK::ConversionFunction:
+    return CompletionItemKind::Function;
+  case SK::Variable:
+  case SK::Parameter:
+    return CompletionItemKind::Variable;
+  case SK::Field:
+    return CompletionItemKind::Field;
+  // FIXME(ioeric): use LSP enum constant when it is supported in the protocol.
+  case SK::EnumConstant:
+    return CompletionItemKind::Value;
+  case SK::InstanceMethod:
+  case SK::ClassMethod:
+  case SK::StaticMethod:
+  case SK::Destructor:
+    return CompletionItemKind::Method;
+  case SK::InstanceProperty:
+  case SK::ClassProperty:
+  case SK::StaticProperty:
+    return CompletionItemKind::Property;
+  case SK::Constructor:
+    return CompletionItemKind::Constructor;
+  }
+  llvm_unreachable("Unhandled clang::index::SymbolKind.");
+}
+
 std::string escapeSnippet(const llvm::StringRef Text) {
   std::string Result;
   Result.reserve(Text.size()); // Assume '$', '}' and '\\' are rare.
@@ -228,20 +287,48 @@ private:
   }
 };
 
+/// \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 Information from sema about (parital) symbol names to be completed.
+/// For example, for completion "ns::ab^", this stores the scope specifier
+/// "ns::" and the completion filter text "ab".
+struct NameToComplete {
+  // The partial identifier being completed, without qualifier.
+  std::string Filter;
+
+  /// This is set if the completion is for qualified IDs, e.g. "abc::x^".
+  llvm::Optional<SpecifiedScope> SSInfo;
+};
+
+SpecifiedScope extraCompletionScope(Sema &S, const CXXScopeSpec &SS);
+
 class CompletionItemsCollector : public CodeCompleteConsumer {
 public:
   CompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
-                           CompletionList &Items)
+                           CompletionList &Items, NameToComplete &CompletedName)
       : CodeCompleteConsumer(CodeCompleteOpts.getClangCompleteOpts(),
                              /*OutputIsBinary=*/false),
         ClangdOpts(CodeCompleteOpts), Items(Items),
         Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
-        CCTUInfo(Allocator) {}
+        CCTUInfo(Allocator), CompletedName(CompletedName) {}
 
   void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
                                   CodeCompletionResult *Results,
                                   unsigned NumResults) override final {
-    StringRef Filter = S.getPreprocessor().getCodeCompletionFilter();
+    if (auto SS = Context.getCXXScopeSpecifier())
+      CompletedName.SSInfo = extraCompletionScope(S, **SS);
+
+    CompletedName.Filter = S.getPreprocessor().getCodeCompletionFilter();
     std::priority_queue<CompletionCandidate> Candidates;
     for (unsigned I = 0; I < NumResults; ++I) {
       auto &Result = Results[I];
@@ -249,7 +336,8 @@ public:
           (Result.Availability == CXAvailability_NotAvailable ||
            Result.Availability == CXAvailability_NotAccessible))
         continue;
-      if (!Filter.empty() && !fuzzyMatch(S, Context, Filter, Result))
+      if (!CompletedName.Filter.empty() &&
+          !fuzzyMatch(S, Context, CompletedName.Filter, Result))
         continue;
       Candidates.emplace(Result);
       if (ClangdOpts.Limit && Candidates.size() > ClangdOpts.Limit) {
@@ -324,7 +412,8 @@ private:
     ProcessChunks(CCS, Item);
 
     // Fill in the kind field of the CompletionItem.
-    Item.kind = getKind(Candidate.Result->Kind, Candidate.Result->CursorKind);
+    Item.kind = toCompletionItemKind(Candidate.Result->Kind,
+                                     Candidate.Result->CursorKind);
 
     return Item;
   }
@@ -336,7 +425,7 @@ private:
   CompletionList &Items;
   std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
   CodeCompletionTUInfo CCTUInfo;
-
+  NameToComplete &CompletedName;
 }; // CompletionItemsCollector
 
 bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) {
@@ -349,8 +438,9 @@ class PlainTextCompletionItemsCollector
 
 public:
   PlainTextCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
-                                    CompletionList &Items)
-      : CompletionItemsCollector(CodeCompleteOpts, Items) {}
+                                    CompletionList &Items,
+                                    NameToComplete &CompletedName)
+      : CompletionItemsCollector(CodeCompleteOpts, Items, CompletedName) {}
 
 private:
   void ProcessChunks(const CodeCompletionString &CCS,
@@ -385,8 +475,9 @@ class SnippetCompletionItemsCollector fi
 
 public:
   SnippetCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
-                                  CompletionList &Items)
-      : CompletionItemsCollector(CodeCompleteOpts, Items) {}
+                                  CompletionList &Items,
+                                  NameToComplete &CompletedName)
+      : CompletionItemsCollector(CodeCompleteOpts, Items, CompletedName) {}
 
 private:
   void ProcessChunks(const CodeCompletionString &CCS,
@@ -648,6 +739,61 @@ bool invokeCodeComplete(const Context &C
   return true;
 }
 
+CompletionItem indexCompletionItem(const Symbol &Sym, llvm::StringRef Filter,
+                                   const SpecifiedScope &SSInfo) {
+  CompletionItem Item;
+  Item.kind = toCompletionItemKind(Sym.SymInfo.Kind);
+  Item.label = Sym.Name;
+  // FIXME(ioeric): support inserting/replacing scope qualifiers.
+  Item.insertText = Sym.Name;
+  // FIXME(ioeric): support snippets.
+  Item.insertTextFormat = InsertTextFormat::PlainText;
+  Item.filterText = Filter;
+
+  // FIXME(ioeric): sort symbols appropriately.
+  Item.sortText = "";
+
+  // FIXME(ioeric): use more symbol information (e.g. documentation, label) to
+  // populate the completion item.
+
+  return Item;
+}
+
+void completeWithIndex(const Context &Ctx, const SymbolIndex &Index,
+                       llvm::StringRef Code, const SpecifiedScope &SSInfo,
+                       llvm::StringRef Filter, CompletionList *Items) {
+  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()};
+
+  Items->isIncomplete = !Index.fuzzyFind(Ctx, Req, [&](const Symbol &Sym) {
+    Items->items.push_back(indexCompletionItem(Sym, Filter, SSInfo));
+  });
+}
+
+SpecifiedScope extraCompletionScope(Sema &S, const CXXScopeSpec &SS) {
+  SpecifiedScope Info;
+  auto &SM = S.getSourceManager();
+  auto SpecifierRange = SS.getRange();
+  Info.Written = Lexer::getSourceText(
+      CharSourceRange::getCharRange(SpecifierRange), SM, clang::LangOptions());
+  if (SS.isValid()) {
+    DeclContext *DC = S.computeDeclContext(SS);
+    if (auto *NS = llvm::dyn_cast<NamespaceDecl>(DC)) {
+      Info.Resolved = NS->getQualifiedNameAsString();
+    } else if (auto *TU = llvm::dyn_cast<TranslationUnitDecl>(DC)) {
+      Info.Resolved = "::";
+      // Sema does not include the suffix "::" in the range of SS, so we add
+      // it back here.
+      Info.Written = "::";
+    }
+  }
+  return Info;
+}
+
 } // namespace
 
 clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
@@ -657,6 +803,9 @@ clang::CodeCompleteOptions CodeCompleteO
   Result.IncludeGlobals = IncludeGlobals;
   Result.IncludeBriefComments = IncludeBriefComments;
 
+  // Enable index-based code completion when Index is provided.
+  Result.IncludeNamespaceLevelDecls = !Index;
+
   return Result;
 }
 
@@ -669,16 +818,24 @@ CompletionList codeComplete(const Contex
                             CodeCompleteOptions Opts) {
   CompletionList Results;
   std::unique_ptr<CodeCompleteConsumer> Consumer;
+  NameToComplete CompletedName;
   if (Opts.EnableSnippets) {
-    Consumer =
-        llvm::make_unique<SnippetCompletionItemsCollector>(Opts, Results);
+    Consumer = llvm::make_unique<SnippetCompletionItemsCollector>(
+        Opts, Results, CompletedName);
   } else {
-    Consumer =
-        llvm::make_unique<PlainTextCompletionItemsCollector>(Opts, Results);
+    Consumer = llvm::make_unique<PlainTextCompletionItemsCollector>(
+        Opts, Results, CompletedName);
   }
   invokeCodeComplete(Ctx, std::move(Consumer), Opts.getClangCompleteOpts(),
                      FileName, Command, Preamble, Contents, Pos, std::move(VFS),
                      std::move(PCHs));
+  if (Opts.Index && CompletedName.SSInfo) {
+    log(Ctx, "WARNING: Got completion results from sema for completion on "
+             "qualified ID while symbol index is provided.");
+    Results.items.clear();
+    completeWithIndex(Ctx, *Opts.Index, Contents, *CompletedName.SSInfo,
+                      CompletedName.Filter, &Results);
+  }
   return Results;
 }
 

Modified: clang-tools-extra/trunk/clangd/CodeComplete.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.h?rev=321083&r1=321082&r2=321083&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/CodeComplete.h (original)
+++ clang-tools-extra/trunk/clangd/CodeComplete.h Tue Dec 19 08:50:37 2017
@@ -19,6 +19,7 @@
 #include "Logger.h"
 #include "Path.h"
 #include "Protocol.h"
+#include "index/Index.h"
 #include "clang/Frontend/PrecompiledPreamble.h"
 #include "clang/Sema/CodeCompleteOptions.h"
 #include "clang/Tooling/CompilationDatabase.h"
@@ -59,6 +60,13 @@ struct CodeCompleteOptions {
   /// Limit the number of results returned (0 means no limit).
   /// If more results are available, we set CompletionList.isIncomplete.
   size_t Limit = 0;
+
+  // Populated internally by clangd, do not set.
+  /// If `Index` is set, it is used to augment the code completion
+  /// results.
+  /// FIXME(ioeric): we might want a better way to pass the index around inside
+  /// clangd.
+  const SymbolIndex *Index = nullptr;
 };
 
 /// Get code completions at a specified \p Pos in \p FileName.

Modified: clang-tools-extra/trunk/clangd/index/FileIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/FileIndex.cpp?rev=321083&r1=321082&r2=321083&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/FileIndex.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/FileIndex.cpp Tue Dec 19 08:50:37 2017
@@ -63,7 +63,7 @@ std::shared_ptr<std::vector<const Symbol
   return {std::move(Snap), Pointers};
 }
 
-void FileIndex::update(Context &Ctx, PathRef Path, ParsedAST *AST) {
+void FileIndex::update(const Context &Ctx, PathRef Path, ParsedAST *AST) {
   if (!AST) {
     FSymbols.update(Path, nullptr);
   } else {
@@ -74,7 +74,7 @@ void FileIndex::update(Context &Ctx, Pat
   Index.build(std::move(Symbols));
 }
 
-bool FileIndex::fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
+bool FileIndex::fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
                           std::function<void(const Symbol &)> Callback) const {
   return Index.fuzzyFind(Ctx, Req, std::move(Callback));
 }

Modified: clang-tools-extra/trunk/clangd/index/FileIndex.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/FileIndex.h?rev=321083&r1=321082&r2=321083&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/FileIndex.h (original)
+++ clang-tools-extra/trunk/clangd/index/FileIndex.h Tue Dec 19 08:50:37 2017
@@ -58,9 +58,9 @@ class FileIndex : public SymbolIndex {
 public:
   /// \brief Update symbols in \p Path with symbols in \p AST. If \p AST is
   /// nullptr, this removes all symbols in the file
-  void update(Context &Ctx, PathRef Path, ParsedAST *AST);
+  void update(const Context &Ctx, PathRef Path, ParsedAST *AST);
 
-  bool fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
+  bool fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
                  std::function<void(const Symbol &)> Callback) const override;
 
 private:

Modified: clang-tools-extra/trunk/clangd/index/Index.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/Index.h?rev=321083&r1=321082&r2=321083&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/Index.h (original)
+++ clang-tools-extra/trunk/clangd/index/Index.h Tue Dec 19 08:50:37 2017
@@ -153,7 +153,7 @@ public:
   /// Returns true if the result list is complete, false if it was truncated due
   /// to MaxCandidateCount
   virtual bool
-  fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
+  fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
             std::function<void(const Symbol &)> Callback) const = 0;
 
   // FIXME: add interfaces for more index use cases:

Modified: clang-tools-extra/trunk/clangd/index/MemIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/MemIndex.cpp?rev=321083&r1=321082&r2=321083&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/MemIndex.cpp (original)
+++ clang-tools-extra/trunk/clangd/index/MemIndex.cpp Tue Dec 19 08:50:37 2017
@@ -26,7 +26,7 @@ void MemIndex::build(std::shared_ptr<std
   }
 }
 
-bool MemIndex::fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
+bool MemIndex::fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
                          std::function<void(const Symbol &)> Callback) const {
   assert(!StringRef(Req.Query).contains("::") &&
          "There must be no :: in query.");

Modified: clang-tools-extra/trunk/clangd/index/MemIndex.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/index/MemIndex.h?rev=321083&r1=321082&r2=321083&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/index/MemIndex.h (original)
+++ clang-tools-extra/trunk/clangd/index/MemIndex.h Tue Dec 19 08:50:37 2017
@@ -24,7 +24,7 @@ public:
   /// accessible as long as `Symbols` is kept alive.
   void build(std::shared_ptr<std::vector<const Symbol *>> Symbols);
 
-  bool fuzzyFind(Context &Ctx, const FuzzyFindRequest &Req,
+  bool fuzzyFind(const Context &Ctx, const FuzzyFindRequest &Req,
                  std::function<void(const Symbol &)> Callback) const override;
 
 private:

Modified: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp?rev=321083&r1=321082&r2=321083&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp Tue Dec 19 08:50:37 2017
@@ -6,6 +6,7 @@
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
+
 #include "ClangdServer.h"
 #include "Compiler.h"
 #include "Context.h"
@@ -13,6 +14,7 @@
 #include "Protocol.h"
 #include "SourceCode.h"
 #include "TestFS.h"
+#include "index/MemIndex.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -83,6 +85,7 @@ StringWithPos parseTextMarker(StringRef
 MATCHER_P(Named, Name, "") { return arg.insertText == Name; }
 MATCHER_P(Labeled, Label, "") { return arg.label == Label; }
 MATCHER_P(Kind, K, "") { return arg.kind == K; }
+MATCHER_P(Filter, F, "") { return arg.filterText == F; }
 MATCHER_P(PlainText, Text, "") {
   return arg.insertTextFormat == clangd::InsertTextFormat::PlainText &&
          arg.insertText == Text;
@@ -457,6 +460,104 @@ TEST(SignatureHelpTest, ActiveArg) {
   EXPECT_EQ(1, Results.activeParameter);
 }
 
+std::unique_ptr<SymbolIndex> simpleIndexFromSymbols(
+    std::vector<std::pair<std::string, index::SymbolKind>> Symbols) {
+  auto I = llvm::make_unique<MemIndex>();
+  struct Snapshot {
+    SymbolSlab Slab;
+    std::vector<const Symbol *> Pointers;
+  };
+  auto Snap = std::make_shared<Snapshot>();
+  for (const auto &Pair : Symbols) {
+    Symbol Sym;
+    Sym.ID = SymbolID(Pair.first);
+    llvm::StringRef QName = Pair.first;
+    size_t Pos = QName.rfind("::");
+    if (Pos == llvm::StringRef::npos) {
+      Sym.Name = QName;
+      Sym.Scope = "";
+    } else {
+      Sym.Name = QName.substr(Pos + 2);
+      Sym.Scope = QName.substr(0, Pos);
+    }
+    Sym.SymInfo.Kind = Pair.second;
+    Snap->Slab.insert(std::move(Sym));
+  }
+  for (auto &Iter : Snap->Slab)
+    Snap->Pointers.push_back(&Iter.second);
+  auto S = std::shared_ptr<std::vector<const Symbol *>>(std::move(Snap),
+                                                        &Snap->Pointers);
+  I->build(std::move(S));
+  return I;
+}
+
+TEST(CompletionTest, NoIndex) {
+  clangd::CodeCompleteOptions Opts;
+  Opts.Index = nullptr;
+
+  auto Results = completions(R"cpp(
+      namespace ns { class No {}; }
+      void f() { ns::^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, Has("No"));
+}
+
+TEST(CompletionTest, SimpleIndexBased) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class},
+                                   {"nx::XYZ", index::SymbolKind::Class},
+                                   {"ns::foo", index::SymbolKind::Function}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      namespace ns { class No {}; }
+      void f() { ns::^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
+  EXPECT_THAT(Results.items, Has("foo", CompletionItemKind::Function));
+  EXPECT_THAT(Results.items, Not(Has("No")));
+}
+
+TEST(CompletionTest, IndexBasedWithFilter) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class},
+                                   {"ns::foo", index::SymbolKind::Function}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      void f() { ns::x^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, Contains(AllOf(Named("XYZ"), Filter("x"))));
+  EXPECT_THAT(Results.items, Not(Has("foo")));
+}
+
+TEST(CompletionTest, GlobalQualified) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"XYZ", index::SymbolKind::Class}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      void f() { ::^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
+}
+
+TEST(CompletionTest, FullyQualifiedScope) {
+  clangd::CodeCompleteOptions Opts;
+  auto I = simpleIndexFromSymbols({{"ns::XYZ", index::SymbolKind::Class}});
+  Opts.Index = I.get();
+
+  auto Results = completions(R"cpp(
+      void f() { ::ns::^ }
+  )cpp",
+                             Opts);
+  EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang

Modified: clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp?rev=321083&r1=321082&r2=321083&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp (original)
+++ clang-tools-extra/trunk/unittests/clangd/IndexTests.cpp Tue Dec 19 08:50:37 2017
@@ -143,7 +143,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWi
   I.build(generateSymbols({"a::xyz", "b::yz", "yz"}));
   FuzzyFindRequest Req;
   Req.Query = "y";
-  Req.Scopes.push_back("");
+  Req.Scopes = {""};
   auto Matches = match(I, Req);
   EXPECT_THAT(match(I, Req), UnorderedElementsAre("yz"));
 }
@@ -153,7 +153,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWi
   I.build(generateSymbols({"a::xyz", "a::yy", "a::xz", "b::yz", "yz"}));
   FuzzyFindRequest Req;
   Req.Query = "y";
-  Req.Scopes.push_back("a");
+  Req.Scopes = {"a"};
   auto Matches = match(I, Req);
   EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::xyz", "a::yy"));
 }
@@ -163,8 +163,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWi
   I.build(generateSymbols({"a::xyz", "a::yy", "a::xz", "b::yz", "yz"}));
   FuzzyFindRequest Req;
   Req.Query = "y";
-  Req.Scopes.push_back("a");
-  Req.Scopes.push_back("b");
+  Req.Scopes = {"a", "b"};
   auto Matches = match(I, Req);
   EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::xyz", "a::yy", "b::yz"));
 }
@@ -174,7 +173,7 @@ TEST(MemIndexTest, NoMatchNestedScopes)
   I.build(generateSymbols({"a::xyz", "a::b::yy"}));
   FuzzyFindRequest Req;
   Req.Query = "y";
-  Req.Scopes.push_back("a");
+  Req.Scopes = {"a"};
   auto Matches = match(I, Req);
   EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::xyz"));
 }




More information about the cfe-commits mailing list