summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang-tools-extra/clangd/AST.cpp15
-rw-r--r--clang-tools-extra/clangd/AST.h5
-rw-r--r--clang-tools-extra/clangd/CodeComplete.cpp9
-rw-r--r--clang-tools-extra/clangd/Protocol.h6
-rw-r--r--clang-tools-extra/clangd/index/SymbolCollector.cpp13
-rw-r--r--clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp12
6 files changed, 48 insertions, 12 deletions
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index cfcde118202..ec1862cd73d 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -38,5 +38,20 @@ SourceLocation findNameLoc(const clang::Decl* D) {
return SpellingLoc;
}
+std::string printQualifiedName(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 QName;
+}
+
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index aeeb22d2660..cb47c100b82 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_AST_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_AST_H_
+#include "clang/AST/Decl.h"
#include "clang/Basic/SourceLocation.h"
namespace clang {
@@ -28,6 +29,10 @@ namespace clangd {
/// decl occurs in the code.
SourceLocation findNameLoc(const clang::Decl *D);
+/// Returns the qualified name of ND. The scope doesn't contain unwritten scopes
+/// like inline namespaces.
+std::string printQualifiedName(const NamedDecl &ND);
+
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 31619398d17..647e02da728 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -19,6 +19,7 @@
//===---------------------------------------------------------------------===//
#include "CodeComplete.h"
+#include "AST.h"
#include "CodeCompletionStrings.h"
#include "Compiler.h"
#include "FuzzyMatch.h"
@@ -243,6 +244,8 @@ struct CompletionCandidate {
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) {
@@ -253,8 +256,14 @@ struct CompletionCandidate {
I.filterText = Text;
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(printQualifiedName(*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.
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index c3941583359..27a0c443f58 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -734,6 +734,12 @@ struct CompletionItem {
//
// 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 &);
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 08215b3f7c8..2d1e0d5bba6 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -363,20 +363,9 @@ const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND,
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::string QName = printQualifiedName(ND);
std::tie(S.Scope, S.Name) = splitQualifiedName(QName);
// FIXME: this returns foo:bar: for objective-C methods, we prefer only foo:
// for consistency with CodeCompletionString and a clean name/signature split.
diff --git a/clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp b/clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp
index 0c6003a7a35..45ea505df0f 100644
--- a/clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp
+++ b/clang-tools-extra/unittests/clangd/CodeCompleteTests.cpp
@@ -44,6 +44,7 @@ class IgnoreDiagnostics : public DiagnosticsConsumer {
// GMock helpers for matching completion items.
MATCHER_P(Named, Name, "") { return arg.insertText == Name; }
+MATCHER_P(Scope, Name, "") { return arg.SymbolScope == Name; }
MATCHER_P(Labeled, Label, "") {
std::string Indented;
if (!StringRef(Label).startswith(
@@ -1251,6 +1252,17 @@ TEST(CompletionTest, CompleteOnInvalidLine) {
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(Scope("ns::"), Scope("ns::"),
+ Scope("ns::")));
+}
} // namespace
} // namespace clangd
OpenPOWER on IntegriCloud