hokein created this revision.
Herald added subscribers: mgorny, klimek.
- The "Symbol" class represents a C++ symbol in the codebase, containing all
the information of a C++ symbol needed by clangd. clangd will use it in
clangd's AST/dynamic index and global/static index (code completion and code
navigation).
- The SymbolCollector (another IndexAction) will be used to recollect the
symbols when the source file is changed (for ASTIndex), or to generate all C++
symbols for the whole project.
In the long term (when index-while-building is ready), clangd should share a
same "Symbol" structure and IndexAction with index-while-building, but
for now we want to have some stuff working in clangd.
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,110 @@
+//===-- 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::ASTFrontendAction *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 llvm::cast<ASTFrontendAction>(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->getSymbols();
+ return true;
+ }
+
+protected:
+ std::set<Symbol> 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,87 @@
+//===--- 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 <set>
+
+namespace clang {
+namespace clangd {
+
+struct SymbolLocation {
+ // The path of the source file where a symbol occurs.
+ std::string FilePath;
+ // The offset to the first character of the symbol from the beginning of the
+ // source file.
+ unsigned StartOffset;
+ // The offset to the last character of the symbol from the beginning of the
+ // source file.
+ unsigned EndOffset;
+};
+
+struct CodeCompletionInfo {
+ // FIXME: add fields here.
+};
+
+// The class presents a C++ symbol, e.g. class, function.
+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;
+ // Information for code completion.
+ CodeCompletionInfo CompletionInfo;
+
+ bool operator<(const Symbol& S) const {
+ return Identifier < S.Identifier;
+ }
+
+ // FIXME: add extra fields for index scoring signals.
+ // FIXME: add all occurrences of the symbol.
+};
+
+// 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;
+
+ const std::set<Symbol> &getSymbols() const { return Symbols; }
+
+ bool
+ handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
+ ArrayRef<index::SymbolRelation> Relations, FileID FID,
+ unsigned Offset,
+ index::IndexDataConsumer::ASTNodeInfo ASTNode) override;
+
+private:
+ std::set<Symbol> Symbols;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOL_H
Index: clangd/Symbol.cpp
===================================================================
--- /dev/null
+++ clangd/Symbol.cpp
@@ -0,0 +1,58 @@
+//===--- 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/Basic/SourceManager.h"
+#include "clang/Index/IndexSymbol.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+namespace clang {
+namespace clangd {
+
+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 ID(Buff.data(), Buff.size());
+ auto It = Symbols.find({ID});
+ if (It != Symbols.end()) {
+ return true;
+ }
+
+ auto &SM = ND->getASTContext().getSourceManager();
+ SymbolLocation Location = {SM.getFilename(D->getLocation()),
+ SM.getFileOffset(D->getLocStart()),
+ SM.getFileOffset(D->getLocEnd())};
+ Symbols.insert({std::move(ID), 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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits