ioeric created this revision.
ioeric added reviewers: sammccall, hokein.
Herald added subscribers: cfe-commits, jkorous-apple, ilya-biryukov, klimek.

Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D42796

Files:
  clangd/index/SymbolCollector.cpp
  unittests/clangd/SymbolCollectorTests.cpp

Index: unittests/clangd/SymbolCollectorTests.cpp
===================================================================
--- unittests/clangd/SymbolCollectorTests.cpp
+++ unittests/clangd/SymbolCollectorTests.cpp
@@ -201,8 +201,7 @@
   runSymbolCollector(Header, /*Main=*/"");
   EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Red"), QName("Color"),
                                             QName("Green"), QName("Color2"),
-                                            QName("ns"),
-                                            QName("ns::Black")));
+                                            QName("ns"), QName("ns::Black")));
 }
 
 TEST_F(SymbolCollectorTest, IgnoreNamelessSymbols) {
@@ -324,6 +323,41 @@
   EXPECT_THAT(Symbols, UnorderedElementsAre(QName("Foo")));
 }
 
+TEST_F(SymbolCollectorTest, Scopes) {
+  const std::string Header = R"(
+    namespace na {
+    class Foo {};
+    namespace nb {
+    class Bar {};
+    }
+    }
+  )";
+  runSymbolCollector(Header, /*Main=*/"");
+  EXPECT_THAT(Symbols,
+              UnorderedElementsAre(QName("na"), QName("na::nb"),
+                                   QName("na::Foo"), QName("na::nb::Bar")));
+}
+
+TEST_F(SymbolCollectorTest, SkipInlineNamespace) {
+  const std::string Header = R"(
+    namespace na {
+    inline namespace nb {
+    class Foo {};
+    }
+    }
+    namespace na {
+    // This is still inlined.
+    namespace nb {
+    class Bar {};
+    }
+    }
+  )";
+  runSymbolCollector(Header, /*Main=*/"");
+  EXPECT_THAT(Symbols,
+              UnorderedElementsAre(QName("na"), QName("na::nb"),
+                                   QName("na::Foo"), QName("na::Bar")));
+}
+
 TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
   const std::string Header = R"(
     namespace nx {
Index: clangd/index/SymbolCollector.cpp
===================================================================
--- clangd/index/SymbolCollector.cpp
+++ clangd/index/SymbolCollector.cpp
@@ -9,6 +9,7 @@
 
 #include "SymbolCollector.h"
 #include "../CodeCompletionStrings.h"
+#include "Logger.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Basic/SourceManager.h"
@@ -63,14 +64,33 @@
   return AbsolutePath.str();
 }
 
-// "a::b::c", return {"a::b::", "c"}. Scope is empty if there's no qualifier.
-std::pair<llvm::StringRef, llvm::StringRef>
-splitQualifiedName(llvm::StringRef QName) {
-  assert(!QName.startswith("::") && "Qualified names should not start with ::");
-  size_t Pos = QName.rfind("::");
-  if (Pos == llvm::StringRef::npos)
-    return {StringRef(), QName};
-  return {QName.substr(0, Pos + 2), QName.substr(Pos + 2)};
+// For a symbol "a::b::c", return "a::b::". Scope is empty if there's no
+// qualifier. Inline namespaces and unscoped enums are skipped.
+llvm::Expected<std::string> getScope(const NamedDecl *ND) {
+  llvm::SmallVector<llvm::StringRef, 4> Contexts;
+  for (const auto *Context = ND->getDeclContext(); Context;
+       Context = Context->getParent()) {
+    if (llvm::isa<TranslationUnitDecl>(Context) ||
+        llvm::isa<LinkageSpecDecl>(Context))
+      break;
+
+    if (const auto *NSD = dyn_cast<NamespaceDecl>(Context)) {
+      if (!NSD->isInlineNamespace())
+        Contexts.push_back(NSD->getName());
+    } else if (const auto *ED = dyn_cast<EnumDecl>(Context)) {
+      if (ED->isScoped())
+        Contexts.push_back(ED->getName());
+    } else {
+      return llvm::make_error<llvm::StringError>(
+          llvm::Twine("Unexpected context type ") + Context->getDeclKindName() +
+              " for symbol " + ND->getQualifiedNameAsString(),
+          llvm::inconvertibleErrorCode());
+    }
+  }
+  std::string Scope = llvm::join(Contexts.rbegin(), Contexts.rend(), "::");
+  if (!Scope.empty())
+    Scope.append("::");
+  return Scope;
 }
 
 bool shouldFilterDecl(const NamedDecl *ND, ASTContext *ASTCtx,
@@ -172,19 +192,27 @@
     if (shouldFilterDecl(ND, ASTCtx, Opts))
       return true;
     llvm::SmallString<128> USR;
+    if (ND->getIdentifier() == nullptr)
+      return true;
     if (index::generateUSRForDecl(ND, USR))
       return true;
 
     auto ID = SymbolID(USR);
     if (Symbols.find(ID) != nullptr)
       return true;
 
     auto &SM = ND->getASTContext().getSourceManager();
-    std::string QName = ND->getQualifiedNameAsString();
+    auto Scope = getScope(ND);
+    if (!Scope) {
+      log(llvm::toString(Scope.takeError()));
+      return true;
+    }
+    std::string Name = ND->getName();
 
     Symbol S;
     S.ID = std::move(ID);
-    std::tie(S.Scope, S.Name) = splitQualifiedName(QName);
+    S.Scope = *Scope;
+    S.Name = Name;
     S.SymInfo = index::getSymbolInfo(D);
     std::string FilePath;
     S.CanonicalDeclaration = GetSymbolLocation(ND, SM, Opts.FallbackDir, FilePath);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to