blob: 3b9046e74efea717faed1f7821dd006c13f0b70b [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"
Eric Liu76f6b442018-01-09 17:32:00 +000011#include "../CodeCompletionStrings.h"
Haojian Wu4c1394d2017-12-12 15:42:10 +000012#include "clang/AST/DeclCXX.h"
13#include "clang/Basic/SourceManager.h"
14#include "clang/Index/IndexSymbol.h"
15#include "clang/Index/USRGeneration.h"
16#include "llvm/Support/MemoryBuffer.h"
17#include "llvm/Support/Path.h"
18
19namespace clang {
20namespace clangd {
21
22namespace {
23// Make the Path absolute using the current working directory of the given
24// SourceManager if the Path is not an absolute path.
25//
26// The Path can be a path relative to the build directory, or retrieved from
27// the SourceManager.
28std::string makeAbsolutePath(const SourceManager &SM, StringRef Path) {
29 llvm::SmallString<128> AbsolutePath(Path);
30 if (std::error_code EC =
31 SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
32 AbsolutePath))
33 llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
34 << '\n';
35 // Handle the symbolic link path case where the current working directory
36 // (getCurrentWorkingDirectory) is a symlink./ We always want to the real
37 // file path (instead of the symlink path) for the C++ symbols.
38 //
39 // Consider the following example:
40 //
41 // src dir: /project/src/foo.h
42 // current working directory (symlink): /tmp/build -> /project/src/
43 //
44 // The file path of Symbol is "/project/src/foo.h" instead of
45 // "/tmp/build/foo.h"
46 const DirectoryEntry *Dir = SM.getFileManager().getDirectory(
47 llvm::sys::path::parent_path(AbsolutePath.str()));
48 if (Dir) {
49 StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
Benjamin Kramer50a967d2017-12-28 14:47:01 +000050 SmallString<128> AbsoluteFilename;
Haojian Wu4c1394d2017-12-12 15:42:10 +000051 llvm::sys::path::append(AbsoluteFilename, DirName,
52 llvm::sys::path::filename(AbsolutePath.str()));
Benjamin Kramer50a967d2017-12-28 14:47:01 +000053 return AbsoluteFilename.str();
Haojian Wu4c1394d2017-12-12 15:42:10 +000054 }
55 return AbsolutePath.str();
56}
Eric Liu4feda802017-12-19 11:37:40 +000057
Eric Liu4feda802017-12-19 11:37:40 +000058// "a::b::c", return {"a::b", "c"}. Scope is empty if it doesn't exist.
59std::pair<llvm::StringRef, llvm::StringRef>
60splitQualifiedName(llvm::StringRef QName) {
61 assert(!QName.startswith("::") && "Qualified names should not start with ::");
62 size_t Pos = QName.rfind("::");
63 if (Pos == llvm::StringRef::npos)
64 return {StringRef(), QName};
65 return {QName.substr(0, Pos), QName.substr(Pos + 2)};
66}
67
Haojian Wu4c1394d2017-12-12 15:42:10 +000068} // namespace
69
Eric Liu76f6b442018-01-09 17:32:00 +000070void SymbolCollector::initialize(ASTContext &Ctx) {
71 ASTCtx = &Ctx;
72 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
73 CompletionTUInfo =
74 llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
75}
76
Haojian Wu4c1394d2017-12-12 15:42:10 +000077// Always return true to continue indexing.
78bool SymbolCollector::handleDeclOccurence(
79 const Decl *D, index::SymbolRoleSet Roles,
80 ArrayRef<index::SymbolRelation> Relations, FileID FID, unsigned Offset,
81 index::IndexDataConsumer::ASTNodeInfo ASTNode) {
82 // FIXME: collect all symbol references.
83 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
84 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
85 return true;
86
Eric Liu76f6b442018-01-09 17:32:00 +000087 assert(CompletionAllocator && CompletionTUInfo);
88
Haojian Wu4c1394d2017-12-12 15:42:10 +000089 if (const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D)) {
Haojian Wufe22b742018-01-09 10:44:09 +000090 // FIXME: figure out a way to handle internal linkage symbols (e.g. static
91 // variables, function) defined in the .cc files. Also we skip the symbols
92 // in anonymous namespace as the qualifier names of these symbols are like
93 // `foo::<anonymous>::bar`, which need a special handling.
94 // In real world projects, we have a relatively large set of header files
95 // that define static variables (like "static const int A = 1;"), we still
96 // want to collect these symbols, although they cause potential ODR
97 // violations.
98 if (ND->isInAnonymousNamespace())
Haojian Wu4c1394d2017-12-12 15:42:10 +000099 return true;
100
Benjamin Kramer50a967d2017-12-28 14:47:01 +0000101 llvm::SmallString<128> USR;
102 if (index::generateUSRForDecl(ND, USR))
Haojian Wu4c1394d2017-12-12 15:42:10 +0000103 return true;
104
Haojian Wu4c1394d2017-12-12 15:42:10 +0000105 auto ID = SymbolID(USR);
Sam McCall4b9bbb32017-12-23 19:38:03 +0000106 if (Symbols.find(ID) != nullptr)
Haojian Wu4c1394d2017-12-12 15:42:10 +0000107 return true;
108
109 auto &SM = ND->getASTContext().getSourceManager();
Benjamin Kramer6452efd2017-12-21 17:51:35 +0000110 std::string FilePath =
111 makeAbsolutePath(SM, SM.getFilename(D->getLocation()));
112 SymbolLocation Location = {FilePath, SM.getFileOffset(D->getLocStart()),
113 SM.getFileOffset(D->getLocEnd())};
Eric Liu4feda802017-12-19 11:37:40 +0000114 std::string QName = ND->getQualifiedNameAsString();
115 auto ScopeAndName = splitQualifiedName(QName);
Sam McCall98a73182017-12-22 08:12:39 +0000116
117 Symbol S;
118 S.ID = std::move(ID);
119 S.Scope = ScopeAndName.first;
120 S.Name = ScopeAndName.second;
121 S.SymInfo = index::getSymbolInfo(D);
122 S.CanonicalDeclaration = Location;
Eric Liu76f6b442018-01-09 17:32:00 +0000123
124 // Add completion info.
125 assert(ASTCtx && PP.get() && "ASTContext and Preprocessor must be set.");
126 CodeCompletionResult SymbolCompletion(ND, 0);
127 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
128 *ASTCtx, *PP, CodeCompletionContext::CCC_Name, *CompletionAllocator,
129 *CompletionTUInfo,
130 /*IncludeBriefComments*/ true);
131 std::string Label;
132 std::string SnippetInsertText;
133 std::string IgnoredLabel;
134 std::string PlainInsertText;
135 getLabelAndInsertText(*CCS, &Label, &SnippetInsertText,
136 /*EnableSnippets=*/true);
137 getLabelAndInsertText(*CCS, &IgnoredLabel, &PlainInsertText,
138 /*EnableSnippets=*/false);
139 std::string FilterText = getFilterText(*CCS);
140 std::string Documentation = getDocumentation(*CCS);
141 std::string CompletionDetail = getDetail(*CCS);
142
143 S.CompletionFilterText = FilterText;
144 S.CompletionLabel = Label;
145 S.CompletionPlainInsertText = PlainInsertText;
146 S.CompletionSnippetInsertText = SnippetInsertText;
147 Symbol::Details Detail;
148 Detail.Documentation = Documentation;
149 Detail.CompletionDetail = CompletionDetail;
150 S.Detail = &Detail;
151
Sam McCall98a73182017-12-22 08:12:39 +0000152 Symbols.insert(S);
Haojian Wu4c1394d2017-12-12 15:42:10 +0000153 }
154
155 return true;
156}
157
Haojian Wu4c1394d2017-12-12 15:42:10 +0000158} // namespace clangd
159} // namespace clang