blob: c53522a40f207ea4333cfc29cf3838a7ec34677e [file] [log] [blame]
Haojian Wu4c1394d2017-12-12 15:42:10 +00001//===--- SymbolCollector.cpp -------------------------------------*- C++-*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "SymbolCollector.h"
Haojian Wu4c1394d2017-12-12 15:42:10 +000011#include "clang/AST/ASTContext.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/DeclCXX.h"
14#include "clang/Basic/SourceManager.h"
15#include "clang/Index/IndexSymbol.h"
16#include "clang/Index/USRGeneration.h"
17#include "llvm/Support/MemoryBuffer.h"
18#include "llvm/Support/Path.h"
19
20namespace clang {
21namespace clangd {
22
23namespace {
24// Make the Path absolute using the current working directory of the given
25// SourceManager if the Path is not an absolute path.
26//
27// The Path can be a path relative to the build directory, or retrieved from
28// the SourceManager.
29std::string makeAbsolutePath(const SourceManager &SM, StringRef Path) {
30 llvm::SmallString<128> AbsolutePath(Path);
31 if (std::error_code EC =
32 SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
33 AbsolutePath))
34 llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
35 << '\n';
36 // Handle the symbolic link path case where the current working directory
37 // (getCurrentWorkingDirectory) is a symlink./ We always want to the real
38 // file path (instead of the symlink path) for the C++ symbols.
39 //
40 // Consider the following example:
41 //
42 // src dir: /project/src/foo.h
43 // current working directory (symlink): /tmp/build -> /project/src/
44 //
45 // The file path of Symbol is "/project/src/foo.h" instead of
46 // "/tmp/build/foo.h"
47 const DirectoryEntry *Dir = SM.getFileManager().getDirectory(
48 llvm::sys::path::parent_path(AbsolutePath.str()));
49 if (Dir) {
50 StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
51 SmallVector<char, 128> AbsoluteFilename;
52 llvm::sys::path::append(AbsoluteFilename, DirName,
53 llvm::sys::path::filename(AbsolutePath.str()));
54 return llvm::StringRef(AbsoluteFilename.data(), AbsoluteFilename.size())
55 .str();
56 }
57 return AbsolutePath.str();
58}
Eric Liu4feda802017-12-19 11:37:40 +000059
60// Split a qualified symbol name into scope and unqualified name, e.g. given
61// "a::b::c", return {"a::b", "c"}. Scope is empty if it doesn't exist.
62std::pair<llvm::StringRef, llvm::StringRef>
63splitQualifiedName(llvm::StringRef QName) {
64 assert(!QName.startswith("::") && "Qualified names should not start with ::");
65 size_t Pos = QName.rfind("::");
66 if (Pos == llvm::StringRef::npos)
67 return {StringRef(), QName};
68 return {QName.substr(0, Pos), QName.substr(Pos + 2)};
69}
70
Haojian Wu4c1394d2017-12-12 15:42:10 +000071} // namespace
72
73// Always return true to continue indexing.
74bool SymbolCollector::handleDeclOccurence(
75 const Decl *D, index::SymbolRoleSet Roles,
76 ArrayRef<index::SymbolRelation> Relations, FileID FID, unsigned Offset,
77 index::IndexDataConsumer::ASTNodeInfo ASTNode) {
78 // FIXME: collect all symbol references.
79 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
80 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
81 return true;
82
83 if (const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D)) {
84 // FIXME: Should we include the internal linkage symbols?
85 if (!ND->hasExternalFormalLinkage() || ND->isInAnonymousNamespace())
86 return true;
87
88 llvm::SmallVector<char, 128> Buff;
89 if (index::generateUSRForDecl(ND, Buff))
90 return true;
91
92 std::string USR(Buff.data(), Buff.size());
93 auto ID = SymbolID(USR);
94 if (Symbols.find(ID) != Symbols.end())
95 return true;
96
97 auto &SM = ND->getASTContext().getSourceManager();
Benjamin Kramer6452efd2017-12-21 17:51:35 +000098 std::string FilePath =
99 makeAbsolutePath(SM, SM.getFilename(D->getLocation()));
100 SymbolLocation Location = {FilePath, SM.getFileOffset(D->getLocStart()),
101 SM.getFileOffset(D->getLocEnd())};
Eric Liu4feda802017-12-19 11:37:40 +0000102 std::string QName = ND->getQualifiedNameAsString();
103 auto ScopeAndName = splitQualifiedName(QName);
Sam McCall98a73182017-12-22 08:12:39 +0000104
105 Symbol S;
106 S.ID = std::move(ID);
107 S.Scope = ScopeAndName.first;
108 S.Name = ScopeAndName.second;
109 S.SymInfo = index::getSymbolInfo(D);
110 S.CanonicalDeclaration = Location;
111 Symbols.insert(S);
Haojian Wu4c1394d2017-12-12 15:42:10 +0000112 }
113
114 return true;
115}
116
Ilya Biryukov5a85b8e2017-12-13 12:53:16 +0000117void SymbolCollector::finish() { Symbols.freeze(); }
Haojian Wu4c1394d2017-12-12 15:42:10 +0000118
119} // namespace clangd
120} // namespace clang