ioeric updated this revision to Diff 152331.
ioeric marked 2 inline comments as done.
ioeric added a comment.

- store scope in completion item. Add splitQualifiedName for NamedDecl.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D48425

Files:
  clangd/CodeComplete.cpp
  clangd/Protocol.h
  clangd/SourceCode.cpp
  clangd/SourceCode.h
  clangd/index/SymbolCollector.cpp
  unittests/clangd/CodeCompleteTests.cpp
  unittests/clangd/SourceCodeTests.cpp

Index: unittests/clangd/SourceCodeTests.cpp
===================================================================
--- unittests/clangd/SourceCodeTests.cpp
+++ unittests/clangd/SourceCodeTests.cpp
@@ -7,11 +7,13 @@
 //
 //===----------------------------------------------------------------------===//
 #include "SourceCode.h"
+#include "TestTU.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/raw_os_ostream.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include <utility>
 
 namespace clang{
 namespace clangd {
@@ -119,6 +121,24 @@
   EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds";
 }
 
+std::pair<std::string, std::string> makePair(StringRef x, StringRef y) {
+  return std::make_pair<std::string, std::string>(x, y);
+}
+
+TEST(SplitQualifiedName, SplitQName) {
+  auto TU = TestTU::withCode("namespace nx { class X; }");
+  auto AST = TU.build();
+  EXPECT_EQ(makePair("", "nx"), splitQualifiedName(findDecl(AST, "nx")));
+  EXPECT_EQ(makePair("nx::", "X"), splitQualifiedName(findDecl(AST, "nx::X")));
+}
+
+TEST(SplitQualifiedName, NoInlineNamespace) {
+  auto TU =
+      TestTU::withCode("namespace nx { inline namespace ny { class X; } }");
+  auto AST = TU.build();
+  EXPECT_EQ(makePair("nx::", "X"), splitQualifiedName(findAnyDecl(AST, "X")));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: unittests/clangd/CodeCompleteTests.cpp
===================================================================
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -44,6 +44,9 @@
 
 // GMock helpers for matching completion items.
 MATCHER_P(Named, Name, "") { return arg.insertText == Name; }
+MATCHER_P(QName, Name, "") {
+  return (arg.SymbolScope + arg.filterText) == Name;
+}
 MATCHER_P(Labeled, Label, "") {
   std::string Indented;
   if (!StringRef(Label).startswith(
@@ -1251,6 +1254,18 @@
       Failed());
 }
 
+TEST(CompletionTest, QualifiedNames) {
+  auto Results = completions(
+      R"cpp(
+          namespace ns { int local; void both(); }
+          void f() { ::ns::^ }
+      )cpp",
+      {func("ns::both"), cls("ns::Index")});
+  // We get results from both index and sema, with no duplicates.
+  EXPECT_THAT(Results.items,
+              UnorderedElementsAre(QName("ns::local"), QName("ns::Index"),
+                                   QName("ns::both")));
+}
 
 } // namespace
 } // namespace clangd
Index: clangd/index/SymbolCollector.cpp
===================================================================
--- clangd/index/SymbolCollector.cpp
+++ clangd/index/SymbolCollector.cpp
@@ -363,21 +363,10 @@
   auto &Ctx = ND.getASTContext();
   auto &SM = Ctx.getSourceManager();
 
-  std::string QName;
-  llvm::raw_string_ostream OS(QName);
-  PrintingPolicy Policy(ASTCtx->getLangOpts());
-  // Note that inline namespaces are treated as transparent scopes. This
-  // reflects the way they're most commonly used for lookup. Ideally we'd
-  // include them, but at query time it's hard to find all the inline
-  // namespaces to query: the preamble doesn't have a dedicated list.
-  Policy.SuppressUnwrittenScope = true;
-  ND.printQualifiedName(OS, Policy);
-  OS.flush();
-  assert(!StringRef(QName).startswith("::"));
-
   Symbol S;
   S.ID = std::move(ID);
-  std::tie(S.Scope, S.Name) = splitQualifiedName(QName);
+  auto ScopeAndName = splitQualifiedName(ND);
+  std::tie(S.Scope, S.Name) = ScopeAndName;
 
   S.IsIndexedForCodeCompletion = isIndexedForCodeCompletion(ND, Ctx);
   S.SymInfo = index::getSymbolInfo(&ND);
@@ -413,8 +402,9 @@
   if (Opts.CollectIncludePath && shouldCollectIncludePath(S.SymInfo.Kind)) {
     // Use the expansion location to get the #include header since this is
     // where the symbol is exposed.
-    if (auto Header = getIncludeHeader(
-            QName, SM, SM.getExpansionLoc(ND.getLocation()), Opts))
+    if (auto Header =
+            getIncludeHeader(ScopeAndName.first + ScopeAndName.second, SM,
+                             SM.getExpansionLoc(ND.getLocation()), Opts))
       Include = std::move(*Header);
   }
   S.CompletionFilterText = FilterText;
Index: clangd/SourceCode.h
===================================================================
--- clangd/SourceCode.h
+++ clangd/SourceCode.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H
 #include "Protocol.h"
+#include "clang/AST/Decl.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Tooling/Core/Replacement.h"
 
@@ -56,6 +57,10 @@
 std::pair<llvm::StringRef, llvm::StringRef>
 splitQualifiedName(llvm::StringRef QName);
 
+/// Splits the qualified name of ND. The scope doesn't contain unwritten scopes
+/// like inline namespaces.
+std::pair<std::string, std::string> splitQualifiedName(const NamedDecl &ND);
+
 TextEdit replacementToEdit(StringRef Code, const tooling::Replacement &R);
 
 std::vector<TextEdit> replacementsToEdits(StringRef Code,
Index: clangd/SourceCode.cpp
===================================================================
--- clangd/SourceCode.cpp
+++ clangd/SourceCode.cpp
@@ -8,6 +8,9 @@
 //===----------------------------------------------------------------------===//
 #include "SourceCode.h"
 
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/PrettyPrinter.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
@@ -166,6 +169,23 @@
   return {QName.substr(0, Pos + 2), QName.substr(Pos + 2)};
 }
 
+std::pair<std::string, std::string>
+splitQualifiedName(const NamedDecl &ND) {
+  std::string QName;
+  llvm::raw_string_ostream OS(QName);
+  PrintingPolicy Policy(ND.getASTContext().getLangOpts());
+  // Note that inline namespaces are treated as transparent scopes. This
+  // reflects the way they're most commonly used for lookup. Ideally we'd
+  // include them, but at query time it's hard to find all the inline
+  // namespaces to query: the preamble doesn't have a dedicated list.
+  Policy.SuppressUnwrittenScope = true;
+  ND.printQualifiedName(OS, Policy);
+  OS.flush();
+  assert(!StringRef(QName).startswith("::"));
+
+  return splitQualifiedName(QName);
+}
+
 TextEdit replacementToEdit(StringRef Code, const tooling::Replacement &R) {
   Range ReplacementRange = {
       offsetToPosition(Code, R.getOffset()),
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -734,6 +734,12 @@
   //
   // data?: any - A data entry field that is preserved on a completion item
   //              between a completion and a completion resolve request.
+
+  // C++ extension that is only expected to be used by users of ClangdServer's
+  // C++ API. Not serialized from/to json.
+  /// The containing scope (e.g. namespace) of the symbol this item corresponds
+  /// to, e.g. "" (global scope), "ns::" (top-level namespace).
+  std::string SymbolScope;
 };
 json::Expr toJSON(const CompletionItem &);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CompletionItem &);
Index: clangd/CodeComplete.cpp
===================================================================
--- clangd/CodeComplete.cpp
+++ clangd/CodeComplete.cpp
@@ -243,6 +243,8 @@
                        const IncludeInserter &Includes,
                        llvm::StringRef SemaDocComment) const {
     assert(bool(SemaResult) == bool(SemaCCS));
+    assert(SemaResult || IndexResult);
+
     CompletionItem I;
     bool InsertingInclude = false; // Whether a new #include will be added.
     if (SemaResult) {
@@ -252,8 +254,14 @@
       I.filterText = getFilterText(*SemaCCS);
       I.documentation = formatDocumentation(*SemaCCS, SemaDocComment);
       I.detail = getDetail(*SemaCCS);
+      if (SemaResult->Kind == CodeCompletionResult::RK_Declaration)
+        if (const auto *D = SemaResult->getDeclaration())
+          if (const auto *ND = llvm::dyn_cast<NamedDecl>(D))
+            I.SymbolScope = splitQualifiedName(*ND).first;
     }
     if (IndexResult) {
+      if (I.SymbolScope.empty())
+        I.SymbolScope = IndexResult->Scope;
       if (I.kind == CompletionItemKind::Missing)
         I.kind = toCompletionItemKind(IndexResult->SymInfo.Kind);
       // FIXME: reintroduce a way to show the index source for debugging.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to