blob: f6d3cf5a02e62791e6d3d83a2c3b1bcac5355efe [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};
119 if (Limit)
Kirill Bobyreve6dd0802018-09-13 14:27:03 +0000120 Req.Limit = Limit;
121 TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top(
122 Req.Limit ? *Req.Limit : std::numeric_limits<size_t>::max());
Sam McCall203cdee2018-06-07 06:55:59 +0000123 FuzzyMatcher Filter(Req.Query);
Eric Liu53425f292018-06-19 09:33:53 +0000124 Index->fuzzyFind(Req, [HintPath, &Top, &Filter](const Symbol &Sym) {
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000125 // Prefer the definition over e.g. a function declaration in a header
126 auto &CD = Sym.Definition ? Sym.Definition : Sym.CanonicalDeclaration;
127 auto Uri = URI::parse(CD.FileURI);
128 if (!Uri) {
Sam McCallbed58852018-07-11 10:35:11 +0000129 log("Workspace symbol: Could not parse URI '{0}' for symbol '{1}'.",
130 CD.FileURI, Sym.Name);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000131 return;
132 }
Eric Liu53425f292018-06-19 09:33:53 +0000133 auto Path = URI::resolve(*Uri, HintPath);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000134 if (!Path) {
Sam McCallbed58852018-07-11 10:35:11 +0000135 log("Workspace symbol: Could not resolve path for URI '{0}' for symbol "
136 "'{1}'.",
137 Uri->toString(), Sym.Name);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000138 return;
139 }
140 Location L;
141 L.uri = URIForFile((*Path));
142 Position Start, End;
Haojian Wub515fab2018-10-18 10:43:50 +0000143 Start.line = CD.Start.line();
144 Start.character = CD.Start.column();
145 End.line = CD.End.line();
146 End.character = CD.End.column();
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000147 L.range = {Start, End};
148 SymbolKind SK = indexSymbolKindToSymbolKind(Sym.SymInfo.Kind);
149 std::string Scope = Sym.Scope;
150 StringRef ScopeRef = Scope;
151 ScopeRef.consume_back("::");
Sam McCall203cdee2018-06-07 06:55:59 +0000152 SymbolInformation Info = {Sym.Name, SK, L, ScopeRef};
153
154 SymbolQualitySignals Quality;
155 Quality.merge(Sym);
156 SymbolRelevanceSignals Relevance;
157 Relevance.Query = SymbolRelevanceSignals::Generic;
158 if (auto NameMatch = Filter.match(Sym.Name))
159 Relevance.NameMatch = *NameMatch;
160 else {
Sam McCallbed58852018-07-11 10:35:11 +0000161 log("Workspace symbol: {0} didn't match query {1}", Sym.Name,
162 Filter.pattern());
Sam McCall203cdee2018-06-07 06:55:59 +0000163 return;
164 }
165 Relevance.merge(Sym);
166 auto Score =
167 evaluateSymbolAndRelevance(Quality.evaluate(), Relevance.evaluate());
Sam McCallbed58852018-07-11 10:35:11 +0000168 dlog("FindSymbols: {0}{1} = {2}\n{3}{4}\n", Sym.Scope, Sym.Name, Score,
169 Quality, Relevance);
Sam McCall203cdee2018-06-07 06:55:59 +0000170
171 Top.push({Score, std::move(Info)});
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000172 });
Sam McCall203cdee2018-06-07 06:55:59 +0000173 for (auto &R : std::move(Top).items())
174 Result.push_back(std::move(R.second));
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000175 return Result;
176}
177
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000178namespace {
179/// Finds document symbols in the main file of the AST.
180class DocumentSymbolsConsumer : public index::IndexDataConsumer {
181 ASTContext &AST;
182 std::vector<SymbolInformation> Symbols;
183 // We are always list document for the same file, so cache the value.
Sam McCallc008af62018-10-20 15:30:37 +0000184 Optional<URIForFile> MainFileUri;
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000185
186public:
187 DocumentSymbolsConsumer(ASTContext &AST) : AST(AST) {}
188 std::vector<SymbolInformation> takeSymbols() { return std::move(Symbols); }
189
190 void initialize(ASTContext &Ctx) override {
191 // Compute the absolute path of the main file which we will use for all
192 // results.
193 const SourceManager &SM = AST.getSourceManager();
194 const FileEntry *F = SM.getFileEntryForID(SM.getMainFileID());
195 if (!F)
196 return;
Simon Marchi25f1f732018-08-10 22:27:53 +0000197 auto FilePath = getRealPath(F, SM);
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000198 if (FilePath)
199 MainFileUri = URIForFile(*FilePath);
200 }
201
202 bool shouldIncludeSymbol(const NamedDecl *ND) {
203 if (!ND || ND->isImplicit())
204 return false;
205 // Skip anonymous declarations, e.g (anonymous enum/class/struct).
206 if (ND->getDeclName().isEmpty())
207 return false;
208 return true;
209 }
210
211 bool
212 handleDeclOccurence(const Decl *, index::SymbolRoleSet Roles,
213 ArrayRef<index::SymbolRelation> Relations,
214 SourceLocation Loc,
215 index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
216 assert(ASTNode.OrigD);
217 // No point in continuing the index consumer if we could not get the
218 // absolute path of the main file.
219 if (!MainFileUri)
220 return false;
221 // We only want declarations and definitions, i.e. no references.
222 if (!(Roles & static_cast<unsigned>(index::SymbolRole::Declaration) ||
223 Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
224 return true;
225 SourceLocation NameLoc = findNameLoc(ASTNode.OrigD);
226 const SourceManager &SourceMgr = AST.getSourceManager();
227 // We should be only be looking at "local" decls in the main file.
228 if (!SourceMgr.isWrittenInMainFile(NameLoc)) {
229 // Even thought we are visiting only local (non-preamble) decls,
Fangrui Song445bdd12018-09-05 08:01:37 +0000230 // we can get here when in the presence of "extern" decls.
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000231 return true;
232 }
Sam McCallc008af62018-10-20 15:30:37 +0000233 const NamedDecl *ND = dyn_cast<NamedDecl>(ASTNode.OrigD);
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000234 if (!shouldIncludeSymbol(ND))
235 return true;
236
237 SourceLocation EndLoc =
238 Lexer::getLocForEndOfToken(NameLoc, 0, SourceMgr, AST.getLangOpts());
239 Position Begin = sourceLocToPosition(SourceMgr, NameLoc);
240 Position End = sourceLocToPosition(SourceMgr, EndLoc);
241 Range R = {Begin, End};
242 Location L;
243 L.uri = *MainFileUri;
244 L.range = R;
245
246 std::string QName = printQualifiedName(*ND);
247 StringRef Scope, Name;
248 std::tie(Scope, Name) = splitQualifiedName(QName);
249 Scope.consume_back("::");
250
251 index::SymbolInfo SymInfo = index::getSymbolInfo(ND);
252 SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
253
254 SymbolInformation SI;
255 SI.name = Name;
256 SI.kind = SK;
257 SI.location = L;
258 SI.containerName = Scope;
259 Symbols.push_back(std::move(SI));
260 return true;
261 }
262};
263} // namespace
264
Sam McCallc008af62018-10-20 15:30:37 +0000265Expected<std::vector<SymbolInformation>> getDocumentSymbols(ParsedAST &AST) {
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000266 DocumentSymbolsConsumer DocumentSymbolsCons(AST.getASTContext());
267
268 index::IndexingOptions IndexOpts;
269 IndexOpts.SystemSymbolFilter =
270 index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly;
271 IndexOpts.IndexFunctionLocals = false;
Eric Liuf7367662018-09-18 08:52:14 +0000272 indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(),
273 AST.getLocalTopLevelDecls(), DocumentSymbolsCons,
274 IndexOpts);
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000275
276 return DocumentSymbolsCons.takeSymbols();
277}
278
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000279} // namespace clangd
280} // namespace clang