blob: 21c5e2bafa500d52cdcb62689f750370b3dfdf85 [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"
Eric Liu9af958f2018-01-10 14:57:58 +000013#include "clang/ASTMatchers/ASTMatchFinder.h"
Haojian Wu4c1394d2017-12-12 15:42:10 +000014#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);
Benjamin Kramer50a967d2017-12-28 14:47:01 +000051 SmallString<128> AbsoluteFilename;
Haojian Wu4c1394d2017-12-12 15:42:10 +000052 llvm::sys::path::append(AbsoluteFilename, DirName,
53 llvm::sys::path::filename(AbsolutePath.str()));
Benjamin Kramer50a967d2017-12-28 14:47:01 +000054 return AbsoluteFilename.str();
Haojian Wu4c1394d2017-12-12 15:42:10 +000055 }
56 return AbsolutePath.str();
57}
Eric Liu4feda802017-12-19 11:37:40 +000058
Eric Liu4feda802017-12-19 11:37:40 +000059// "a::b::c", return {"a::b", "c"}. Scope is empty if it doesn't exist.
60std::pair<llvm::StringRef, llvm::StringRef>
61splitQualifiedName(llvm::StringRef QName) {
62 assert(!QName.startswith("::") && "Qualified names should not start with ::");
63 size_t Pos = QName.rfind("::");
64 if (Pos == llvm::StringRef::npos)
65 return {StringRef(), QName};
66 return {QName.substr(0, Pos), QName.substr(Pos + 2)};
67}
68
Eric Liu9af958f2018-01-10 14:57:58 +000069bool shouldFilterDecl(const NamedDecl *ND, ASTContext *ASTCtx,
70 const SymbolCollector::Options &Opts) {
71 using namespace clang::ast_matchers;
72 if (ND->isImplicit())
73 return true;
74 // FIXME: figure out a way to handle internal linkage symbols (e.g. static
75 // variables, function) defined in the .cc files. Also we skip the symbols
76 // in anonymous namespace as the qualifier names of these symbols are like
77 // `foo::<anonymous>::bar`, which need a special handling.
78 // In real world projects, we have a relatively large set of header files
79 // that define static variables (like "static const int A = 1;"), we still
80 // want to collect these symbols, although they cause potential ODR
81 // violations.
82 if (ND->isInAnonymousNamespace())
83 return true;
84
85 // We only want symbols in namespaces or translation unit scopes (e.g. no
86 // class members).
87 if (match(decl(allOf(
88 Opts.IndexMainFiles ? decl()
89 : decl(unless(isExpansionInMainFile())),
90 hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl())))),
91 *ND, *ASTCtx)
92 .empty())
93 return true;
94
95 return false;
96}
97
Haojian Wu4c1394d2017-12-12 15:42:10 +000098} // namespace
99
Eric Liu9af958f2018-01-10 14:57:58 +0000100SymbolCollector::SymbolCollector(Options Opts) : Opts(std::move(Opts)) {}
101
Eric Liu76f6b442018-01-09 17:32:00 +0000102void SymbolCollector::initialize(ASTContext &Ctx) {
103 ASTCtx = &Ctx;
104 CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
105 CompletionTUInfo =
106 llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
107}
108
Haojian Wu4c1394d2017-12-12 15:42:10 +0000109// Always return true to continue indexing.
110bool SymbolCollector::handleDeclOccurence(
111 const Decl *D, index::SymbolRoleSet Roles,
112 ArrayRef<index::SymbolRelation> Relations, FileID FID, unsigned Offset,
113 index::IndexDataConsumer::ASTNodeInfo ASTNode) {
Eric Liu9af958f2018-01-10 14:57:58 +0000114 assert(ASTCtx && PP.get() && "ASTContext and Preprocessor must be set.");
115
Haojian Wu4c1394d2017-12-12 15:42:10 +0000116 // FIXME: collect all symbol references.
117 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
118 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
119 return true;
120
Eric Liu76f6b442018-01-09 17:32:00 +0000121 assert(CompletionAllocator && CompletionTUInfo);
122
Haojian Wu4c1394d2017-12-12 15:42:10 +0000123 if (const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D)) {
Eric Liu9af958f2018-01-10 14:57:58 +0000124 if (shouldFilterDecl(ND, ASTCtx, Opts))
Haojian Wu4c1394d2017-12-12 15:42:10 +0000125 return true;
Benjamin Kramer50a967d2017-12-28 14:47:01 +0000126 llvm::SmallString<128> USR;
127 if (index::generateUSRForDecl(ND, USR))
Haojian Wu4c1394d2017-12-12 15:42:10 +0000128 return true;
129
Haojian Wu4c1394d2017-12-12 15:42:10 +0000130 auto ID = SymbolID(USR);
Sam McCall4b9bbb32017-12-23 19:38:03 +0000131 if (Symbols.find(ID) != nullptr)
Haojian Wu4c1394d2017-12-12 15:42:10 +0000132 return true;
133
134 auto &SM = ND->getASTContext().getSourceManager();
Benjamin Kramer6452efd2017-12-21 17:51:35 +0000135 std::string FilePath =
136 makeAbsolutePath(SM, SM.getFilename(D->getLocation()));
137 SymbolLocation Location = {FilePath, SM.getFileOffset(D->getLocStart()),
138 SM.getFileOffset(D->getLocEnd())};
Eric Liu4feda802017-12-19 11:37:40 +0000139 std::string QName = ND->getQualifiedNameAsString();
140 auto ScopeAndName = splitQualifiedName(QName);
Sam McCall98a73182017-12-22 08:12:39 +0000141
142 Symbol S;
143 S.ID = std::move(ID);
144 S.Scope = ScopeAndName.first;
145 S.Name = ScopeAndName.second;
146 S.SymInfo = index::getSymbolInfo(D);
147 S.CanonicalDeclaration = Location;
Eric Liu76f6b442018-01-09 17:32:00 +0000148
149 // Add completion info.
150 assert(ASTCtx && PP.get() && "ASTContext and Preprocessor must be set.");
151 CodeCompletionResult SymbolCompletion(ND, 0);
152 const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
153 *ASTCtx, *PP, CodeCompletionContext::CCC_Name, *CompletionAllocator,
154 *CompletionTUInfo,
155 /*IncludeBriefComments*/ true);
156 std::string Label;
157 std::string SnippetInsertText;
158 std::string IgnoredLabel;
159 std::string PlainInsertText;
160 getLabelAndInsertText(*CCS, &Label, &SnippetInsertText,
161 /*EnableSnippets=*/true);
162 getLabelAndInsertText(*CCS, &IgnoredLabel, &PlainInsertText,
163 /*EnableSnippets=*/false);
164 std::string FilterText = getFilterText(*CCS);
165 std::string Documentation = getDocumentation(*CCS);
166 std::string CompletionDetail = getDetail(*CCS);
167
168 S.CompletionFilterText = FilterText;
169 S.CompletionLabel = Label;
170 S.CompletionPlainInsertText = PlainInsertText;
171 S.CompletionSnippetInsertText = SnippetInsertText;
172 Symbol::Details Detail;
173 Detail.Documentation = Documentation;
174 Detail.CompletionDetail = CompletionDetail;
175 S.Detail = &Detail;
176
Sam McCall98a73182017-12-22 08:12:39 +0000177 Symbols.insert(S);
Haojian Wu4c1394d2017-12-12 15:42:10 +0000178 }
179
180 return true;
181}
182
Haojian Wu4c1394d2017-12-12 15:42:10 +0000183} // namespace clangd
184} // namespace clang