hokein updated this revision to Diff 126115.
hokein marked 4 inline comments as done.
hokein added a comment.

Address review comments.

- Use SymbolSlab, for allowing future space optimization.
- Fix a Cast issue when building debug unittest.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D40897

Files:
  clangd/CMakeLists.txt
  clangd/Symbol.cpp
  clangd/Symbol.h
  unittests/clangd/CMakeLists.txt
  unittests/clangd/SymbolCollectorTests.cpp

Index: unittests/clangd/SymbolCollectorTests.cpp
===================================================================
--- /dev/null
+++ unittests/clangd/SymbolCollectorTests.cpp
@@ -0,0 +1,112 @@
+//===-- SymbolCollectorTests.cpp  -------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Symbol.h"
+#include "clang/Index/IndexingAction.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include <memory>
+#include <string>
+
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Field;
+
+namespace clang {
+namespace clangd {
+
+namespace {
+class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
+ public:
+  SymbolIndexActionFactory() = default;
+
+  clang::FrontendAction *create() override {
+    index::IndexingOptions IndexOpts;
+    IndexOpts.SystemSymbolFilter =
+        index::IndexingOptions::SystemSymbolFilterKind::All;
+    IndexOpts.IndexFunctionLocals = false;
+    Collector = std::make_shared<SymbolCollector>();
+    FrontendAction *Action =
+        index::createIndexingAction(Collector, IndexOpts, nullptr).release();
+    return Action;
+  }
+
+  std::shared_ptr<SymbolCollector> Collector;
+};
+
+class SymbolCollectorTest : public ::testing::Test {
+public:
+  bool runSymbolCollector(StringRef HeaderCode, StringRef MainCode) {
+    llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
+        new vfs::InMemoryFileSystem);
+    llvm::IntrusiveRefCntPtr<FileManager> Files(
+        new FileManager(FileSystemOptions(), InMemoryFileSystem));
+
+    const std::string FileName = "symbol.cc";
+    const std::string HeaderName = "symbols.h";
+    auto Factory = llvm::make_unique<SymbolIndexActionFactory>();
+
+    tooling::ToolInvocation Invocation(
+        {"symbol_collector", "-fsyntax-only", "-std=c++11", FileName},
+        Factory->create(), Files.get(),
+        std::make_shared<PCHContainerOperations>());
+
+    InMemoryFileSystem->addFile(HeaderName, 0,
+                                llvm::MemoryBuffer::getMemBuffer(HeaderCode));
+
+    std::string Content = "#include\"" + std::string(HeaderName) + "\"";
+    Content += "\n" + MainCode.str();
+    InMemoryFileSystem->addFile(FileName, 0,
+                                llvm::MemoryBuffer::getMemBuffer(Content));
+    Invocation.run();
+    Symbols = Factory->Collector->takeSymbols();
+
+    EXPECT_EQ(FileName, Factory->Collector->getFilename());
+    return true;
+  }
+
+protected:
+  SymbolSlab Symbols;
+};
+
+TEST_F(SymbolCollectorTest, CollectSymbol) {
+  const std::string Header = R"(
+    class Foo {
+      void f();
+    };
+    void f1();
+    inline void f2() {}
+  )";
+  const std::string Main = R"(
+    namespace {
+    void ff() {} // ignore
+    }
+    void f1() {}
+  )";
+  runSymbolCollector(Header, Main);
+  EXPECT_THAT(Symbols,
+              UnorderedElementsAre(Field(&Symbol::QualifiedName, Eq("Foo")),
+                                   Field(&Symbol::QualifiedName, Eq("Foo::f")),
+                                   Field(&Symbol::QualifiedName, Eq("f1")),
+                                   Field(&Symbol::QualifiedName, Eq("f2"))));
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: unittests/clangd/CMakeLists.txt
===================================================================
--- unittests/clangd/CMakeLists.txt
+++ unittests/clangd/CMakeLists.txt
@@ -15,6 +15,7 @@
   JSONExprTests.cpp
   TestFS.cpp
   TraceTests.cpp
+  SymbolCollectorTests.cpp
   )
 
 target_link_libraries(ClangdTests
Index: clangd/Symbol.h
===================================================================
--- /dev/null
+++ clangd/Symbol.h
@@ -0,0 +1,133 @@
+//===--- Symbol.h -----------------------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOL_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOL_H
+
+#include "clang/Index/IndexDataConsumer.h"
+#include "clang/Index/IndexSymbol.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringExtras.h"
+
+#include <set>
+
+namespace clang Identifier{
+namespace clangd {
+
+struct SymbolLocation {
+  // The path of the source file where a symbol occurs.
+  //
+  // The path could be absolute or relative to the build working directory,
+  // depends on the build system. It is usually relative for project-related
+  // header files, and absolute for ".cc" files.
+  std::string FilePath;
+  // The 0-based offset to the first character of the symbol from the beginning
+  // of the source file.
+  unsigned StartOffset;
+  // The 0-based offset to the last character of the symbol from the beginning
+  // of the source file.
+  unsigned EndOffset;
+};
+
+// The class presents a C++ symbol, e.g. class, function.
+//
+// FIXME: instead of having own copy fields for each symbol, we can share
+// storage from SymbolSlab.
+struct Symbol {
+  // The symbol identifier, using USR.
+  std::string Identifier;
+  // The qualified name of the symbol, e.g. Foo::bar.
+  std::string QualifiedName;
+  // The symbol information, like symbol kind.
+  index::SymbolInfo SymInfo;
+  // The canonical location of the symbol.
+  //
+  // A C++ symbol might have multiple locations (e.g. a function is declared in
+  // ".h" file, and is defined in ".cc" file).
+  //   * For non-inline functions and class methods, the canonical location is
+  //     where they are declared.
+  //   * For classes, the canonical location is where they are defined.
+  SymbolLocation CanonicalLocation;
+
+  // FIXME: add all occurrences support.
+  // FIXME: add extra fields for index scoring signals.
+  // FIXME: add code completion information.
+};
+
+// A symbol container that stores a set of symbols. The container will maintain
+// the lifetime of the symbols.
+//
+// FIXME: Use a space-efficient implementation, a lot of Symbol fields could
+// share the same storage.
+class SymbolSlab {
+ public:
+  using Iterator = llvm::DenseSet<Symbol>::const_iterator;
+
+  void addSymbol(Symbol S);
+  bool hasSymbol(StringRef Identifier) const;
+
+  Iterator begin() const;
+  Iterator end() const;
+
+ private:
+  llvm::DenseSet<Symbol> Symbols;
+};
+
+// Collect all symbols from an AST.
+//
+// Clients (e.g. clangd) can use SymbolCollector together with
+// index::indexTopLevelDecls to retrieve all symbols when the source file is
+// changed.
+class SymbolCollector : public index::IndexDataConsumer {
+public:
+  SymbolCollector() = default;
+
+  void initialize(ASTContext &Ctx) override;
+
+  // Always return true to continue indexing.
+  bool
+  handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
+                      ArrayRef<index::SymbolRelation> Relations, FileID FID,
+                      unsigned Offset,
+                      index::IndexDataConsumer::ASTNodeInfo ASTNode) override;
+
+  StringRef getFilename() const {
+    return Filename;
+  }
+
+  SymbolSlab takeSymbols() const { return std::move(Symbols); }
+
+private:
+  // The file path where the AST comes from.
+  std::string Filename;
+
+  // All Symbols collected from the AST.
+  SymbolSlab Symbols;
+};
+
+} // namespace clangd
+} // namespace clang
+
+namespace llvm {
+template <> struct DenseMapInfo<clang::clangd::Symbol> {
+  static inline clang::clangd::Symbol getEmptyKey() { return {"EMPTYKEY"}; }
+  static inline clang::clangd::Symbol getTombstoneKey() {
+    return {"TOMBSTONEKEY"};
+  }
+  static unsigned getHashValue(const clang::clangd::Symbol &Sym) {
+    return llvm::HashString(Sym.Identifier);
+  }
+  static bool isEqual(const clang::clangd::Symbol &LHS,
+                      const clang::clangd::Symbol &RHS) {
+    return LHS.Identifier == RHS.Identifier;
+  }
+};
+} // namespace llvm
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOL_H
Index: clangd/Symbol.cpp
===================================================================
--- /dev/null
+++ clangd/Symbol.cpp
@@ -0,0 +1,80 @@
+//===--- Symbol.cpp ---------------------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#include "Symbol.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Index/IndexSymbol.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+namespace clang {
+namespace clangd {
+
+SymbolSlab::Iterator SymbolSlab::begin() const {
+  return Symbols.begin();
+}
+
+SymbolSlab::Iterator SymbolSlab::end() const {
+  return Symbols.end();
+}
+
+void SymbolSlab::addSymbol(Symbol S) {
+  Symbols.insert(std::move(S));
+}
+
+bool SymbolSlab::hasSymbol(StringRef Identifier) const {
+  auto It = Symbols.find({Identifier});
+  return It != Symbols.end();
+}
+
+void SymbolCollector::initialize(ASTContext &Ctx) {
+  auto FID = Ctx.getSourceManager().getMainFileID();
+  const auto *Entry = Ctx.getSourceManager().getFileEntryForID(FID);
+  Filename = Entry->tryGetRealPathName();
+}
+
+bool SymbolCollector::handleDeclOccurence(
+    const Decl *D, index::SymbolRoleSet Roles,
+    ArrayRef<index::SymbolRelation> Relations, FileID FID, unsigned Offset,
+    index::IndexDataConsumer::ASTNodeInfo ASTNode) {
+  // FIXME: collect all symbol references.
+  if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
+        Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
+    return true;
+
+  if (const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D)) {
+    // FIXME: Should we include the internal linkage symbols?
+    if (!ND->hasExternalFormalLinkage() || ND->isInAnonymousNamespace())
+      return true;
+
+    llvm::SmallVector<char, 128> Buff;
+    if (index::generateUSRForDecl(ND, Buff))
+      return true;
+
+    std::string USR(Buff.data(), Buff.size());
+    if (Symbols.hasSymbol(USR))
+      return true;
+
+    auto &SM = ND->getASTContext().getSourceManager();
+    SymbolLocation Location = {SM.getFilename(D->getLocation()),
+                               SM.getFileOffset(D->getLocStart()),
+                               SM.getFileOffset(D->getLocEnd())};
+    Symbols.addSymbol({std::move(USR), ND->getQualifiedNameAsString(),
+                       index::getSymbolInfo(D), std::move(Location)});
+  }
+
+  return true;
+}
+
+}  // clangd
+}  // clang
Index: clangd/CMakeLists.txt
===================================================================
--- clangd/CMakeLists.txt
+++ clangd/CMakeLists.txt
@@ -17,6 +17,7 @@
   Logger.cpp
   Protocol.cpp
   ProtocolHandlers.cpp
+  Symbol.cpp
   Trace.cpp
 
   LINK_LIBS
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to