blob: b808e374eae252c95af726ca33ee70857eef747b [file] [log] [blame]
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +00001//===--- FindSymbols.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#include "FindSymbols.h"
10
Marc-Andre Laperle1be69702018-07-05 19:35:01 +000011#include "AST.h"
12#include "ClangdUnit.h"
Sam McCall203cdee2018-06-07 06:55:59 +000013#include "FuzzyMatch.h"
Marc-Andre Laperle1be69702018-07-05 19:35:01 +000014#include "Logger.h"
Sam McCall203cdee2018-06-07 06:55:59 +000015#include "Quality.h"
Marc-Andre Laperle1be69702018-07-05 19:35:01 +000016#include "SourceCode.h"
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000017#include "index/Index.h"
Marc-Andre Laperle1be69702018-07-05 19:35:01 +000018#include "clang/Index/IndexDataConsumer.h"
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000019#include "clang/Index/IndexSymbol.h"
Marc-Andre Laperle1be69702018-07-05 19:35:01 +000020#include "clang/Index/IndexingAction.h"
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000021#include "llvm/Support/FormatVariadic.h"
22#include "llvm/Support/Path.h"
23
Sam McCall203cdee2018-06-07 06:55:59 +000024#define DEBUG_TYPE "FindSymbols"
25
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000026namespace clang {
27namespace clangd {
28
29namespace {
30
31// Convert a index::SymbolKind to clangd::SymbolKind (LSP)
32// Note, some are not perfect matches and should be improved when this LSP
33// issue is addressed:
34// https://github.com/Microsoft/language-server-protocol/issues/344
35SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) {
36 switch (Kind) {
37 case index::SymbolKind::Unknown:
38 return SymbolKind::Variable;
39 case index::SymbolKind::Module:
40 return SymbolKind::Module;
41 case index::SymbolKind::Namespace:
42 return SymbolKind::Namespace;
43 case index::SymbolKind::NamespaceAlias:
44 return SymbolKind::Namespace;
45 case index::SymbolKind::Macro:
46 return SymbolKind::String;
47 case index::SymbolKind::Enum:
48 return SymbolKind::Enum;
49 case index::SymbolKind::Struct:
50 return SymbolKind::Struct;
51 case index::SymbolKind::Class:
52 return SymbolKind::Class;
53 case index::SymbolKind::Protocol:
54 return SymbolKind::Interface;
55 case index::SymbolKind::Extension:
56 return SymbolKind::Interface;
57 case index::SymbolKind::Union:
58 return SymbolKind::Class;
59 case index::SymbolKind::TypeAlias:
60 return SymbolKind::Class;
61 case index::SymbolKind::Function:
62 return SymbolKind::Function;
63 case index::SymbolKind::Variable:
64 return SymbolKind::Variable;
65 case index::SymbolKind::Field:
66 return SymbolKind::Field;
67 case index::SymbolKind::EnumConstant:
68 return SymbolKind::EnumMember;
69 case index::SymbolKind::InstanceMethod:
70 case index::SymbolKind::ClassMethod:
71 case index::SymbolKind::StaticMethod:
72 return SymbolKind::Method;
73 case index::SymbolKind::InstanceProperty:
74 case index::SymbolKind::ClassProperty:
75 case index::SymbolKind::StaticProperty:
76 return SymbolKind::Property;
77 case index::SymbolKind::Constructor:
78 case index::SymbolKind::Destructor:
79 return SymbolKind::Method;
80 case index::SymbolKind::ConversionFunction:
81 return SymbolKind::Function;
82 case index::SymbolKind::Parameter:
83 return SymbolKind::Variable;
84 case index::SymbolKind::Using:
85 return SymbolKind::Namespace;
86 }
87 llvm_unreachable("invalid symbol kind");
88}
89
Sam McCall203cdee2018-06-07 06:55:59 +000090using ScoredSymbolInfo = std::pair<float, SymbolInformation>;
91struct ScoredSymbolGreater {
92 bool operator()(const ScoredSymbolInfo &L, const ScoredSymbolInfo &R) {
93 if (L.first != R.first)
94 return L.first > R.first;
95 return L.second.name < R.second.name; // Earlier name is better.
96 }
97};
98
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000099} // namespace
100
101llvm::Expected<std::vector<SymbolInformation>>
Eric Liu53425f292018-06-19 09:33:53 +0000102getWorkspaceSymbols(StringRef Query, int Limit, const SymbolIndex *const Index,
103 StringRef HintPath) {
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000104 std::vector<SymbolInformation> Result;
105 if (Query.empty() || !Index)
106 return Result;
107
108 auto Names = splitQualifiedName(Query);
109
110 FuzzyFindRequest Req;
111 Req.Query = Names.second;
112
113 // FuzzyFind doesn't want leading :: qualifier
114 bool IsGlobalQuery = Names.first.consume_front("::");
115 // Restrict results to the scope in the query string if present (global or
116 // not).
117 if (IsGlobalQuery || !Names.first.empty())
118 Req.Scopes = {Names.first};
119 if (Limit)
120 Req.MaxCandidateCount = Limit;
Sam McCall203cdee2018-06-07 06:55:59 +0000121 TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top(Req.MaxCandidateCount);
122 FuzzyMatcher Filter(Req.Query);
Eric Liu53425f292018-06-19 09:33:53 +0000123 Index->fuzzyFind(Req, [HintPath, &Top, &Filter](const Symbol &Sym) {
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000124 // Prefer the definition over e.g. a function declaration in a header
125 auto &CD = Sym.Definition ? Sym.Definition : Sym.CanonicalDeclaration;
126 auto Uri = URI::parse(CD.FileURI);
127 if (!Uri) {
Sam McCallbed58852018-07-11 10:35:11 +0000128 log("Workspace symbol: Could not parse URI '{0}' for symbol '{1}'.",
129 CD.FileURI, Sym.Name);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000130 return;
131 }
Eric Liu53425f292018-06-19 09:33:53 +0000132 auto Path = URI::resolve(*Uri, HintPath);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000133 if (!Path) {
Sam McCallbed58852018-07-11 10:35:11 +0000134 log("Workspace symbol: Could not resolve path for URI '{0}' for symbol "
135 "'{1}'.",
136 Uri->toString(), Sym.Name);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000137 return;
138 }
139 Location L;
140 L.uri = URIForFile((*Path));
141 Position Start, End;
142 Start.line = CD.Start.Line;
143 Start.character = CD.Start.Column;
144 End.line = CD.End.Line;
145 End.character = CD.End.Column;
146 L.range = {Start, End};
147 SymbolKind SK = indexSymbolKindToSymbolKind(Sym.SymInfo.Kind);
148 std::string Scope = Sym.Scope;
149 StringRef ScopeRef = Scope;
150 ScopeRef.consume_back("::");
Sam McCall203cdee2018-06-07 06:55:59 +0000151 SymbolInformation Info = {Sym.Name, SK, L, ScopeRef};
152
153 SymbolQualitySignals Quality;
154 Quality.merge(Sym);
155 SymbolRelevanceSignals Relevance;
156 Relevance.Query = SymbolRelevanceSignals::Generic;
157 if (auto NameMatch = Filter.match(Sym.Name))
158 Relevance.NameMatch = *NameMatch;
159 else {
Sam McCallbed58852018-07-11 10:35:11 +0000160 log("Workspace symbol: {0} didn't match query {1}", Sym.Name,
161 Filter.pattern());
Sam McCall203cdee2018-06-07 06:55:59 +0000162 return;
163 }
164 Relevance.merge(Sym);
165 auto Score =
166 evaluateSymbolAndRelevance(Quality.evaluate(), Relevance.evaluate());
Sam McCallbed58852018-07-11 10:35:11 +0000167 dlog("FindSymbols: {0}{1} = {2}\n{3}{4}\n", Sym.Scope, Sym.Name, Score,
168 Quality, Relevance);
Sam McCall203cdee2018-06-07 06:55:59 +0000169
170 Top.push({Score, std::move(Info)});
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000171 });
Sam McCall203cdee2018-06-07 06:55:59 +0000172 for (auto &R : std::move(Top).items())
173 Result.push_back(std::move(R.second));
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000174 return Result;
175}
176
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000177namespace {
178/// Finds document symbols in the main file of the AST.
179class DocumentSymbolsConsumer : public index::IndexDataConsumer {
180 ASTContext &AST;
181 std::vector<SymbolInformation> Symbols;
182 // We are always list document for the same file, so cache the value.
183 llvm::Optional<URIForFile> MainFileUri;
184
185public:
186 DocumentSymbolsConsumer(ASTContext &AST) : AST(AST) {}
187 std::vector<SymbolInformation> takeSymbols() { return std::move(Symbols); }
188
189 void initialize(ASTContext &Ctx) override {
190 // Compute the absolute path of the main file which we will use for all
191 // results.
192 const SourceManager &SM = AST.getSourceManager();
193 const FileEntry *F = SM.getFileEntryForID(SM.getMainFileID());
194 if (!F)
195 return;
Simon Marchi25f1f732018-08-10 22:27:53 +0000196 auto FilePath = getRealPath(F, SM);
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000197 if (FilePath)
198 MainFileUri = URIForFile(*FilePath);
199 }
200
201 bool shouldIncludeSymbol(const NamedDecl *ND) {
202 if (!ND || ND->isImplicit())
203 return false;
204 // Skip anonymous declarations, e.g (anonymous enum/class/struct).
205 if (ND->getDeclName().isEmpty())
206 return false;
207 return true;
208 }
209
210 bool
211 handleDeclOccurence(const Decl *, index::SymbolRoleSet Roles,
212 ArrayRef<index::SymbolRelation> Relations,
213 SourceLocation Loc,
214 index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
215 assert(ASTNode.OrigD);
216 // No point in continuing the index consumer if we could not get the
217 // absolute path of the main file.
218 if (!MainFileUri)
219 return false;
220 // We only want declarations and definitions, i.e. no references.
221 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
222 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
223 return true;
224 SourceLocation NameLoc = findNameLoc(ASTNode.OrigD);
225 const SourceManager &SourceMgr = AST.getSourceManager();
226 // We should be only be looking at "local" decls in the main file.
227 if (!SourceMgr.isWrittenInMainFile(NameLoc)) {
228 // Even thought we are visiting only local (non-preamble) decls,
Fangrui Song445bdd12018-09-05 08:01:37 +0000229 // we can get here when in the presence of "extern" decls.
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000230 return true;
231 }
232 const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(ASTNode.OrigD);
233 if (!shouldIncludeSymbol(ND))
234 return true;
235
236 SourceLocation EndLoc =
237 Lexer::getLocForEndOfToken(NameLoc, 0, SourceMgr, AST.getLangOpts());
238 Position Begin = sourceLocToPosition(SourceMgr, NameLoc);
239 Position End = sourceLocToPosition(SourceMgr, EndLoc);
240 Range R = {Begin, End};
241 Location L;
242 L.uri = *MainFileUri;
243 L.range = R;
244
245 std::string QName = printQualifiedName(*ND);
246 StringRef Scope, Name;
247 std::tie(Scope, Name) = splitQualifiedName(QName);
248 Scope.consume_back("::");
249
250 index::SymbolInfo SymInfo = index::getSymbolInfo(ND);
251 SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
252
253 SymbolInformation SI;
254 SI.name = Name;
255 SI.kind = SK;
256 SI.location = L;
257 SI.containerName = Scope;
258 Symbols.push_back(std::move(SI));
259 return true;
260 }
261};
262} // namespace
263
264llvm::Expected<std::vector<SymbolInformation>>
265getDocumentSymbols(ParsedAST &AST) {
266 DocumentSymbolsConsumer DocumentSymbolsCons(AST.getASTContext());
267
268 index::IndexingOptions IndexOpts;
269 IndexOpts.SystemSymbolFilter =
270 index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly;
271 IndexOpts.IndexFunctionLocals = false;
272 indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(),
273 DocumentSymbolsCons, IndexOpts);
274
275 return DocumentSymbolsCons.takeSymbols();
276}
277
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000278} // namespace clangd
279} // namespace clang