blob: 64a0896a70cf28e7ccb9c627efc58610d099582d [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
Sam McCallc008af62018-10-20 15:30:37 +000026using namespace llvm;
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000027namespace clang {
28namespace clangd {
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000029namespace {
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
Sam McCallc008af62018-10-20 15:30:37 +0000101Expected<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};
Eric Liub04869a2018-11-06 11:08:17 +0000119 else
120 Req.AnyScope = true;
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000121 if (Limit)
Kirill Bobyreve6dd0802018-09-13 14:27:03 +0000122 Req.Limit = Limit;
123 TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top(
124 Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max());
Sam McCall203cdee2018-06-07 06:55:59 +0000125 FuzzyMatcher Filter(Req.Query);
Eric Liu53425f292018-06-19 09:33:53 +0000126 Index->fuzzyFind(Req, [HintPath, &Top, &Filter](const Symbol &Sym) {
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000127 // Prefer the definition over e.g. a function declaration in a header
128 auto &CD = Sym.Definition ? Sym.Definition : Sym.CanonicalDeclaration;
129 auto Uri = URI::parse(CD.FileURI);
130 if (!Uri) {
Sam McCallbed58852018-07-11 10:35:11 +0000131 log("Workspace symbol: Could not parse URI '{0}' for symbol '{1}'.",
132 CD.FileURI, Sym.Name);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000133 return;
134 }
Eric Liu53425f292018-06-19 09:33:53 +0000135 auto Path = URI::resolve(*Uri, HintPath);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000136 if (!Path) {
Sam McCallbed58852018-07-11 10:35:11 +0000137 log("Workspace symbol: Could not resolve path for URI '{0}' for symbol "
138 "'{1}'.",
139 Uri->toString(), Sym.Name);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000140 return;
141 }
142 Location L;
143 L.uri = URIForFile((*Path));
144 Position Start, End;
Haojian Wub515fab2018-10-18 10:43:50 +0000145 Start.line = CD.Start.line();
146 Start.character = CD.Start.column();
147 End.line = CD.End.line();
148 End.character = CD.End.column();
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000149 L.range = {Start, End};
150 SymbolKind SK = indexSymbolKindToSymbolKind(Sym.SymInfo.Kind);
151 std::string Scope = Sym.Scope;
152 StringRef ScopeRef = Scope;
153 ScopeRef.consume_back("::");
Sam McCall203cdee2018-06-07 06:55:59 +0000154 SymbolInformation Info = {Sym.Name, SK, L, ScopeRef};
155
156 SymbolQualitySignals Quality;
157 Quality.merge(Sym);
158 SymbolRelevanceSignals Relevance;
159 Relevance.Query = SymbolRelevanceSignals::Generic;
160 if (auto NameMatch = Filter.match(Sym.Name))
161 Relevance.NameMatch = *NameMatch;
162 else {
Sam McCallbed58852018-07-11 10:35:11 +0000163 log("Workspace symbol: {0} didn't match query {1}", Sym.Name,
164 Filter.pattern());
Sam McCall203cdee2018-06-07 06:55:59 +0000165 return;
166 }
167 Relevance.merge(Sym);
168 auto Score =
169 evaluateSymbolAndRelevance(Quality.evaluate(), Relevance.evaluate());
Sam McCallbed58852018-07-11 10:35:11 +0000170 dlog("FindSymbols: {0}{1} = {2}\n{3}{4}\n", Sym.Scope, Sym.Name, Score,
171 Quality, Relevance);
Sam McCall203cdee2018-06-07 06:55:59 +0000172
173 Top.push({Score, std::move(Info)});
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000174 });
Sam McCall203cdee2018-06-07 06:55:59 +0000175 for (auto &R : std::move(Top).items())
176 Result.push_back(std::move(R.second));
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000177 return Result;
178}
179
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000180namespace {
181/// Finds document symbols in the main file of the AST.
182class DocumentSymbolsConsumer : public index::IndexDataConsumer {
183 ASTContext &AST;
184 std::vector<SymbolInformation> Symbols;
185 // We are always list document for the same file, so cache the value.
Sam McCallc008af62018-10-20 15:30:37 +0000186 Optional<URIForFile> MainFileUri;
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000187
188public:
189 DocumentSymbolsConsumer(ASTContext &AST) : AST(AST) {}
190 std::vector<SymbolInformation> takeSymbols() { return std::move(Symbols); }
191
192 void initialize(ASTContext &Ctx) override {
193 // Compute the absolute path of the main file which we will use for all
194 // results.
195 const SourceManager &SM = AST.getSourceManager();
196 const FileEntry *F = SM.getFileEntryForID(SM.getMainFileID());
197 if (!F)
198 return;
Simon Marchi25f1f732018-08-10 22:27:53 +0000199 auto FilePath = getRealPath(F, SM);
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000200 if (FilePath)
201 MainFileUri = URIForFile(*FilePath);
202 }
203
204 bool shouldIncludeSymbol(const NamedDecl *ND) {
205 if (!ND || ND->isImplicit())
206 return false;
207 // Skip anonymous declarations, e.g (anonymous enum/class/struct).
208 if (ND->getDeclName().isEmpty())
209 return false;
210 return true;
211 }
212
213 bool
214 handleDeclOccurence(const Decl *, index::SymbolRoleSet Roles,
215 ArrayRef<index::SymbolRelation> Relations,
216 SourceLocation Loc,
217 index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
218 assert(ASTNode.OrigD);
219 // No point in continuing the index consumer if we could not get the
220 // absolute path of the main file.
221 if (!MainFileUri)
222 return false;
223 // We only want declarations and definitions, i.e. no references.
224 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
225 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
226 return true;
227 SourceLocation NameLoc = findNameLoc(ASTNode.OrigD);
228 const SourceManager &SourceMgr = AST.getSourceManager();
229 // We should be only be looking at "local" decls in the main file.
230 if (!SourceMgr.isWrittenInMainFile(NameLoc)) {
231 // Even thought we are visiting only local (non-preamble) decls,
Fangrui Song445bdd12018-09-05 08:01:37 +0000232 // we can get here when in the presence of "extern" decls.
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000233 return true;
234 }
Sam McCallc008af62018-10-20 15:30:37 +0000235 const NamedDecl *ND = dyn_cast<NamedDecl>(ASTNode.OrigD);
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000236 if (!shouldIncludeSymbol(ND))
237 return true;
238
239 SourceLocation EndLoc =
240 Lexer::getLocForEndOfToken(NameLoc, 0, SourceMgr, AST.getLangOpts());
241 Position Begin = sourceLocToPosition(SourceMgr, NameLoc);
242 Position End = sourceLocToPosition(SourceMgr, EndLoc);
243 Range R = {Begin, End};
244 Location L;
245 L.uri = *MainFileUri;
246 L.range = R;
247
248 std::string QName = printQualifiedName(*ND);
249 StringRef Scope, Name;
250 std::tie(Scope, Name) = splitQualifiedName(QName);
251 Scope.consume_back("::");
252
253 index::SymbolInfo SymInfo = index::getSymbolInfo(ND);
254 SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
255
256 SymbolInformation SI;
257 SI.name = Name;
258 SI.kind = SK;
259 SI.location = L;
260 SI.containerName = Scope;
261 Symbols.push_back(std::move(SI));
262 return true;
263 }
264};
265} // namespace
266
Sam McCallc008af62018-10-20 15:30:37 +0000267Expected<std::vector<SymbolInformation>> getDocumentSymbols(ParsedAST &AST) {
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000268 DocumentSymbolsConsumer DocumentSymbolsCons(AST.getASTContext());
269
270 index::IndexingOptions IndexOpts;
271 IndexOpts.SystemSymbolFilter =
272 index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly;
273 IndexOpts.IndexFunctionLocals = false;
Eric Liuf7367662018-09-18 08:52:14 +0000274 indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(),
275 AST.getLocalTopLevelDecls(), DocumentSymbolsCons,
276 IndexOpts);
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000277
278 return DocumentSymbolsCons.takeSymbols();
279}
280
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000281} // namespace clangd
282} // namespace clang