clang-tools  7.0.0
FindSymbols.cpp
Go to the documentation of this file.
1 //===--- 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 
11 #include "AST.h"
12 #include "ClangdUnit.h"
13 #include "FuzzyMatch.h"
14 #include "Logger.h"
15 #include "Quality.h"
16 #include "SourceCode.h"
17 #include "index/Index.h"
18 #include "clang/Index/IndexDataConsumer.h"
19 #include "clang/Index/IndexSymbol.h"
20 #include "clang/Index/IndexingAction.h"
21 #include "llvm/Support/FormatVariadic.h"
22 #include "llvm/Support/Path.h"
23 
24 #define DEBUG_TYPE "FindSymbols"
25 
26 namespace clang {
27 namespace clangd {
28 
29 namespace {
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
35 SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) {
36  switch (Kind) {
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:
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 
90 using ScoredSymbolInfo = std::pair<float, SymbolInformation>;
91 struct 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 
99 } // namespace
100 
101 llvm::Expected<std::vector<SymbolInformation>>
102 getWorkspaceSymbols(StringRef Query, int Limit, const SymbolIndex *const Index,
103  StringRef HintPath) {
104  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;
121  TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top(Req.MaxCandidateCount);
122  FuzzyMatcher Filter(Req.Query);
123  Index->fuzzyFind(Req, [HintPath, &Top, &Filter](const Symbol &Sym) {
124  // 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) {
128  log("Workspace symbol: Could not parse URI '{0}' for symbol '{1}'.",
129  CD.FileURI, Sym.Name);
130  return;
131  }
132  auto Path = URI::resolve(*Uri, HintPath);
133  if (!Path) {
134  log("Workspace symbol: Could not resolve path for URI '{0}' for symbol "
135  "'{1}'.",
136  Uri->toString(), Sym.Name);
137  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("::");
151  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 {
160  log("Workspace symbol: {0} didn't match query {1}", Sym.Name,
161  Filter.pattern());
162  return;
163  }
164  Relevance.merge(Sym);
165  auto Score =
166  evaluateSymbolAndRelevance(Quality.evaluate(), Relevance.evaluate());
167  dlog("FindSymbols: {0}{1} = {2}\n{3}{4}\n", Sym.Scope, Sym.Name, Score,
168  Quality, Relevance);
169 
170  Top.push({Score, std::move(Info)});
171  });
172  for (auto &R : std::move(Top).items())
173  Result.push_back(std::move(R.second));
174  return Result;
175 }
176 
177 namespace {
178 /// Finds document symbols in the main file of the AST.
179 class 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 
185 public:
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;
196  auto FilePath = getAbsoluteFilePath(F, SM);
197  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,
229  // we can get here when in the presense of "extern" decls.
230  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 
264 llvm::Expected<std::vector<SymbolInformation>>
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 
278 } // namespace clangd
279 } // namespace clang
SourceLocation Loc
&#39;#&#39; location in the include directive
llvm::StringRef Name
std::string printQualifiedName(const NamedDecl &ND)
Returns the qualified name of ND.
Definition: AST.cpp:41
llvm::Expected< std::vector< SymbolInformation > > getDocumentSymbols(ParsedAST &AST)
Retrieves the symbols contained in the "main file" section of an AST in the same order that they appe...
clang::find_all_symbols::SymbolInfo::SymbolKind SymbolKind
Definition: SymbolInfo.cpp:22
Interface for symbol indexes that can be used for searching or matching symbols among a set of symbol...
Definition: Index.h:317
SourceLocation findNameLoc(const clang::Decl *D)
Find the identifier source location of the given D.
Definition: AST.cpp:20
llvm::Expected< std::vector< SymbolInformation > > getWorkspaceSymbols(StringRef Query, int Limit, const SymbolIndex *const Index, StringRef HintPath)
ASTContext & getASTContext()
Note that the returned ast will not contain decls from the preamble that were not deserialized during...
Definition: ClangdUnit.cpp:190
std::vector< std::string > Scopes
If this is non-empty, symbols must be in at least one of the scopes (e.g.
Definition: Index.h:300
BindArgumentKind Kind
ArrayRef< Decl * > getLocalTopLevelDecls()
This function returns top-level decls present in the main file of the AST.
Definition: ClangdUnit.cpp:206
clang::find_all_symbols::SymbolInfo SymbolInfo
void log(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:62
std::string Path
A typedef to represent a file path.
Definition: Path.h:21
std::string Query
A query string for the fuzzy find.
Definition: Index.h:293
#define dlog(...)
Definition: Logger.h:72
SymbolKind
The SymbolInfo Type.
Definition: SymbolInfo.h:31
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
Definition: SourceCode.cpp:130
Stores and provides access to parsed AST.
Definition: ClangdUnit.h:66
float evaluateSymbolAndRelevance(float SymbolQuality, float SymbolRelevance)
Combine symbol quality and relevance into a single score.
Definition: Quality.cpp:367
FunctionInfo Info
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
std::pair< llvm::StringRef, llvm::StringRef > splitQualifiedName(llvm::StringRef QName)
From "a::b::c", return {"a::b::", "c"}.
Definition: SourceCode.cpp:166
llvm::Optional< std::string > getAbsoluteFilePath(const FileEntry *F, const SourceManager &SourceMgr)
Get the absolute file path of a given file entry.
Definition: SourceCode.cpp:189
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
Resolves the absolute path of U.
Definition: URI.cpp:191
tooling::ExecutionContext * Ctx
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
Definition: URI.cpp:156