clang-tools 22.0.0git
XRefs.cpp
Go to the documentation of this file.
1//===--- XRefs.cpp -----------------------------------------------*- C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8#include "XRefs.h"
9#include "AST.h"
10#include "FindSymbols.h"
11#include "FindTarget.h"
12#include "Headers.h"
13#include "IncludeCleaner.h"
14#include "ParsedAST.h"
15#include "Protocol.h"
16#include "Quality.h"
17#include "Selection.h"
18#include "SourceCode.h"
19#include "URI.h"
20#include "clang-include-cleaner/Analysis.h"
21#include "clang-include-cleaner/Types.h"
22#include "index/Index.h"
23#include "index/Merge.h"
24#include "index/Ref.h"
25#include "index/Relation.h"
27#include "index/SymbolID.h"
29#include "support/Logger.h"
30#include "clang/AST/ASTContext.h"
31#include "clang/AST/ASTTypeTraits.h"
32#include "clang/AST/Attr.h"
33#include "clang/AST/Attrs.inc"
34#include "clang/AST/Decl.h"
35#include "clang/AST/DeclCXX.h"
36#include "clang/AST/DeclObjC.h"
37#include "clang/AST/DeclTemplate.h"
38#include "clang/AST/DeclVisitor.h"
39#include "clang/AST/ExprCXX.h"
40#include "clang/AST/RecursiveASTVisitor.h"
41#include "clang/AST/Stmt.h"
42#include "clang/AST/StmtCXX.h"
43#include "clang/AST/StmtVisitor.h"
44#include "clang/AST/Type.h"
45#include "clang/Basic/LLVM.h"
46#include "clang/Basic/LangOptions.h"
47#include "clang/Basic/SourceLocation.h"
48#include "clang/Basic/SourceManager.h"
49#include "clang/Basic/TokenKinds.h"
50#include "clang/Index/IndexDataConsumer.h"
51#include "clang/Index/IndexSymbol.h"
52#include "clang/Index/IndexingAction.h"
53#include "clang/Index/IndexingOptions.h"
54#include "clang/Index/USRGeneration.h"
55#include "clang/Lex/Lexer.h"
56#include "clang/Sema/HeuristicResolver.h"
57#include "clang/Tooling/Syntax/Tokens.h"
58#include "llvm/ADT/ArrayRef.h"
59#include "llvm/ADT/DenseMap.h"
60#include "llvm/ADT/DenseSet.h"
61#include "llvm/ADT/STLExtras.h"
62#include "llvm/ADT/ScopeExit.h"
63#include "llvm/ADT/SmallSet.h"
64#include "llvm/ADT/SmallVector.h"
65#include "llvm/ADT/StringRef.h"
66#include "llvm/Support/Casting.h"
67#include "llvm/Support/Error.h"
68#include "llvm/Support/ErrorHandling.h"
69#include "llvm/Support/Path.h"
70#include "llvm/Support/raw_ostream.h"
71#include <algorithm>
72#include <optional>
73#include <string>
74#include <vector>
75
76namespace clang {
77namespace clangd {
78namespace {
79
80// Returns the single definition of the entity declared by D, if visible.
81// In particular:
82// - for non-redeclarable kinds (e.g. local vars), return D
83// - for kinds that allow multiple definitions (e.g. namespaces), return nullptr
84// Kinds of nodes that always return nullptr here will not have definitions
85// reported by locateSymbolAt().
86const NamedDecl *getDefinition(const NamedDecl *D) {
87 assert(D);
88 // Decl has one definition that we can find.
89 if (const auto *TD = dyn_cast<TagDecl>(D))
90 return TD->getDefinition();
91 if (const auto *VD = dyn_cast<VarDecl>(D))
92 return VD->getDefinition();
93 if (const auto *FD = dyn_cast<FunctionDecl>(D))
94 return FD->getDefinition();
95 if (const auto *CTD = dyn_cast<ClassTemplateDecl>(D))
96 if (const auto *RD = CTD->getTemplatedDecl())
97 return RD->getDefinition();
98 if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
99 if (MD->isThisDeclarationADefinition())
100 return MD;
101 // Look for the method definition inside the implementation decl.
102 auto *DeclCtx = cast<Decl>(MD->getDeclContext());
103 if (DeclCtx->isInvalidDecl())
104 return nullptr;
105
106 if (const auto *CD = dyn_cast<ObjCContainerDecl>(DeclCtx))
107 if (const auto *Impl = getCorrespondingObjCImpl(CD))
108 return Impl->getMethod(MD->getSelector(), MD->isInstanceMethod());
109 }
110 if (const auto *CD = dyn_cast<ObjCContainerDecl>(D))
111 return getCorrespondingObjCImpl(CD);
112 // Only a single declaration is allowed.
113 if (isa<ValueDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
114 isa<TemplateTemplateParmDecl>(D)) // except cases above
115 return D;
116 // Multiple definitions are allowed.
117 return nullptr; // except cases above
118}
119
120void logIfOverflow(const SymbolLocation &Loc) {
121 if (Loc.Start.hasOverflow() || Loc.End.hasOverflow())
122 log("Possible overflow in symbol location: {0}", Loc);
123}
124
125// Convert a SymbolLocation to LSP's Location.
126// TUPath is used to resolve the path of URI.
127std::optional<Location> toLSPLocation(const SymbolLocation &Loc,
128 llvm::StringRef TUPath) {
129 if (!Loc)
130 return std::nullopt;
131 auto LSPLoc = indexToLSPLocation(Loc, TUPath);
132 if (!LSPLoc) {
133 elog("{0}", LSPLoc.takeError());
134 return std::nullopt;
135 }
136 logIfOverflow(Loc);
137 return *LSPLoc;
138}
139
140SymbolLocation toIndexLocation(const Location &Loc, std::string &URIStorage) {
141 SymbolLocation SymLoc;
142 URIStorage = Loc.uri.uri();
143 SymLoc.FileURI = URIStorage.c_str();
144 SymLoc.Start.setLine(Loc.range.start.line);
145 SymLoc.Start.setColumn(Loc.range.start.character);
146 SymLoc.End.setLine(Loc.range.end.line);
147 SymLoc.End.setColumn(Loc.range.end.character);
148 return SymLoc;
149}
150
151// Returns the preferred location between an AST location and an index location.
152SymbolLocation getPreferredLocation(const Location &ASTLoc,
153 const SymbolLocation &IdxLoc,
154 std::string &Scratch) {
155 // Also use a mock symbol for the index location so that other fields (e.g.
156 // definition) are not factored into the preference.
157 Symbol ASTSym, IdxSym;
158 ASTSym.ID = IdxSym.ID = SymbolID("mock_symbol_id");
159 ASTSym.CanonicalDeclaration = toIndexLocation(ASTLoc, Scratch);
160 IdxSym.CanonicalDeclaration = IdxLoc;
161 auto Merged = mergeSymbol(ASTSym, IdxSym);
162 return Merged.CanonicalDeclaration;
163}
164
165std::vector<std::pair<const NamedDecl *, DeclRelationSet>>
166getDeclAtPositionWithRelations(ParsedAST &AST, SourceLocation Pos,
167 DeclRelationSet Relations,
168 ASTNodeKind *NodeKind = nullptr) {
169 unsigned Offset = AST.getSourceManager().getDecomposedSpellingLoc(Pos).second;
170 std::vector<std::pair<const NamedDecl *, DeclRelationSet>> Result;
171 auto ResultFromTree = [&](SelectionTree ST) {
172 if (const SelectionTree::Node *N = ST.commonAncestor()) {
173 if (NodeKind)
174 *NodeKind = N->ASTNode.getNodeKind();
175 // Attributes don't target decls, look at the
176 // thing it's attached to.
177 // We still report the original NodeKind!
178 // This makes the `override` hack work.
179 if (N->ASTNode.get<Attr>() && N->Parent)
180 N = N->Parent;
181 llvm::copy_if(allTargetDecls(N->ASTNode, AST.getHeuristicResolver()),
182 std::back_inserter(Result),
183 [&](auto &Entry) { return !(Entry.second & ~Relations); });
184 }
185 return !Result.empty();
186 };
187 SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Offset,
188 Offset, ResultFromTree);
189 return Result;
190}
191
192std::vector<const NamedDecl *>
193getDeclAtPosition(ParsedAST &AST, SourceLocation Pos, DeclRelationSet Relations,
194 ASTNodeKind *NodeKind = nullptr) {
195 std::vector<const NamedDecl *> Result;
196 for (auto &Entry :
197 getDeclAtPositionWithRelations(AST, Pos, Relations, NodeKind))
198 Result.push_back(Entry.first);
199 return Result;
200}
201
202// Expects Loc to be a SpellingLocation, will bail out otherwise as it can't
203// figure out a filename.
204std::optional<Location> makeLocation(const ASTContext &AST, SourceLocation Loc,
205 llvm::StringRef TUPath) {
206 const auto &SM = AST.getSourceManager();
207 const auto F = SM.getFileEntryRefForID(SM.getFileID(Loc));
208 if (!F)
209 return std::nullopt;
210 auto FilePath = getCanonicalPath(*F, SM.getFileManager());
211 if (!FilePath) {
212 log("failed to get path!");
213 return std::nullopt;
214 }
215 Location L;
216 L.uri = URIForFile::canonicalize(*FilePath, TUPath);
217 // We call MeasureTokenLength here as TokenBuffer doesn't store spelled tokens
218 // outside the main file.
219 auto TokLen = Lexer::MeasureTokenLength(Loc, SM, AST.getLangOpts());
220 L.range = halfOpenToRange(
221 SM, CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(TokLen)));
222 return L;
223}
224
225// Treat #included files as symbols, to enable go-to-definition on them.
226std::optional<LocatedSymbol> locateFileReferent(const Position &Pos,
227 ParsedAST &AST,
228 llvm::StringRef MainFilePath) {
229 for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
230 if (!Inc.Resolved.empty() && Inc.HashLine == Pos.line) {
232 File.Name = std::string(llvm::sys::path::filename(Inc.Resolved));
233 File.PreferredDeclaration = {
234 URIForFile::canonicalize(Inc.Resolved, MainFilePath), Range{}};
235 File.Definition = File.PreferredDeclaration;
236 // We're not going to find any further symbols on #include lines.
237 return File;
238 }
239 }
240 return std::nullopt;
241}
242
243// Macros are simple: there's no declaration/definition distinction.
244// As a consequence, there's no need to look them up in the index either.
245std::optional<LocatedSymbol>
246locateMacroReferent(const syntax::Token &TouchedIdentifier, ParsedAST &AST,
247 llvm::StringRef MainFilePath) {
248 if (auto M = locateMacroAt(TouchedIdentifier, AST.getPreprocessor())) {
249 if (auto Loc =
250 makeLocation(AST.getASTContext(), M->NameLoc, MainFilePath)) {
252 Macro.Name = std::string(M->Name);
253 Macro.PreferredDeclaration = *Loc;
254 Macro.Definition = Loc;
255 Macro.ID = getSymbolID(M->Name, M->Info, AST.getSourceManager());
256 return Macro;
257 }
258 }
259 return std::nullopt;
260}
261
262// A wrapper around `Decl::getCanonicalDecl` to support cases where Clang's
263// definition of a canonical declaration doesn't match up to what a programmer
264// would expect. For example, Objective-C classes can have three types of
265// declarations:
266//
267// - forward declaration(s): @class MyClass;
268// - true declaration (interface definition): @interface MyClass ... @end
269// - true definition (implementation): @implementation MyClass ... @end
270//
271// Clang will consider the forward declaration to be the canonical declaration
272// because it is first. We actually want the class definition if it is
273// available since that is what a programmer would consider the primary
274// declaration to be.
275const NamedDecl *getPreferredDecl(const NamedDecl *D) {
276 // FIXME: Canonical declarations of some symbols might refer to built-in
277 // decls with possibly-invalid source locations (e.g. global new operator).
278 // In such cases we should pick up a redecl with valid source location
279 // instead of failing.
280 D = llvm::cast<NamedDecl>(D->getCanonicalDecl());
281
282 // Prefer Objective-C class/protocol definitions over the forward declaration.
283 if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(D))
284 if (const auto *DefinitionID = ID->getDefinition())
285 return DefinitionID;
286 if (const auto *PD = dyn_cast<ObjCProtocolDecl>(D))
287 if (const auto *DefinitionID = PD->getDefinition())
288 return DefinitionID;
289
290 return D;
291}
292
293std::vector<LocatedSymbol> findImplementors(llvm::DenseSet<SymbolID> IDs,
294 RelationKind Predicate,
295 const SymbolIndex *Index,
296 llvm::StringRef MainFilePath) {
297 if (IDs.empty() || !Index)
298 return {};
299 static constexpr trace::Metric FindImplementorsMetric(
300 "find_implementors", trace::Metric::Counter, "case");
301 switch (Predicate) {
303 FindImplementorsMetric.record(1, "find-base");
304 break;
306 FindImplementorsMetric.record(1, "find-override");
307 break;
308 }
309
311 Req.Predicate = Predicate;
312 Req.Subjects = std::move(IDs);
313 std::vector<LocatedSymbol> Results;
314 Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
315 auto DeclLoc =
316 indexToLSPLocation(Object.CanonicalDeclaration, MainFilePath);
317 if (!DeclLoc) {
318 elog("Find overrides: {0}", DeclLoc.takeError());
319 return;
320 }
321 Results.emplace_back();
322 Results.back().Name = Object.Name.str();
323 Results.back().PreferredDeclaration = *DeclLoc;
324 auto DefLoc = indexToLSPLocation(Object.Definition, MainFilePath);
325 if (!DefLoc) {
326 elog("Failed to convert location: {0}", DefLoc.takeError());
327 return;
328 }
329 Results.back().Definition = *DefLoc;
330 });
331 return Results;
332}
333
334// Given LocatedSymbol results derived from the AST, query the index to obtain
335// definitions and preferred declarations.
336void enhanceLocatedSymbolsFromIndex(llvm::MutableArrayRef<LocatedSymbol> Result,
337 const SymbolIndex *Index,
338 llvm::StringRef MainFilePath) {
339 LookupRequest QueryRequest;
340 llvm::DenseMap<SymbolID, unsigned> ResultIndex;
341 for (unsigned I = 0; I < Result.size(); ++I) {
342 if (auto ID = Result[I].ID) {
343 ResultIndex.try_emplace(ID, I);
344 QueryRequest.IDs.insert(ID);
345 }
346 }
347 if (!Index || QueryRequest.IDs.empty())
348 return;
349 std::string Scratch;
350 Index->lookup(QueryRequest, [&](const Symbol &Sym) {
351 auto &R = Result[ResultIndex.lookup(Sym.ID)];
352
353 if (R.Definition) { // from AST
354 // Special case: if the AST yielded a definition, then it may not be
355 // the right *declaration*. Prefer the one from the index.
356 if (auto Loc = toLSPLocation(Sym.CanonicalDeclaration, MainFilePath))
357 R.PreferredDeclaration = *Loc;
358
359 // We might still prefer the definition from the index, e.g. for
360 // generated symbols.
361 if (auto Loc = toLSPLocation(
362 getPreferredLocation(*R.Definition, Sym.Definition, Scratch),
363 MainFilePath))
364 R.Definition = *Loc;
365 } else {
366 R.Definition = toLSPLocation(Sym.Definition, MainFilePath);
367
368 // Use merge logic to choose AST or index declaration.
369 if (auto Loc = toLSPLocation(
370 getPreferredLocation(R.PreferredDeclaration,
371 Sym.CanonicalDeclaration, Scratch),
372 MainFilePath))
373 R.PreferredDeclaration = *Loc;
374 }
375 });
376}
377
378bool objcMethodIsTouched(const SourceManager &SM, const ObjCMethodDecl *OMD,
379 SourceLocation Loc) {
380 unsigned NumSels = OMD->getNumSelectorLocs();
381 for (unsigned I = 0; I < NumSels; ++I)
382 if (SM.getSpellingLoc(OMD->getSelectorLoc(I)) == Loc)
383 return true;
384 return false;
385}
386
387// Decls are more complicated.
388// The AST contains at least a declaration, maybe a definition.
389// These are up-to-date, and so generally preferred over index results.
390// We perform a single batch index lookup to find additional definitions.
391std::vector<LocatedSymbol>
392locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
393 ParsedAST &AST, llvm::StringRef MainFilePath,
394 const SymbolIndex *Index, ASTNodeKind &NodeKind) {
395 const SourceManager &SM = AST.getSourceManager();
396 // Results follow the order of Symbols.Decls.
397 std::vector<LocatedSymbol> Result;
398
399 static constexpr trace::Metric LocateASTReferentMetric(
400 "locate_ast_referent", trace::Metric::Counter, "case");
401 auto AddResultDecl = [&](const NamedDecl *D) {
402 D = getPreferredDecl(D);
403 auto Loc =
404 makeLocation(AST.getASTContext(), nameLocation(*D, SM), MainFilePath);
405 if (!Loc)
406 return;
407
408 Result.emplace_back();
409 Result.back().Name = printName(AST.getASTContext(), *D);
410 Result.back().PreferredDeclaration = *Loc;
411 Result.back().ID = getSymbolID(D);
412 if (const NamedDecl *Def = getDefinition(D))
413 Result.back().Definition = makeLocation(
414 AST.getASTContext(), nameLocation(*Def, SM), MainFilePath);
415 };
416
417 // Emit all symbol locations (declaration or definition) from AST.
418 DeclRelationSet Relations =
420 auto Candidates =
421 getDeclAtPositionWithRelations(AST, CurLoc, Relations, &NodeKind);
422 llvm::DenseSet<SymbolID> VirtualMethods;
423 for (const auto &E : Candidates) {
424 const NamedDecl *D = E.first;
425 if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
426 // Special case: virtual void ^method() = 0: jump to all overrides.
427 // FIXME: extend it to ^virtual, unfortunately, virtual location is not
428 // saved in the AST.
429 if (CMD->isPureVirtual()) {
430 if (TouchedIdentifier && SM.getSpellingLoc(CMD->getLocation()) ==
431 TouchedIdentifier->location()) {
432 VirtualMethods.insert(getSymbolID(CMD));
433 LocateASTReferentMetric.record(1, "method-to-override");
434 }
435 }
436 // Special case: void foo() ^override: jump to the overridden method.
437 if (NodeKind.isSame(ASTNodeKind::getFromNodeKind<OverrideAttr>()) ||
438 NodeKind.isSame(ASTNodeKind::getFromNodeKind<FinalAttr>())) {
439 // We may be overridding multiple methods - offer them all.
440 for (const NamedDecl *ND : CMD->overridden_methods())
441 AddResultDecl(ND);
442 continue;
443 }
444 }
445 // Special case: - (void)^method {} should jump to overrides, but the decl
446 // shouldn't, only the definition. Note that an Objective-C method can
447 // override a parent class or protocol.
448 //
449 // FIXME: Support jumping from a protocol decl to overrides on go-to
450 // definition.
451 if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D)) {
452 if (OMD->isThisDeclarationADefinition() && TouchedIdentifier &&
453 objcMethodIsTouched(SM, OMD, TouchedIdentifier->location())) {
454 llvm::SmallVector<const ObjCMethodDecl *, 4> Overrides;
455 OMD->getOverriddenMethods(Overrides);
456 if (!Overrides.empty()) {
457 for (const auto *Override : Overrides)
458 AddResultDecl(Override);
459 LocateASTReferentMetric.record(1, "objc-overriden-method");
460 }
461 AddResultDecl(OMD);
462 continue;
463 }
464 }
465
466 // Special case: the cursor is on an alias, prefer other results.
467 // This targets "using ns::^Foo", where the target is more interesting.
468 // This does not trigger on renaming aliases:
469 // `using Foo = ^Bar` already targets Bar via a TypeLoc
470 // `using ^Foo = Bar` has no other results, as Underlying is filtered.
471 if (E.second & DeclRelation::Alias && Candidates.size() > 1 &&
472 // beginLoc/endLoc are a token range, so rewind the identifier we're in.
473 SM.isPointWithin(TouchedIdentifier ? TouchedIdentifier->location()
474 : CurLoc,
475 D->getBeginLoc(), D->getEndLoc()))
476 continue;
477
478 // Special case: the point of declaration of a template specialization,
479 // it's more useful to navigate to the template declaration.
480 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
481 if (TouchedIdentifier &&
482 D->getLocation() == TouchedIdentifier->location()) {
483 LocateASTReferentMetric.record(1, "template-specialization-to-primary");
484 AddResultDecl(CTSD->getSpecializedTemplate());
485 continue;
486 }
487 }
488
489 // Special case: if the class name is selected, also map Objective-C
490 // categories and category implementations back to their class interface.
491 //
492 // Since `TouchedIdentifier` might refer to the `ObjCCategoryImplDecl`
493 // instead of the `ObjCCategoryDecl` we intentionally check the contents
494 // of the locs when checking for class name equivalence.
495 if (const auto *CD = dyn_cast<ObjCCategoryDecl>(D))
496 if (const auto *ID = CD->getClassInterface())
497 if (TouchedIdentifier &&
498 (CD->getLocation() == TouchedIdentifier->location() ||
499 ID->getName() == TouchedIdentifier->text(SM))) {
500 LocateASTReferentMetric.record(1, "objc-category-to-class");
501 AddResultDecl(ID);
502 }
503
504 LocateASTReferentMetric.record(1, "regular");
505 // Otherwise the target declaration is the right one.
506 AddResultDecl(D);
507 }
508 enhanceLocatedSymbolsFromIndex(Result, Index, MainFilePath);
509
510 auto Overrides = findImplementors(VirtualMethods, RelationKind::OverriddenBy,
511 Index, MainFilePath);
512 Result.insert(Result.end(), Overrides.begin(), Overrides.end());
513 return Result;
514}
515
516std::vector<LocatedSymbol> locateSymbolForType(const ParsedAST &AST,
517 const QualType &Type,
518 const SymbolIndex *Index) {
519 const auto &SM = AST.getSourceManager();
520 auto MainFilePath = AST.tuPath();
521
522 // FIXME: this sends unique_ptr<Foo> to unique_ptr<T>.
523 // Likely it would be better to send it to Foo (heuristically) or to both.
524 auto Decls = targetDecl(DynTypedNode::create(Type.getNonReferenceType()),
526 AST.getHeuristicResolver());
527 if (Decls.empty())
528 return {};
529
530 std::vector<LocatedSymbol> Results;
531 const auto &ASTContext = AST.getASTContext();
532
533 for (const NamedDecl *D : Decls) {
534 D = getPreferredDecl(D);
535
536 auto Loc = makeLocation(ASTContext, nameLocation(*D, SM), MainFilePath);
537 if (!Loc)
538 continue;
539
540 Results.emplace_back();
541 Results.back().Name = printName(ASTContext, *D);
542 Results.back().PreferredDeclaration = *Loc;
543 Results.back().ID = getSymbolID(D);
544 if (const NamedDecl *Def = getDefinition(D))
545 Results.back().Definition =
546 makeLocation(ASTContext, nameLocation(*Def, SM), MainFilePath);
547 }
548 enhanceLocatedSymbolsFromIndex(Results, Index, MainFilePath);
549
550 return Results;
551}
552
553bool tokenSpelledAt(SourceLocation SpellingLoc, const syntax::TokenBuffer &TB) {
554 auto ExpandedTokens = TB.expandedTokens(
555 TB.sourceManager().getMacroArgExpandedLocation(SpellingLoc));
556 return !ExpandedTokens.empty();
557}
558
559llvm::StringRef sourcePrefix(SourceLocation Loc, const SourceManager &SM) {
560 auto D = SM.getDecomposedLoc(Loc);
561 bool Invalid = false;
562 llvm::StringRef Buf = SM.getBufferData(D.first, &Invalid);
563 if (Invalid || D.second > Buf.size())
564 return "";
565 return Buf.substr(0, D.second);
566}
567
568bool isDependentName(ASTNodeKind NodeKind) {
569 return NodeKind.isSame(ASTNodeKind::getFromNodeKind<OverloadExpr>()) ||
570 NodeKind.isSame(
571 ASTNodeKind::getFromNodeKind<CXXDependentScopeMemberExpr>()) ||
572 NodeKind.isSame(
573 ASTNodeKind::getFromNodeKind<DependentScopeDeclRefExpr>());
574}
575
576} // namespace
577
578std::vector<LocatedSymbol> locateSymbolTextually(const SpelledWord &Word,
579 ParsedAST &AST,
580 const SymbolIndex *Index,
581 llvm::StringRef MainFilePath,
582 ASTNodeKind NodeKind) {
583 // Don't use heuristics if this is a real identifier, or not an
584 // identifier.
585 // Exception: dependent names, because those may have useful textual
586 // matches that AST-based heuristics cannot find.
587 if ((Word.ExpandedToken && !isDependentName(NodeKind)) ||
588 !Word.LikelyIdentifier || !Index)
589 return {};
590 // We don't want to handle words in string literals. (It'd be nice to list
591 // *allowed* token kinds explicitly, but comment Tokens aren't retained).
592 if (Word.PartOfSpelledToken &&
593 isStringLiteral(Word.PartOfSpelledToken->kind()))
594 return {};
595
596 const auto &SM = AST.getSourceManager();
597 // Look up the selected word in the index.
599 Req.Query = Word.Text.str();
600 Req.ProximityPaths = {MainFilePath.str()};
601 // Find the namespaces to query by lexing the file.
602 Req.Scopes =
603 visibleNamespaces(sourcePrefix(Word.Location, SM), AST.getLangOpts());
604 // FIXME: For extra strictness, consider AnyScope=false.
605 Req.AnyScope = true;
606 // We limit the results to 3 further below. This limit is to avoid fetching
607 // too much data, while still likely having enough for 3 results to remain
608 // after additional filtering.
609 Req.Limit = 10;
610 bool TooMany = false;
611 using ScoredLocatedSymbol = std::pair<float, LocatedSymbol>;
612 std::vector<ScoredLocatedSymbol> ScoredResults;
613 Index->fuzzyFind(Req, [&](const Symbol &Sym) {
614 // Only consider exact name matches, including case.
615 // This is to avoid too many false positives.
616 // We could relax this in the future (e.g. to allow for typos) if we make
617 // the query more accurate by other means.
618 if (Sym.Name != Word.Text)
619 return;
620
621 // Exclude constructor results. They have the same name as the class,
622 // but we don't have enough context to prefer them over the class.
623 if (Sym.SymInfo.Kind == index::SymbolKind::Constructor)
624 return;
625
626 auto MaybeDeclLoc =
627 indexToLSPLocation(Sym.CanonicalDeclaration, MainFilePath);
628 if (!MaybeDeclLoc) {
629 log("locateSymbolNamedTextuallyAt: {0}", MaybeDeclLoc.takeError());
630 return;
631 }
632 LocatedSymbol Located;
633 Located.PreferredDeclaration = *MaybeDeclLoc;
634 Located.Name = (Sym.Name + Sym.TemplateSpecializationArgs).str();
635 Located.ID = Sym.ID;
636 if (Sym.Definition) {
637 auto MaybeDefLoc = indexToLSPLocation(Sym.Definition, MainFilePath);
638 if (!MaybeDefLoc) {
639 log("locateSymbolNamedTextuallyAt: {0}", MaybeDefLoc.takeError());
640 return;
641 }
642 Located.PreferredDeclaration = *MaybeDefLoc;
643 Located.Definition = *MaybeDefLoc;
644 }
645
646 if (ScoredResults.size() >= 5) {
647 // If we have more than 5 results, don't return anything,
648 // as confidence is too low.
649 // FIXME: Alternatively, try a stricter query?
650 TooMany = true;
651 return;
652 }
653
654 SymbolQualitySignals Quality;
655 Quality.merge(Sym);
656 SymbolRelevanceSignals Relevance;
657 Relevance.Name = Sym.Name;
659 Relevance.merge(Sym);
660 auto Score = evaluateSymbolAndRelevance(Quality.evaluateHeuristics(),
661 Relevance.evaluateHeuristics());
662 dlog("locateSymbolNamedTextuallyAt: {0}{1} = {2}\n{3}{4}\n", Sym.Scope,
663 Sym.Name, Score, Quality, Relevance);
664
665 ScoredResults.push_back({Score, std::move(Located)});
666 });
667
668 if (TooMany) {
669 vlog("Heuristic index lookup for {0} returned too many candidates, ignored",
670 Word.Text);
671 return {};
672 }
673
674 llvm::sort(ScoredResults,
675 [](const ScoredLocatedSymbol &A, const ScoredLocatedSymbol &B) {
676 return A.first > B.first;
677 });
678 std::vector<LocatedSymbol> Results;
679 for (auto &Res : std::move(ScoredResults))
680 Results.push_back(std::move(Res.second));
681 if (Results.empty())
682 vlog("No heuristic index definition for {0}", Word.Text);
683 else
684 log("Found definition heuristically in index for {0}", Word.Text);
685 return Results;
686}
687
688const syntax::Token *findNearbyIdentifier(const SpelledWord &Word,
689 const syntax::TokenBuffer &TB) {
690 // Don't use heuristics if this is a real identifier.
691 // Unlikely identifiers are OK if they were used as identifiers nearby.
692 if (Word.ExpandedToken)
693 return nullptr;
694 // We don't want to handle words in string literals. (It'd be nice to list
695 // *allowed* token kinds explicitly, but comment Tokens aren't retained).
696 if (Word.PartOfSpelledToken &&
697 isStringLiteral(Word.PartOfSpelledToken->kind()))
698 return {};
699
700 const SourceManager &SM = TB.sourceManager();
701 // We prefer the closest possible token, line-wise. Backwards is penalized.
702 // Ties are implicitly broken by traversal order (first-one-wins).
703 auto File = SM.getFileID(Word.Location);
704 unsigned WordLine = SM.getSpellingLineNumber(Word.Location);
705 auto Cost = [&](SourceLocation Loc) -> unsigned {
706 assert(SM.getFileID(Loc) == File && "spelled token in wrong file?");
707 unsigned Line = SM.getSpellingLineNumber(Loc);
708 return Line >= WordLine ? Line - WordLine : 2 * (WordLine - Line);
709 };
710 const syntax::Token *BestTok = nullptr;
711 unsigned BestCost = -1;
712 // Search bounds are based on word length:
713 // - forward: 2^N lines
714 // - backward: 2^(N-1) lines.
715 unsigned MaxDistance =
716 1U << std::min<unsigned>(Word.Text.size(),
717 std::numeric_limits<unsigned>::digits - 1);
718 // Line number for SM.translateLineCol() should be one-based, also
719 // SM.translateLineCol() can handle line number greater than
720 // number of lines in the file.
721 // - LineMin = max(1, WordLine + 1 - 2^(N-1))
722 // - LineMax = WordLine + 1 + 2^N
723 unsigned LineMin =
724 WordLine + 1 <= MaxDistance / 2 ? 1 : WordLine + 1 - MaxDistance / 2;
725 unsigned LineMax = WordLine + 1 + MaxDistance;
726 SourceLocation LocMin = SM.translateLineCol(File, LineMin, 1);
727 assert(LocMin.isValid());
728 SourceLocation LocMax = SM.translateLineCol(File, LineMax, 1);
729 assert(LocMax.isValid());
730
731 // Updates BestTok and BestCost if Tok is a good candidate.
732 // May return true if the cost is too high for this token.
733 auto Consider = [&](const syntax::Token &Tok) {
734 if (Tok.location() < LocMin || Tok.location() > LocMax)
735 return true; // we are too far from the word, break the outer loop.
736 if (!(Tok.kind() == tok::identifier && Tok.text(SM) == Word.Text))
737 return false;
738 // No point guessing the same location we started with.
739 if (Tok.location() == Word.Location)
740 return false;
741 // We've done cheap checks, compute cost so we can break the caller's loop.
742 unsigned TokCost = Cost(Tok.location());
743 if (TokCost >= BestCost)
744 return true; // causes the outer loop to break.
745 // Allow locations that might be part of the AST, and macros (even if empty)
746 // but not things like disabled preprocessor sections.
747 if (!(tokenSpelledAt(Tok.location(), TB) || TB.expansionStartingAt(&Tok)))
748 return false;
749 // We already verified this token is an improvement.
750 BestCost = TokCost;
751 BestTok = &Tok;
752 return false;
753 };
754 auto SpelledTokens = TB.spelledTokens(File);
755 // Find where the word occurred in the token stream, to search forward & back.
756 auto *I = llvm::partition_point(SpelledTokens, [&](const syntax::Token &T) {
757 assert(SM.getFileID(T.location()) == SM.getFileID(Word.Location));
758 return T.location() < Word.Location; // Comparison OK: same file.
759 });
760 // Search for matches after the cursor.
761 for (const syntax::Token &Tok : llvm::ArrayRef(I, SpelledTokens.end()))
762 if (Consider(Tok))
763 break; // costs of later tokens are greater...
764 // Search for matches before the cursor.
765 for (const syntax::Token &Tok :
766 llvm::reverse(llvm::ArrayRef(SpelledTokens.begin(), I)))
767 if (Consider(Tok))
768 break;
769
770 if (BestTok)
771 vlog(
772 "Word {0} under cursor {1} isn't a token (after PP), trying nearby {2}",
773 Word.Text, Word.Location.printToString(SM),
774 BestTok->location().printToString(SM));
775
776 return BestTok;
777}
778
779std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
780 const SymbolIndex *Index) {
781 const auto &SM = AST.getSourceManager();
782 auto MainFilePath = AST.tuPath();
783
784 if (auto File = locateFileReferent(Pos, AST, MainFilePath))
785 return {std::move(*File)};
786
787 auto CurLoc = sourceLocationInMainFile(SM, Pos);
788 if (!CurLoc) {
789 elog("locateSymbolAt failed to convert position to source location: {0}",
790 CurLoc.takeError());
791 return {};
792 }
793
794 const syntax::Token *TouchedIdentifier = nullptr;
795 auto TokensTouchingCursor =
796 syntax::spelledTokensTouching(*CurLoc, AST.getTokens());
797 for (const syntax::Token &Tok : TokensTouchingCursor) {
798 if (Tok.kind() == tok::identifier) {
799 if (auto Macro = locateMacroReferent(Tok, AST, MainFilePath))
800 // Don't look at the AST or index if we have a macro result.
801 // (We'd just return declarations referenced from the macro's
802 // expansion.)
803 return {*std::move(Macro)};
804
805 TouchedIdentifier = &Tok;
806 break;
807 }
808
809 if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) {
810 // go-to-definition on auto should find the definition of the deduced
811 // type, if possible
812 if (auto Deduced =
813 getDeducedType(AST.getASTContext(), AST.getHeuristicResolver(),
814 Tok.location())) {
815 auto LocSym = locateSymbolForType(AST, *Deduced, Index);
816 if (!LocSym.empty())
817 return LocSym;
818 }
819 }
820 }
821
822 ASTNodeKind NodeKind;
823 auto ASTResults = locateASTReferent(*CurLoc, TouchedIdentifier, AST,
824 MainFilePath, Index, NodeKind);
825 if (!ASTResults.empty())
826 return ASTResults;
827
828 // If the cursor can't be resolved directly, try fallback strategies.
829 auto Word =
830 SpelledWord::touching(*CurLoc, AST.getTokens(), AST.getLangOpts());
831 if (Word) {
832 // Is the same word nearby a real identifier that might refer to something?
833 if (const syntax::Token *NearbyIdent =
834 findNearbyIdentifier(*Word, AST.getTokens())) {
835 if (auto Macro = locateMacroReferent(*NearbyIdent, AST, MainFilePath)) {
836 log("Found macro definition heuristically using nearby identifier {0}",
837 Word->Text);
838 return {*std::move(Macro)};
839 }
840 ASTResults = locateASTReferent(NearbyIdent->location(), NearbyIdent, AST,
841 MainFilePath, Index, NodeKind);
842 if (!ASTResults.empty()) {
843 log("Found definition heuristically using nearby identifier {0}",
844 NearbyIdent->text(SM));
845 return ASTResults;
846 }
847 vlog("No definition found using nearby identifier {0} at {1}", Word->Text,
848 Word->Location.printToString(SM));
849 }
850 // No nearby word, or it didn't refer to anything either. Try the index.
851 auto TextualResults =
852 locateSymbolTextually(*Word, AST, Index, MainFilePath, NodeKind);
853 if (!TextualResults.empty())
854 return TextualResults;
855 }
856
857 return {};
858}
859
860std::vector<DocumentLink> getDocumentLinks(ParsedAST &AST) {
861 const auto &SM = AST.getSourceManager();
862
863 std::vector<DocumentLink> Result;
864 for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
865 if (Inc.Resolved.empty())
866 continue;
867
868 // Get the location of the # symbole of the "#include ..." statement
869 auto HashLoc = SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset);
870
871 // get the # Token itself, std::next to get the "include" token and the
872 // first token after (aka "File Token")
873 const auto *HashTok = AST.getTokens().spelledTokenContaining(HashLoc);
874 assert(HashTok && "got inclusion at wrong offset");
875 const auto *IncludeTok = std::next(HashTok);
876 const auto *FileTok = std::next(IncludeTok);
877
878 // The File Token can either be of kind :
879 // "less" if using the "#include <h-char-sequence> new-line" syntax
880 // "string_literal" if using the "#include "q-char-sequence" new-line"
881 // syntax something else (most likely "identifier") if using the "#include
882 // pp-tokens new-line" syntax (#include with macro argument)
883
884 CharSourceRange FileRange;
885
886 if (FileTok->kind() == tok::TokenKind::less) {
887 // FileTok->range would only include the '<' char. Hence we explicitly use
888 // Inc.Written's length.
889 FileRange =
890 syntax::FileRange(SM, FileTok->location(), Inc.Written.length())
891 .toCharRange(SM);
892 } else if (FileTok->kind() == tok::TokenKind::string_literal) {
893 // FileTok->range includes the quotes for string literals so just return
894 // it.
895 FileRange = FileTok->range(SM).toCharRange(SM);
896 } else {
897 // FileTok is the first Token of a macro spelling
898
899 // Report the range of the first token (as it should be the macro
900 // identifier)
901 // We could use the AST to find the last spelled token of the macro and
902 // report a range spanning the full macro expression, but it would require
903 // using token-buffers that are deemed too unstable and crash-prone
904 // due to optimizations in cland
905
906 FileRange = FileTok->range(SM).toCharRange(SM);
907 }
908
909 Result.push_back(
910 DocumentLink({halfOpenToRange(SM, FileRange),
911 URIForFile::canonicalize(Inc.Resolved, AST.tuPath())}));
912 }
913
914 return Result;
915}
916
917namespace {
918
919/// Collects references to symbols within the main file.
920class ReferenceFinder : public index::IndexDataConsumer {
921public:
922 struct Reference {
923 syntax::Token SpelledTok;
924 index::SymbolRoleSet Role;
925 const Decl *Container;
926
927 Range range(const SourceManager &SM) const {
928 return halfOpenToRange(SM, SpelledTok.range(SM).toCharRange(SM));
929 }
930 };
931
932 ReferenceFinder(const ParsedAST &AST,
933 const llvm::ArrayRef<const NamedDecl *> Targets,
934 bool PerToken)
935 : PerToken(PerToken), AST(AST) {
936 for (const NamedDecl *ND : Targets)
937 TargetDecls.insert(ND->getCanonicalDecl());
938 }
939
940 std::vector<Reference> take() && {
941 llvm::sort(References, [](const Reference &L, const Reference &R) {
942 auto LTok = L.SpelledTok.location();
943 auto RTok = R.SpelledTok.location();
944 return std::tie(LTok, L.Role) < std::tie(RTok, R.Role);
945 });
946 // We sometimes see duplicates when parts of the AST get traversed twice.
947 References.erase(llvm::unique(References,
948 [](const Reference &L, const Reference &R) {
949 auto LTok = L.SpelledTok.location();
950 auto RTok = R.SpelledTok.location();
951 return std::tie(LTok, L.Role) ==
952 std::tie(RTok, R.Role);
953 }),
954 References.end());
955 return std::move(References);
956 }
957
958 bool
959 handleDeclOccurrence(const Decl *D, index::SymbolRoleSet Roles,
960 llvm::ArrayRef<index::SymbolRelation> Relations,
961 SourceLocation Loc,
962 index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
963 if (!TargetDecls.contains(D->getCanonicalDecl()))
964 return true;
965 const SourceManager &SM = AST.getSourceManager();
966 if (!isInsideMainFile(Loc, SM))
967 return true;
968 const auto &TB = AST.getTokens();
969
970 llvm::SmallVector<SourceLocation, 1> Locs;
971 if (PerToken) {
972 // Check whether this is one of the few constructs where the reference
973 // can be split over several tokens.
974 if (auto *OME = llvm::dyn_cast_or_null<ObjCMessageExpr>(ASTNode.OrigE)) {
975 OME->getSelectorLocs(Locs);
976 } else if (auto *OMD =
977 llvm::dyn_cast_or_null<ObjCMethodDecl>(ASTNode.OrigD)) {
978 OMD->getSelectorLocs(Locs);
979 }
980 // Sanity check: we expect the *first* token to match the reported loc.
981 // Otherwise, maybe it was e.g. some other kind of reference to a Decl.
982 if (!Locs.empty() && Locs.front() != Loc)
983 Locs.clear(); // First token doesn't match, assume our guess was wrong.
984 }
985 if (Locs.empty())
986 Locs.push_back(Loc);
987
988 SymbolCollector::Options CollectorOpts;
989 CollectorOpts.CollectMainFileSymbols = true;
990 for (SourceLocation L : Locs) {
991 L = SM.getFileLoc(L);
992 if (const auto *Tok = TB.spelledTokenContaining(L))
993 References.push_back(
994 {*Tok, Roles,
995 SymbolCollector::getRefContainer(ASTNode.Parent, CollectorOpts)});
996 }
997 return true;
998 }
999
1000private:
1001 bool PerToken; // If true, report 3 references for split ObjC selector names.
1002 std::vector<Reference> References;
1003 const ParsedAST &AST;
1004 llvm::DenseSet<const Decl *> TargetDecls;
1005};
1006
1007std::vector<ReferenceFinder::Reference>
1008findRefs(const llvm::ArrayRef<const NamedDecl *> TargetDecls, ParsedAST &AST,
1009 bool PerToken) {
1010 ReferenceFinder RefFinder(AST, TargetDecls, PerToken);
1011 index::IndexingOptions IndexOpts;
1012 IndexOpts.SystemSymbolFilter =
1013 index::IndexingOptions::SystemSymbolFilterKind::All;
1014 IndexOpts.IndexFunctionLocals = true;
1015 IndexOpts.IndexParametersInDeclarations = true;
1016 IndexOpts.IndexTemplateParameters = true;
1017 indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(),
1018 AST.getLocalTopLevelDecls(), RefFinder, IndexOpts);
1019 return std::move(RefFinder).take();
1020}
1021
1022const Stmt *getFunctionBody(DynTypedNode N) {
1023 if (const auto *FD = N.get<FunctionDecl>())
1024 return FD->getBody();
1025 if (const auto *FD = N.get<BlockDecl>())
1026 return FD->getBody();
1027 if (const auto *FD = N.get<LambdaExpr>())
1028 return FD->getBody();
1029 if (const auto *FD = N.get<ObjCMethodDecl>())
1030 return FD->getBody();
1031 return nullptr;
1032}
1033
1034const Stmt *getLoopBody(DynTypedNode N) {
1035 if (const auto *LS = N.get<ForStmt>())
1036 return LS->getBody();
1037 if (const auto *LS = N.get<CXXForRangeStmt>())
1038 return LS->getBody();
1039 if (const auto *LS = N.get<WhileStmt>())
1040 return LS->getBody();
1041 if (const auto *LS = N.get<DoStmt>())
1042 return LS->getBody();
1043 return nullptr;
1044}
1045
1046// AST traversal to highlight control flow statements under some root.
1047// Once we hit further control flow we prune the tree (or at least restrict
1048// what we highlight) so we capture e.g. breaks from the outer loop only.
1049class FindControlFlow : public RecursiveASTVisitor<FindControlFlow> {
1050 // Types of control-flow statements we might highlight.
1051 enum Target {
1052 Break = 1,
1053 Continue = 2,
1054 Return = 4,
1055 Case = 8,
1056 Throw = 16,
1057 Goto = 32,
1058 All = Break | Continue | Return | Case | Throw | Goto,
1059 };
1060 int Ignore = 0; // bitmask of Target - what are we *not* highlighting?
1061 SourceRange Bounds; // Half-open, restricts reported targets.
1062 std::vector<SourceLocation> &Result;
1063 const SourceManager &SM;
1064
1065 // Masks out targets for a traversal into D.
1066 // Traverses the subtree using Delegate() if any targets remain.
1067 template <typename Func>
1068 bool filterAndTraverse(DynTypedNode D, const Func &Delegate) {
1069 auto RestoreIgnore = llvm::make_scope_exit(
1070 [OldIgnore(Ignore), this] { Ignore = OldIgnore; });
1071 if (getFunctionBody(D))
1072 Ignore = All;
1073 else if (getLoopBody(D))
1074 Ignore |= Continue | Break;
1075 else if (D.get<SwitchStmt>())
1076 Ignore |= Break | Case;
1077 // Prune tree if we're not looking for anything.
1078 return (Ignore == All) ? true : Delegate();
1079 }
1080
1081 void found(Target T, SourceLocation Loc) {
1082 if (T & Ignore)
1083 return;
1084 if (SM.isBeforeInTranslationUnit(Loc, Bounds.getBegin()) ||
1085 SM.isBeforeInTranslationUnit(Bounds.getEnd(), Loc))
1086 return;
1087 Result.push_back(Loc);
1088 }
1089
1090public:
1091 FindControlFlow(SourceRange Bounds, std::vector<SourceLocation> &Result,
1092 const SourceManager &SM)
1093 : Bounds(Bounds), Result(Result), SM(SM) {}
1094
1095 // When traversing function or loops, limit targets to those that still
1096 // refer to the original root.
1097 bool TraverseDecl(Decl *D) {
1098 return !D || filterAndTraverse(DynTypedNode::create(*D), [&] {
1099 return RecursiveASTVisitor::TraverseDecl(D);
1100 });
1101 }
1102 bool TraverseStmt(Stmt *S) {
1103 return !S || filterAndTraverse(DynTypedNode::create(*S), [&] {
1104 return RecursiveASTVisitor::TraverseStmt(S);
1105 });
1106 }
1107
1108 // Add leaves that we found and want.
1109 bool VisitReturnStmt(ReturnStmt *R) {
1110 found(Return, R->getReturnLoc());
1111 return true;
1112 }
1113 bool VisitBreakStmt(BreakStmt *B) {
1114 found(Break, B->getKwLoc());
1115 return true;
1116 }
1117 bool VisitContinueStmt(ContinueStmt *C) {
1118 found(Continue, C->getKwLoc());
1119 return true;
1120 }
1121 bool VisitSwitchCase(SwitchCase *C) {
1122 found(Case, C->getKeywordLoc());
1123 return true;
1124 }
1125 bool VisitCXXThrowExpr(CXXThrowExpr *T) {
1126 found(Throw, T->getThrowLoc());
1127 return true;
1128 }
1129 bool VisitGotoStmt(GotoStmt *G) {
1130 // Goto is interesting if its target is outside the root.
1131 if (const auto *LD = G->getLabel()) {
1132 if (SM.isBeforeInTranslationUnit(LD->getLocation(), Bounds.getBegin()) ||
1133 SM.isBeforeInTranslationUnit(Bounds.getEnd(), LD->getLocation()))
1134 found(Goto, G->getGotoLoc());
1135 }
1136 return true;
1137 }
1138};
1139
1140// Given a location within a switch statement, return the half-open range that
1141// covers the case it's contained in.
1142// We treat `case X: case Y: ...` as one case, and assume no other fallthrough.
1143SourceRange findCaseBounds(const SwitchStmt &Switch, SourceLocation Loc,
1144 const SourceManager &SM) {
1145 // Cases are not stored in order, sort them first.
1146 // (In fact they seem to be stored in reverse order, don't rely on this)
1147 std::vector<const SwitchCase *> Cases;
1148 for (const SwitchCase *Case = Switch.getSwitchCaseList(); Case;
1149 Case = Case->getNextSwitchCase())
1150 Cases.push_back(Case);
1151 llvm::sort(Cases, [&](const SwitchCase *L, const SwitchCase *R) {
1152 return SM.isBeforeInTranslationUnit(L->getKeywordLoc(), R->getKeywordLoc());
1153 });
1154
1155 // Find the first case after the target location, the end of our range.
1156 auto CaseAfter = llvm::partition_point(Cases, [&](const SwitchCase *C) {
1157 return !SM.isBeforeInTranslationUnit(Loc, C->getKeywordLoc());
1158 });
1159 SourceLocation End = CaseAfter == Cases.end() ? Switch.getEndLoc()
1160 : (*CaseAfter)->getKeywordLoc();
1161
1162 // Our target can be before the first case - cases are optional!
1163 if (CaseAfter == Cases.begin())
1164 return SourceRange(Switch.getBeginLoc(), End);
1165 // The start of our range is usually the previous case, but...
1166 auto CaseBefore = std::prev(CaseAfter);
1167 // ... rewind CaseBefore to the first in a `case A: case B: ...` sequence.
1168 while (CaseBefore != Cases.begin() &&
1169 (*std::prev(CaseBefore))->getSubStmt() == *CaseBefore)
1170 --CaseBefore;
1171 return SourceRange((*CaseBefore)->getKeywordLoc(), End);
1172}
1173
1174// Returns the locations of control flow statements related to N. e.g.:
1175// for => branches: break/continue/return/throw
1176// break => controlling loop (forwhile/do), and its related control flow
1177// return => all returns/throws from the same function
1178// When an inner block is selected, we include branches bound to outer blocks
1179// as these are exits from the inner block. e.g. return in a for loop.
1180// FIXME: We don't analyze catch blocks, throw is treated the same as return.
1181std::vector<SourceLocation> relatedControlFlow(const SelectionTree::Node &N) {
1182 const SourceManager &SM =
1183 N.getDeclContext().getParentASTContext().getSourceManager();
1184 std::vector<SourceLocation> Result;
1185
1186 // First, check if we're at a node that can resolve to a root.
1187 enum class Cur { None, Break, Continue, Return, Case, Throw } Cursor;
1188 if (N.ASTNode.get<BreakStmt>()) {
1189 Cursor = Cur::Break;
1190 } else if (N.ASTNode.get<ContinueStmt>()) {
1191 Cursor = Cur::Continue;
1192 } else if (N.ASTNode.get<ReturnStmt>()) {
1193 Cursor = Cur::Return;
1194 } else if (N.ASTNode.get<CXXThrowExpr>()) {
1195 Cursor = Cur::Throw;
1196 } else if (N.ASTNode.get<SwitchCase>()) {
1197 Cursor = Cur::Case;
1198 } else if (const GotoStmt *GS = N.ASTNode.get<GotoStmt>()) {
1199 // We don't know what root to associate with, but highlight the goto/label.
1200 Result.push_back(GS->getGotoLoc());
1201 if (const auto *LD = GS->getLabel())
1202 Result.push_back(LD->getLocation());
1203 Cursor = Cur::None;
1204 } else {
1205 Cursor = Cur::None;
1206 }
1207
1208 const Stmt *Root = nullptr; // Loop or function body to traverse.
1209 SourceRange Bounds;
1210 // Look up the tree for a root (or just at this node if we didn't find a leaf)
1211 for (const auto *P = &N; P; P = P->Parent) {
1212 // return associates with enclosing function
1213 if (const Stmt *FunctionBody = getFunctionBody(P->ASTNode)) {
1214 if (Cursor == Cur::Return || Cursor == Cur::Throw) {
1215 Root = FunctionBody;
1216 }
1217 break; // other leaves don't cross functions.
1218 }
1219 // break/continue associate with enclosing loop.
1220 if (const Stmt *LoopBody = getLoopBody(P->ASTNode)) {
1221 if (Cursor == Cur::None || Cursor == Cur::Break ||
1222 Cursor == Cur::Continue) {
1223 Root = LoopBody;
1224 // Highlight the loop keyword itself.
1225 // FIXME: for do-while, this only covers the `do`..
1226 Result.push_back(P->ASTNode.getSourceRange().getBegin());
1227 break;
1228 }
1229 }
1230 // For switches, users think of case statements as control flow blocks.
1231 // We highlight only occurrences surrounded by the same case.
1232 // We don't detect fallthrough (other than 'case X, case Y').
1233 if (const auto *SS = P->ASTNode.get<SwitchStmt>()) {
1234 if (Cursor == Cur::Break || Cursor == Cur::Case) {
1235 Result.push_back(SS->getSwitchLoc()); // Highlight the switch.
1236 Root = SS->getBody();
1237 // Limit to enclosing case, if there is one.
1238 Bounds = findCaseBounds(*SS, N.ASTNode.getSourceRange().getBegin(), SM);
1239 break;
1240 }
1241 }
1242 // If we didn't start at some interesting node, we're done.
1243 if (Cursor == Cur::None)
1244 break;
1245 }
1246 if (Root) {
1247 if (!Bounds.isValid())
1248 Bounds = Root->getSourceRange();
1249 FindControlFlow(Bounds, Result, SM).TraverseStmt(const_cast<Stmt *>(Root));
1250 }
1251 return Result;
1252}
1253
1254DocumentHighlight toHighlight(const ReferenceFinder::Reference &Ref,
1255 const SourceManager &SM) {
1257 DH.range = Ref.range(SM);
1258 if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write))
1260 else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read))
1262 else
1264 return DH;
1265}
1266
1267std::optional<DocumentHighlight> toHighlight(SourceLocation Loc,
1268 const syntax::TokenBuffer &TB) {
1269 Loc = TB.sourceManager().getFileLoc(Loc);
1270 if (const auto *Tok = TB.spelledTokenContaining(Loc)) {
1271 DocumentHighlight Result;
1272 Result.range = halfOpenToRange(
1273 TB.sourceManager(),
1274 CharSourceRange::getCharRange(Tok->location(), Tok->endLocation()));
1275 return Result;
1276 }
1277 return std::nullopt;
1278}
1279
1280} // namespace
1281
1282std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
1283 Position Pos) {
1284 const SourceManager &SM = AST.getSourceManager();
1285 // FIXME: show references to macro within file?
1286 auto CurLoc = sourceLocationInMainFile(SM, Pos);
1287 if (!CurLoc) {
1288 llvm::consumeError(CurLoc.takeError());
1289 return {};
1290 }
1291 std::vector<DocumentHighlight> Result;
1292 auto TryTree = [&](SelectionTree ST) {
1293 if (const SelectionTree::Node *N = ST.commonAncestor()) {
1294 DeclRelationSet Relations =
1296 auto TargetDecls =
1297 targetDecl(N->ASTNode, Relations, AST.getHeuristicResolver());
1298 if (!TargetDecls.empty()) {
1299 // FIXME: we may get multiple DocumentHighlights with the same location
1300 // and different kinds, deduplicate them.
1301 for (const auto &Ref : findRefs(TargetDecls, AST, /*PerToken=*/true))
1302 Result.push_back(toHighlight(Ref, SM));
1303 return true;
1304 }
1305 auto ControlFlow = relatedControlFlow(*N);
1306 if (!ControlFlow.empty()) {
1307 for (SourceLocation Loc : ControlFlow)
1308 if (auto Highlight = toHighlight(Loc, AST.getTokens()))
1309 Result.push_back(std::move(*Highlight));
1310 return true;
1311 }
1312 }
1313 return false;
1314 };
1315
1316 unsigned Offset =
1317 AST.getSourceManager().getDecomposedSpellingLoc(*CurLoc).second;
1318 SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Offset,
1319 Offset, TryTree);
1320 return Result;
1321}
1322
1323std::vector<LocatedSymbol> findImplementations(ParsedAST &AST, Position Pos,
1324 const SymbolIndex *Index) {
1325 // We rely on index to find the implementations in subclasses.
1326 // FIXME: Index can be stale, so we may loose some latest results from the
1327 // main file.
1328 if (!Index)
1329 return {};
1330 const SourceManager &SM = AST.getSourceManager();
1331 auto CurLoc = sourceLocationInMainFile(SM, Pos);
1332 if (!CurLoc) {
1333 elog("Failed to convert position to source location: {0}",
1334 CurLoc.takeError());
1335 return {};
1336 }
1337 DeclRelationSet Relations =
1339 llvm::DenseSet<SymbolID> IDs;
1341 for (const NamedDecl *ND : getDeclAtPosition(AST, *CurLoc, Relations)) {
1342 if (const auto *CXXMD = llvm::dyn_cast<CXXMethodDecl>(ND)) {
1343 if (CXXMD->isVirtual()) {
1344 IDs.insert(getSymbolID(ND));
1345 QueryKind = RelationKind::OverriddenBy;
1346 }
1347 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(ND)) {
1348 IDs.insert(getSymbolID(RD));
1349 QueryKind = RelationKind::BaseOf;
1350 } else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(ND)) {
1351 IDs.insert(getSymbolID(OMD));
1352 QueryKind = RelationKind::OverriddenBy;
1353 } else if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(ND)) {
1354 IDs.insert(getSymbolID(ID));
1355 QueryKind = RelationKind::BaseOf;
1356 }
1357 }
1358 return findImplementors(std::move(IDs), QueryKind, Index, AST.tuPath());
1359}
1360
1361namespace {
1362// Recursively finds all the overridden methods of `CMD` in complete type
1363// hierarchy.
1364void getOverriddenMethods(const CXXMethodDecl *CMD,
1365 llvm::DenseSet<SymbolID> &OverriddenMethods) {
1366 if (!CMD)
1367 return;
1368 for (const CXXMethodDecl *Base : CMD->overridden_methods()) {
1369 if (auto ID = getSymbolID(Base))
1370 OverriddenMethods.insert(ID);
1371 getOverriddenMethods(Base, OverriddenMethods);
1372 }
1373}
1374
1375// Recursively finds all the overridden methods of `OMD` in complete type
1376// hierarchy.
1377void getOverriddenMethods(const ObjCMethodDecl *OMD,
1378 llvm::DenseSet<SymbolID> &OverriddenMethods) {
1379 if (!OMD)
1380 return;
1381 llvm::SmallVector<const ObjCMethodDecl *, 4> Overrides;
1382 OMD->getOverriddenMethods(Overrides);
1383 for (const ObjCMethodDecl *Base : Overrides) {
1384 if (auto ID = getSymbolID(Base))
1385 OverriddenMethods.insert(ID);
1386 getOverriddenMethods(Base, OverriddenMethods);
1387 }
1388}
1389
1390std::optional<std::string>
1391stringifyContainerForMainFileRef(const Decl *Container) {
1392 // FIXME We might also want to display the signature here
1393 // When doing so, remember to also add the Signature to index results!
1394 if (auto *ND = llvm::dyn_cast_if_present<NamedDecl>(Container))
1395 return printQualifiedName(*ND);
1396 return {};
1397}
1398
1399std::optional<ReferencesResult>
1400maybeFindIncludeReferences(ParsedAST &AST, Position Pos,
1401 URIForFile URIMainFile) {
1402 const auto &Includes = AST.getIncludeStructure().MainFileIncludes;
1403 auto IncludeOnLine = llvm::find_if(Includes, [&Pos](const Inclusion &Inc) {
1404 return Inc.HashLine == Pos.line;
1405 });
1406 if (IncludeOnLine == Includes.end())
1407 return std::nullopt;
1408
1409 const SourceManager &SM = AST.getSourceManager();
1410 ReferencesResult Results;
1411 auto Converted = convertIncludes(AST);
1412 include_cleaner::walkUsed(
1413 AST.getLocalTopLevelDecls(), collectMacroReferences(AST),
1414 &AST.getPragmaIncludes(), AST.getPreprocessor(),
1415 [&](const include_cleaner::SymbolReference &Ref,
1416 llvm::ArrayRef<include_cleaner::Header> Providers) {
1417 if (Ref.RT != include_cleaner::RefType::Explicit ||
1418 !isPreferredProvider(*IncludeOnLine, Converted, Providers))
1419 return;
1420
1421 auto Loc = SM.getFileLoc(Ref.RefLocation);
1422 // File locations can be outside of the main file if macro is
1423 // expanded through an #include.
1424 while (SM.getFileID(Loc) != SM.getMainFileID())
1425 Loc = SM.getIncludeLoc(SM.getFileID(Loc));
1426
1427 ReferencesResult::Reference Result;
1428 const auto *Token = AST.getTokens().spelledTokenContaining(Loc);
1429 assert(Token && "references expected token here");
1430 Result.Loc.range = Range{sourceLocToPosition(SM, Token->location()),
1431 sourceLocToPosition(SM, Token->endLocation())};
1432 Result.Loc.uri = URIMainFile;
1433 Results.References.push_back(std::move(Result));
1434 });
1435 if (Results.References.empty())
1436 return std::nullopt;
1437
1438 // Add the #include line to the references list.
1440 Result.Loc.range = rangeTillEOL(SM.getBufferData(SM.getMainFileID()),
1441 IncludeOnLine->HashOffset);
1442 Result.Loc.uri = URIMainFile;
1443 Results.References.push_back(std::move(Result));
1444 return Results;
1445}
1446} // namespace
1447
1449 const SymbolIndex *Index, bool AddContext) {
1450 ReferencesResult Results;
1451 const SourceManager &SM = AST.getSourceManager();
1452 auto MainFilePath = AST.tuPath();
1453 auto URIMainFile = URIForFile::canonicalize(MainFilePath, MainFilePath);
1454 auto CurLoc = sourceLocationInMainFile(SM, Pos);
1455 if (!CurLoc) {
1456 llvm::consumeError(CurLoc.takeError());
1457 return {};
1458 }
1459
1460 const auto IncludeReferences =
1461 maybeFindIncludeReferences(AST, Pos, URIMainFile);
1462 if (IncludeReferences)
1463 return *IncludeReferences;
1464
1465 llvm::DenseSet<SymbolID> IDsToQuery, OverriddenMethods;
1466
1467 const auto *IdentifierAtCursor =
1468 syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens());
1469 std::optional<DefinedMacro> Macro;
1470 if (IdentifierAtCursor)
1471 Macro = locateMacroAt(*IdentifierAtCursor, AST.getPreprocessor());
1472 if (Macro) {
1473 // Handle references to macro.
1474 if (auto MacroSID = getSymbolID(Macro->Name, Macro->Info, SM)) {
1475 // Collect macro references from main file.
1476 const auto &IDToRefs = AST.getMacros().MacroRefs;
1477 auto Refs = IDToRefs.find(MacroSID);
1478 if (Refs != IDToRefs.end()) {
1479 for (const auto &Ref : Refs->second) {
1481 Result.Loc.range = Ref.toRange(SM);
1482 Result.Loc.uri = URIMainFile;
1483 if (Ref.IsDefinition) {
1486 }
1487 Results.References.push_back(std::move(Result));
1488 }
1489 }
1490 IDsToQuery.insert(MacroSID);
1491 }
1492 } else {
1493 // Handle references to Decls.
1494
1495 DeclRelationSet Relations =
1497 std::vector<const NamedDecl *> Decls =
1498 getDeclAtPosition(AST, *CurLoc, Relations);
1499 llvm::SmallVector<const NamedDecl *> TargetsInMainFile;
1500 for (const NamedDecl *D : Decls) {
1501 auto ID = getSymbolID(D);
1502 if (!ID)
1503 continue;
1504 TargetsInMainFile.push_back(D);
1505 // Not all symbols can be referenced from outside (e.g. function-locals).
1506 // TODO: we could skip TU-scoped symbols here (e.g. static functions) if
1507 // we know this file isn't a header. The details might be tricky.
1508 if (D->getParentFunctionOrMethod())
1509 continue;
1510 IDsToQuery.insert(ID);
1511 }
1512
1514 if (Index) {
1516 for (const NamedDecl *ND : Decls) {
1517 // Special case: For virtual methods, report decl/def of overrides and
1518 // references to all overridden methods in complete type hierarchy.
1519 if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(ND)) {
1520 if (CMD->isVirtual()) {
1521 if (auto ID = getSymbolID(CMD))
1522 OverriddenBy.Subjects.insert(ID);
1523 getOverriddenMethods(CMD, OverriddenMethods);
1524 }
1525 }
1526 // Special case: Objective-C methods can override a parent class or
1527 // protocol, we should be sure to report references to those.
1528 if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(ND)) {
1529 OverriddenBy.Subjects.insert(getSymbolID(OMD));
1530 getOverriddenMethods(OMD, OverriddenMethods);
1531 }
1532 }
1533 }
1534
1535 // We traverse the AST to find references in the main file.
1536 auto MainFileRefs = findRefs(TargetsInMainFile, AST, /*PerToken=*/false);
1537 // We may get multiple refs with the same location and different Roles, as
1538 // cross-reference is only interested in locations, we deduplicate them
1539 // by the location to avoid emitting duplicated locations.
1540 MainFileRefs.erase(llvm::unique(MainFileRefs,
1541 [](const ReferenceFinder::Reference &L,
1542 const ReferenceFinder::Reference &R) {
1543 return L.SpelledTok.location() ==
1544 R.SpelledTok.location();
1545 }),
1546 MainFileRefs.end());
1547 for (const auto &Ref : MainFileRefs) {
1549 Result.Loc.range = Ref.range(SM);
1550 Result.Loc.uri = URIMainFile;
1551 if (AddContext)
1552 Result.Loc.containerName =
1553 stringifyContainerForMainFileRef(Ref.Container);
1554 if (Ref.Role & static_cast<unsigned>(index::SymbolRole::Declaration))
1556 // clang-index doesn't report definitions as declarations, but they are.
1557 if (Ref.Role & static_cast<unsigned>(index::SymbolRole::Definition))
1558 Result.Attributes |=
1560 Results.References.push_back(std::move(Result));
1561 }
1562 // Add decl/def of overridding methods.
1563 if (Index && !OverriddenBy.Subjects.empty()) {
1564 LookupRequest ContainerLookup;
1565 // Different overrides will always be contained in different classes, so
1566 // we have a one-to-one mapping between SymbolID and index here, thus we
1567 // don't need to use std::vector as the map's value type.
1568 llvm::DenseMap<SymbolID, size_t> RefIndexForContainer;
1569 Index->relations(OverriddenBy, [&](const SymbolID &Subject,
1570 const Symbol &Object) {
1571 if (Limit && Results.References.size() >= Limit) {
1572 Results.HasMore = true;
1573 return;
1574 }
1575 const auto LSPLocDecl =
1576 toLSPLocation(Object.CanonicalDeclaration, MainFilePath);
1577 const auto LSPLocDef = toLSPLocation(Object.Definition, MainFilePath);
1578 if (LSPLocDecl && LSPLocDecl != LSPLocDef) {
1580 Result.Loc = {std::move(*LSPLocDecl), std::nullopt};
1581 Result.Attributes =
1583 RefIndexForContainer.insert({Object.ID, Results.References.size()});
1584 ContainerLookup.IDs.insert(Object.ID);
1585 Results.References.push_back(std::move(Result));
1586 }
1587 if (LSPLocDef) {
1589 Result.Loc = {std::move(*LSPLocDef), std::nullopt};
1593 RefIndexForContainer.insert({Object.ID, Results.References.size()});
1594 ContainerLookup.IDs.insert(Object.ID);
1595 Results.References.push_back(std::move(Result));
1596 }
1597 });
1598
1599 if (!ContainerLookup.IDs.empty() && AddContext)
1600 Index->lookup(ContainerLookup, [&](const Symbol &Container) {
1601 auto Ref = RefIndexForContainer.find(Container.ID);
1602 assert(Ref != RefIndexForContainer.end());
1603 Results.References[Ref->getSecond()].Loc.containerName =
1604 Container.Scope.str() + Container.Name.str();
1605 });
1606 }
1607 }
1608 // Now query the index for references from other files.
1609 auto QueryIndex = [&](llvm::DenseSet<SymbolID> IDs, bool AllowAttributes,
1610 bool AllowMainFileSymbols) {
1611 if (IDs.empty() || !Index || Results.HasMore)
1612 return;
1613 RefsRequest Req;
1614 Req.IDs = std::move(IDs);
1615 if (Limit) {
1616 if (Limit < Results.References.size()) {
1617 // We've already filled our quota, still check the index to correctly
1618 // return the `HasMore` info.
1619 Req.Limit = 0;
1620 } else {
1621 // Query index only for the remaining size.
1622 Req.Limit = Limit - Results.References.size();
1623 }
1624 }
1625 LookupRequest ContainerLookup;
1626 llvm::DenseMap<SymbolID, std::vector<size_t>> RefIndicesForContainer;
1627 Results.HasMore |= Index->refs(Req, [&](const Ref &R) {
1628 auto LSPLoc = toLSPLocation(R.Location, MainFilePath);
1629 // Avoid indexed results for the main file - the AST is authoritative.
1630 if (!LSPLoc ||
1631 (!AllowMainFileSymbols && LSPLoc->uri.file() == MainFilePath))
1632 return;
1634 Result.Loc = {std::move(*LSPLoc), std::nullopt};
1635 if (AllowAttributes) {
1638 // FIXME: our index should definitely store def | decl separately!
1640 Result.Attributes |=
1642 }
1643 if (AddContext) {
1644 SymbolID Container = R.Container;
1645 ContainerLookup.IDs.insert(Container);
1646 RefIndicesForContainer[Container].push_back(Results.References.size());
1647 }
1648 Results.References.push_back(std::move(Result));
1649 });
1650
1651 if (!ContainerLookup.IDs.empty() && AddContext)
1652 Index->lookup(ContainerLookup, [&](const Symbol &Container) {
1653 auto Ref = RefIndicesForContainer.find(Container.ID);
1654 assert(Ref != RefIndicesForContainer.end());
1655 auto ContainerName = Container.Scope.str() + Container.Name.str();
1656 for (auto I : Ref->getSecond()) {
1657 Results.References[I].Loc.containerName = ContainerName;
1658 }
1659 });
1660 };
1661 QueryIndex(std::move(IDsToQuery), /*AllowAttributes=*/true,
1662 /*AllowMainFileSymbols=*/false);
1663 // For a virtual method: Occurrences of BaseMethod should be treated as refs
1664 // and not as decl/def. Allow symbols from main file since AST does not report
1665 // these.
1666 QueryIndex(std::move(OverriddenMethods), /*AllowAttributes=*/false,
1667 /*AllowMainFileSymbols=*/true);
1668 return Results;
1669}
1670
1671std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos) {
1672 const SourceManager &SM = AST.getSourceManager();
1673 auto CurLoc = sourceLocationInMainFile(SM, Pos);
1674 if (!CurLoc) {
1675 llvm::consumeError(CurLoc.takeError());
1676 return {};
1677 }
1678 auto MainFilePath = AST.tuPath();
1679 std::vector<SymbolDetails> Results;
1680
1681 // We also want the targets of using-decls, so we include
1682 // DeclRelation::Underlying.
1685 for (const NamedDecl *D : getDeclAtPosition(AST, *CurLoc, Relations)) {
1686 D = getPreferredDecl(D);
1687
1688 SymbolDetails NewSymbol;
1689 std::string QName = printQualifiedName(*D);
1690 auto SplitQName = splitQualifiedName(QName);
1691 NewSymbol.containerName = std::string(SplitQName.first);
1692 NewSymbol.name = std::string(SplitQName.second);
1693
1694 if (NewSymbol.containerName.empty()) {
1695 if (const auto *ParentND =
1696 dyn_cast_or_null<NamedDecl>(D->getDeclContext()))
1697 NewSymbol.containerName = printQualifiedName(*ParentND);
1698 }
1699 llvm::SmallString<32> USR;
1700 if (!index::generateUSRForDecl(D, USR)) {
1701 NewSymbol.USR = std::string(USR);
1702 NewSymbol.ID = SymbolID(NewSymbol.USR);
1703 }
1704 if (const NamedDecl *Def = getDefinition(D))
1705 NewSymbol.definitionRange = makeLocation(
1706 AST.getASTContext(), nameLocation(*Def, SM), MainFilePath);
1707 NewSymbol.declarationRange =
1708 makeLocation(AST.getASTContext(), nameLocation(*D, SM), MainFilePath);
1709
1710 Results.push_back(std::move(NewSymbol));
1711 }
1712
1713 const auto *IdentifierAtCursor =
1714 syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens());
1715 if (!IdentifierAtCursor)
1716 return Results;
1717
1718 if (auto M = locateMacroAt(*IdentifierAtCursor, AST.getPreprocessor())) {
1719 SymbolDetails NewMacro;
1720 NewMacro.name = std::string(M->Name);
1721 llvm::SmallString<32> USR;
1722 if (!index::generateUSRForMacro(NewMacro.name, M->Info->getDefinitionLoc(),
1723 SM, USR)) {
1724 NewMacro.USR = std::string(USR);
1725 NewMacro.ID = SymbolID(NewMacro.USR);
1726 }
1727 Results.push_back(std::move(NewMacro));
1728 }
1729
1730 return Results;
1731}
1732
1733llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const LocatedSymbol &S) {
1734 OS << S.Name << ": " << S.PreferredDeclaration;
1735 if (S.Definition)
1736 OS << " def=" << *S.Definition;
1737 return OS;
1738}
1739
1740llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
1741 const ReferencesResult::Reference &R) {
1742 OS << R.Loc;
1744 OS << " [decl]";
1746 OS << " [def]";
1748 OS << " [override]";
1749 return OS;
1750}
1751
1752template <typename HierarchyItem>
1753static std::optional<HierarchyItem>
1754declToHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
1755 ASTContext &Ctx = ND.getASTContext();
1756 auto &SM = Ctx.getSourceManager();
1757 SourceLocation NameLoc = nameLocation(ND, Ctx.getSourceManager());
1758 SourceLocation BeginLoc = SM.getFileLoc(ND.getBeginLoc());
1759 SourceLocation EndLoc = SM.getFileLoc(ND.getEndLoc());
1760 const auto DeclRange =
1761 toHalfOpenFileRange(SM, Ctx.getLangOpts(), {BeginLoc, EndLoc});
1762 if (!DeclRange)
1763 return std::nullopt;
1764 const auto FE = SM.getFileEntryRefForID(SM.getFileID(NameLoc));
1765 if (!FE)
1766 return std::nullopt;
1767 auto FilePath = getCanonicalPath(*FE, SM.getFileManager());
1768 if (!FilePath)
1769 return std::nullopt; // Not useful without a uri.
1770
1771 Position NameBegin = sourceLocToPosition(SM, NameLoc);
1772 Position NameEnd = sourceLocToPosition(
1773 SM, Lexer::getLocForEndOfToken(NameLoc, 0, SM, Ctx.getLangOpts()));
1774
1775 index::SymbolInfo SymInfo = index::getSymbolInfo(&ND);
1776 // FIXME: This is not classifying constructors, destructors and operators
1777 // correctly.
1778 SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
1779
1780 HierarchyItem HI;
1781 HI.name = printName(Ctx, ND);
1782 // FIXME: Populate HI.detail the way we do in symbolToHierarchyItem?
1783 HI.kind = SK;
1784 HI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()),
1785 sourceLocToPosition(SM, DeclRange->getEnd())};
1786 HI.selectionRange = Range{NameBegin, NameEnd};
1787 if (!HI.range.contains(HI.selectionRange)) {
1788 // 'selectionRange' must be contained in 'range', so in cases where clang
1789 // reports unrelated ranges we need to reconcile somehow.
1790 HI.range = HI.selectionRange;
1791 }
1792
1793 HI.uri = URIForFile::canonicalize(*FilePath, TUPath);
1794
1795 return HI;
1796}
1797
1798static std::optional<TypeHierarchyItem>
1799declToTypeHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
1800 auto Result = declToHierarchyItem<TypeHierarchyItem>(ND, TUPath);
1801 if (Result) {
1802 Result->deprecated = ND.isDeprecated();
1803 // Compute the SymbolID and store it in the 'data' field.
1804 // This allows typeHierarchy/resolve to be used to
1805 // resolve children of items returned in a previous request
1806 // for parents.
1807 Result->data.symbolID = getSymbolID(&ND);
1808 }
1809 return Result;
1810}
1811
1812static std::optional<CallHierarchyItem>
1813declToCallHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
1814 auto Result = declToHierarchyItem<CallHierarchyItem>(ND, TUPath);
1815 if (!Result)
1816 return Result;
1817 if (ND.isDeprecated())
1818 Result->tags.push_back(SymbolTag::Deprecated);
1819 if (auto ID = getSymbolID(&ND))
1820 Result->data = ID.str();
1821 return Result;
1822}
1823
1824template <typename HierarchyItem>
1825static std::optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S,
1826 PathRef TUPath) {
1827 auto Loc = symbolToLocation(S, TUPath);
1828 if (!Loc) {
1829 elog("Failed to convert symbol to hierarchy item: {0}", Loc.takeError());
1830 return std::nullopt;
1831 }
1832 HierarchyItem HI;
1833 HI.name = std::string(S.Name);
1834 HI.detail = (S.Scope + S.Name).str();
1835 HI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind);
1836 HI.selectionRange = Loc->range;
1837 // FIXME: Populate 'range' correctly
1838 // (https://github.com/clangd/clangd/issues/59).
1839 HI.range = HI.selectionRange;
1840 HI.uri = Loc->uri;
1841
1842 return HI;
1843}
1844
1845static std::optional<TypeHierarchyItem>
1847 auto Result = symbolToHierarchyItem<TypeHierarchyItem>(S, TUPath);
1848 if (Result) {
1849 Result->deprecated = (S.Flags & Symbol::Deprecated);
1850 Result->data.symbolID = S.ID;
1851 }
1852 return Result;
1853}
1854
1855static std::optional<CallHierarchyItem>
1857 auto Result = symbolToHierarchyItem<CallHierarchyItem>(S, TUPath);
1858 if (!Result)
1859 return Result;
1860 Result->data = S.ID.str();
1861 if (S.Flags & Symbol::Deprecated)
1862 Result->tags.push_back(SymbolTag::Deprecated);
1863 return Result;
1864}
1865
1866static void fillSubTypes(const SymbolID &ID,
1867 std::vector<TypeHierarchyItem> &SubTypes,
1868 const SymbolIndex *Index, int Levels, PathRef TUPath) {
1869 RelationsRequest Req;
1870 Req.Subjects.insert(ID);
1872 Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
1873 if (std::optional<TypeHierarchyItem> ChildSym =
1875 if (Levels > 1) {
1876 ChildSym->children.emplace();
1877 fillSubTypes(Object.ID, *ChildSym->children, Index, Levels - 1, TUPath);
1878 }
1879 SubTypes.emplace_back(std::move(*ChildSym));
1880 }
1881 });
1882}
1883
1884using RecursionProtectionSet = llvm::SmallPtrSet<const CXXRecordDecl *, 4>;
1885
1886// Extracts parents from AST and populates the type hierarchy item.
1887static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath,
1888 TypeHierarchyItem &Item,
1889 RecursionProtectionSet &RPSet) {
1890 Item.parents.emplace();
1891 Item.data.parents.emplace();
1892 // typeParents() will replace dependent template specializations
1893 // with their class template, so to avoid infinite recursion for
1894 // certain types of hierarchies, keep the templates encountered
1895 // along the parent chain in a set, and stop the recursion if one
1896 // starts to repeat.
1897 auto *Pattern = CXXRD.getDescribedTemplate() ? &CXXRD : nullptr;
1898 if (Pattern) {
1899 if (!RPSet.insert(Pattern).second) {
1900 return;
1901 }
1902 }
1903
1904 for (const CXXRecordDecl *ParentDecl : typeParents(&CXXRD)) {
1905 if (std::optional<TypeHierarchyItem> ParentSym =
1906 declToTypeHierarchyItem(*ParentDecl, TUPath)) {
1907 fillSuperTypes(*ParentDecl, TUPath, *ParentSym, RPSet);
1908 Item.data.parents->emplace_back(ParentSym->data);
1909 Item.parents->emplace_back(std::move(*ParentSym));
1910 }
1911 }
1912
1913 if (Pattern) {
1914 RPSet.erase(Pattern);
1915 }
1916}
1917
1918std::vector<const CXXRecordDecl *> findRecordTypeAt(ParsedAST &AST,
1919 Position Pos) {
1920 auto RecordFromNode = [&AST](const SelectionTree::Node *N) {
1921 std::vector<const CXXRecordDecl *> Records;
1922 if (!N)
1923 return Records;
1924
1925 // Note: explicitReferenceTargets() will search for both template
1926 // instantiations and template patterns, and prefer the former if available
1927 // (generally, one will be available for non-dependent specializations of a
1928 // class template).
1929 auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying,
1930 AST.getHeuristicResolver());
1931 for (const NamedDecl *D : Decls) {
1932
1933 if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
1934 // If this is a variable, use the type of the variable.
1935 if (const auto *RD = VD->getType().getTypePtr()->getAsCXXRecordDecl())
1936 Records.push_back(RD);
1937 continue;
1938 }
1939
1940 if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
1941 // If this is a method, use the type of the class.
1942 Records.push_back(Method->getParent());
1943 continue;
1944 }
1945
1946 // We don't handle FieldDecl because it's not clear what behaviour
1947 // the user would expect: the enclosing class type (as with a
1948 // method), or the field's type (as with a variable).
1949
1950 if (auto *RD = dyn_cast<CXXRecordDecl>(D))
1951 Records.push_back(RD);
1952 }
1953 return Records;
1954 };
1955
1956 const SourceManager &SM = AST.getSourceManager();
1957 std::vector<const CXXRecordDecl *> Result;
1958 auto Offset = positionToOffset(SM.getBufferData(SM.getMainFileID()), Pos);
1959 if (!Offset) {
1960 llvm::consumeError(Offset.takeError());
1961 return Result;
1962 }
1963 SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), *Offset,
1964 *Offset, [&](SelectionTree ST) {
1965 Result = RecordFromNode(ST.commonAncestor());
1966 return !Result.empty();
1967 });
1968 return Result;
1969}
1970
1971// Return the type most associated with an AST node.
1972// This isn't precisely defined: we want "go to type" to do something useful.
1973static QualType typeForNode(const ASTContext &Ctx, const HeuristicResolver *H,
1974 const SelectionTree::Node *N) {
1975 // If we're looking at a namespace qualifier, walk up to what it's qualifying.
1976 // (If we're pointing at a *class* inside a NNS, N will be a TypeLoc).
1977 while (N && N->ASTNode.get<NestedNameSpecifierLoc>())
1978 N = N->Parent;
1979 if (!N)
1980 return QualType();
1981
1982 // If we're pointing at a type => return it.
1983 if (const TypeLoc *TL = N->ASTNode.get<TypeLoc>()) {
1984 if (llvm::isa<DeducedType>(TL->getTypePtr()))
1985 if (auto Deduced = getDeducedType(
1986 N->getDeclContext().getParentASTContext(), H, TL->getBeginLoc()))
1987 return *Deduced;
1988 // Exception: an alias => underlying type.
1989 if (llvm::isa<TypedefType>(TL->getTypePtr()))
1990 return TL->getTypePtr()->getLocallyUnqualifiedSingleStepDesugaredType();
1991 return TL->getType();
1992 }
1993
1994 // Constructor initializers => the type of thing being initialized.
1995 if (const auto *CCI = N->ASTNode.get<CXXCtorInitializer>()) {
1996 if (const FieldDecl *FD = CCI->getAnyMember())
1997 return FD->getType();
1998 if (const Type *Base = CCI->getBaseClass())
1999 return QualType(Base, 0);
2000 }
2001
2002 // Base specifier => the base type.
2003 if (const auto *CBS = N->ASTNode.get<CXXBaseSpecifier>())
2004 return CBS->getType();
2005
2006 if (const Decl *D = N->ASTNode.get<Decl>()) {
2007 struct Visitor : ConstDeclVisitor<Visitor, QualType> {
2008 const ASTContext &Ctx;
2009 Visitor(const ASTContext &Ctx) : Ctx(Ctx) {}
2010
2011 QualType VisitValueDecl(const ValueDecl *D) { return D->getType(); }
2012 // Declaration of a type => that type.
2013 QualType VisitTypeDecl(const TypeDecl *D) {
2014 return Ctx.getTypeDeclType(D);
2015 }
2016 // Exception: alias declaration => the underlying type, not the alias.
2017 QualType VisitTypedefNameDecl(const TypedefNameDecl *D) {
2018 return D->getUnderlyingType();
2019 }
2020 // Look inside templates.
2021 QualType VisitTemplateDecl(const TemplateDecl *D) {
2022 return Visit(D->getTemplatedDecl());
2023 }
2024 } V(Ctx);
2025 return V.Visit(D);
2026 }
2027
2028 if (const Stmt *S = N->ASTNode.get<Stmt>()) {
2029 struct Visitor : ConstStmtVisitor<Visitor, QualType> {
2030 // Null-safe version of visit simplifies recursive calls below.
2031 QualType type(const Stmt *S) { return S ? Visit(S) : QualType(); }
2032
2033 // In general, expressions => type of expression.
2034 QualType VisitExpr(const Expr *S) {
2035 return S->IgnoreImplicitAsWritten()->getType();
2036 }
2037 QualType VisitMemberExpr(const MemberExpr *S) {
2038 // The `foo` in `s.foo()` pretends not to have a real type!
2039 if (S->getType()->isSpecificBuiltinType(BuiltinType::BoundMember))
2040 return Expr::findBoundMemberType(S);
2041 return VisitExpr(S);
2042 }
2043 // Exceptions for void expressions that operate on a type in some way.
2044 QualType VisitCXXDeleteExpr(const CXXDeleteExpr *S) {
2045 return S->getDestroyedType();
2046 }
2047 QualType VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *S) {
2048 return S->getDestroyedType();
2049 }
2050 QualType VisitCXXThrowExpr(const CXXThrowExpr *S) {
2051 return S->getSubExpr()->getType();
2052 }
2053 QualType VisitCoyieldExpr(const CoyieldExpr *S) {
2054 return type(S->getOperand());
2055 }
2056 // Treat a designated initializer like a reference to the field.
2057 QualType VisitDesignatedInitExpr(const DesignatedInitExpr *S) {
2058 // In .foo.bar we want to jump to bar's type, so find *last* field.
2059 for (auto &D : llvm::reverse(S->designators()))
2060 if (D.isFieldDesignator())
2061 if (const auto *FD = D.getFieldDecl())
2062 return FD->getType();
2063 return QualType();
2064 }
2065
2066 // Control flow statements that operate on data: use the data type.
2067 QualType VisitSwitchStmt(const SwitchStmt *S) {
2068 return type(S->getCond());
2069 }
2070 QualType VisitWhileStmt(const WhileStmt *S) { return type(S->getCond()); }
2071 QualType VisitDoStmt(const DoStmt *S) { return type(S->getCond()); }
2072 QualType VisitIfStmt(const IfStmt *S) { return type(S->getCond()); }
2073 QualType VisitCaseStmt(const CaseStmt *S) { return type(S->getLHS()); }
2074 QualType VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
2075 return S->getLoopVariable()->getType();
2076 }
2077 QualType VisitReturnStmt(const ReturnStmt *S) {
2078 return type(S->getRetValue());
2079 }
2080 QualType VisitCoreturnStmt(const CoreturnStmt *S) {
2081 return type(S->getOperand());
2082 }
2083 QualType VisitCXXCatchStmt(const CXXCatchStmt *S) {
2084 return S->getCaughtType();
2085 }
2086 QualType VisitObjCAtThrowStmt(const ObjCAtThrowStmt *S) {
2087 return type(S->getThrowExpr());
2088 }
2089 QualType VisitObjCAtCatchStmt(const ObjCAtCatchStmt *S) {
2090 return S->getCatchParamDecl() ? S->getCatchParamDecl()->getType()
2091 : QualType();
2092 }
2093 } V;
2094 return V.Visit(S);
2095 }
2096
2097 return QualType();
2098}
2099
2100// Given a type targeted by the cursor, return one or more types that are more interesting
2101// to target.
2102static void unwrapFindType(
2103 QualType T, const HeuristicResolver* H, llvm::SmallVector<QualType>& Out) {
2104 if (T.isNull())
2105 return;
2106
2107 // If there's a specific type alias, point at that rather than unwrapping.
2108 if (const auto* TDT = T->getAs<TypedefType>())
2109 return Out.push_back(QualType(TDT, 0));
2110
2111 // Pointers etc => pointee type.
2112 if (const auto *PT = T->getAs<PointerType>())
2113 return unwrapFindType(PT->getPointeeType(), H, Out);
2114 if (const auto *RT = T->getAs<ReferenceType>())
2115 return unwrapFindType(RT->getPointeeType(), H, Out);
2116 if (const auto *AT = T->getAsArrayTypeUnsafe())
2117 return unwrapFindType(AT->getElementType(), H, Out);
2118
2119 // Function type => return type.
2120 if (auto *FT = T->getAs<FunctionType>())
2121 return unwrapFindType(FT->getReturnType(), H, Out);
2122 if (auto *CRD = T->getAsCXXRecordDecl()) {
2123 if (CRD->isLambda())
2124 return unwrapFindType(CRD->getLambdaCallOperator()->getReturnType(), H,
2125 Out);
2126 // FIXME: more cases we'd prefer the return type of the call operator?
2127 // std::function etc?
2128 }
2129
2130 // For smart pointer types, add the underlying type
2131 if (H)
2132 if (auto PointeeType = H->getPointeeType(T.getNonReferenceType());
2133 !PointeeType.isNull()) {
2134 unwrapFindType(PointeeType, H, Out);
2135 return Out.push_back(T);
2136 }
2137
2138 return Out.push_back(T);
2139}
2140
2141// Convenience overload, to allow calling this without the out-parameter
2142static llvm::SmallVector<QualType> unwrapFindType(
2143 QualType T, const HeuristicResolver* H) {
2144 llvm::SmallVector<QualType> Result;
2145 unwrapFindType(T, H, Result);
2146 return Result;
2147}
2148
2149std::vector<LocatedSymbol> findType(ParsedAST &AST, Position Pos,
2150 const SymbolIndex *Index) {
2151 const SourceManager &SM = AST.getSourceManager();
2152 auto Offset = positionToOffset(SM.getBufferData(SM.getMainFileID()), Pos);
2153 std::vector<LocatedSymbol> Result;
2154 if (!Offset) {
2155 elog("failed to convert position {0} for findTypes: {1}", Pos,
2156 Offset.takeError());
2157 return Result;
2158 }
2159 // The general scheme is: position -> AST node -> type -> declaration.
2160 auto SymbolsFromNode =
2161 [&](const SelectionTree::Node *N) -> std::vector<LocatedSymbol> {
2162 std::vector<LocatedSymbol> LocatedSymbols;
2163
2164 // NOTE: unwrapFindType might return duplicates for something like
2165 // unique_ptr<unique_ptr<T>>. Let's *not* remove them, because it gives you some
2166 // information about the type you may have not known before
2167 // (since unique_ptr<unique_ptr<T>> != unique_ptr<T>).
2168 for (const QualType &Type : unwrapFindType(
2169 typeForNode(AST.getASTContext(), AST.getHeuristicResolver(), N),
2170 AST.getHeuristicResolver()))
2171 llvm::copy(locateSymbolForType(AST, Type, Index),
2172 std::back_inserter(LocatedSymbols));
2173
2174 return LocatedSymbols;
2175 };
2176 SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), *Offset,
2177 *Offset, [&](SelectionTree ST) {
2178 Result = SymbolsFromNode(ST.commonAncestor());
2179 return !Result.empty();
2180 });
2181 return Result;
2182}
2183
2184std::vector<const CXXRecordDecl *> typeParents(const CXXRecordDecl *CXXRD) {
2185 std::vector<const CXXRecordDecl *> Result;
2186
2187 // If this is an invalid instantiation, instantiation of the bases
2188 // may not have succeeded, so fall back to the template pattern.
2189 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD)) {
2190 if (CTSD->isInvalidDecl())
2191 CXXRD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
2192 }
2193
2194 // Can't query bases without a definition.
2195 if (!CXXRD->hasDefinition())
2196 return Result;
2197
2198 for (auto Base : CXXRD->bases()) {
2199 const CXXRecordDecl *ParentDecl = nullptr;
2200
2201 const Type *Type = Base.getType().getTypePtr();
2202 if (const RecordType *RT = Type->getAs<RecordType>()) {
2203 ParentDecl = RT->getAsCXXRecordDecl();
2204 }
2205
2206 if (!ParentDecl) {
2207 // Handle a dependent base such as "Base<T>" by using the primary
2208 // template.
2209 if (const TemplateSpecializationType *TS =
2210 Type->getAs<TemplateSpecializationType>()) {
2211 TemplateName TN = TS->getTemplateName();
2212 if (TemplateDecl *TD = TN.getAsTemplateDecl()) {
2213 ParentDecl = dyn_cast<CXXRecordDecl>(TD->getTemplatedDecl());
2214 }
2215 }
2216 }
2217
2218 if (ParentDecl)
2219 Result.push_back(ParentDecl);
2220 }
2221
2222 return Result;
2223}
2224
2225std::vector<TypeHierarchyItem>
2226getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
2227 TypeHierarchyDirection Direction, const SymbolIndex *Index,
2228 PathRef TUPath) {
2229 std::vector<TypeHierarchyItem> Results;
2230 for (const auto *CXXRD : findRecordTypeAt(AST, Pos)) {
2231
2232 bool WantChildren = Direction == TypeHierarchyDirection::Children ||
2233 Direction == TypeHierarchyDirection::Both;
2234
2235 // If we're looking for children, we're doing the lookup in the index.
2236 // The index does not store relationships between implicit
2237 // specializations, so if we have one, use the template pattern instead.
2238 // Note that this needs to be done before the declToTypeHierarchyItem(),
2239 // otherwise the type hierarchy item would misleadingly contain the
2240 // specialization parameters, while the children would involve classes
2241 // that derive from other specializations of the template.
2242 if (WantChildren) {
2243 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
2244 CXXRD = CTSD->getTemplateInstantiationPattern();
2245 }
2246
2247 std::optional<TypeHierarchyItem> Result =
2248 declToTypeHierarchyItem(*CXXRD, AST.tuPath());
2249 if (!Result)
2250 continue;
2251
2253 fillSuperTypes(*CXXRD, AST.tuPath(), *Result, RPSet);
2254
2255 if (WantChildren && ResolveLevels > 0) {
2256 Result->children.emplace();
2257
2258 if (Index) {
2259 if (auto ID = getSymbolID(CXXRD))
2260 fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath);
2261 }
2262 }
2263 Results.emplace_back(std::move(*Result));
2264 }
2265
2266 return Results;
2267}
2268
2269std::optional<std::vector<TypeHierarchyItem>>
2270superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) {
2271 std::vector<TypeHierarchyItem> Results;
2272 if (!Item.data.parents)
2273 return std::nullopt;
2274 if (Item.data.parents->empty())
2275 return Results;
2276 LookupRequest Req;
2277 llvm::DenseMap<SymbolID, const TypeHierarchyItem::ResolveParams *> IDToData;
2278 for (const auto &Parent : *Item.data.parents) {
2279 Req.IDs.insert(Parent.symbolID);
2280 IDToData[Parent.symbolID] = &Parent;
2281 }
2282 Index->lookup(Req, [&Item, &Results, &IDToData](const Symbol &S) {
2283 if (auto THI = symbolToTypeHierarchyItem(S, Item.uri.file())) {
2284 THI->data = *IDToData.lookup(S.ID);
2285 Results.emplace_back(std::move(*THI));
2286 }
2287 });
2288 return Results;
2289}
2290
2291std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item,
2292 const SymbolIndex *Index) {
2293 std::vector<TypeHierarchyItem> Results;
2294 fillSubTypes(Item.data.symbolID, Results, Index, 1, Item.uri.file());
2295 for (auto &ChildSym : Results)
2296 ChildSym.data.parents = {Item.data};
2297 return Results;
2298}
2299
2300void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels,
2301 TypeHierarchyDirection Direction,
2302 const SymbolIndex *Index) {
2303 // We only support typeHierarchy/resolve for children, because for parents
2304 // we ignore ResolveLevels and return all levels of parents eagerly.
2305 if (!Index || Direction == TypeHierarchyDirection::Parents ||
2306 ResolveLevels == 0)
2307 return;
2308
2309 Item.children.emplace();
2310 fillSubTypes(Item.data.symbolID, *Item.children, Index, ResolveLevels,
2311 Item.uri.file());
2312}
2313
2314std::vector<CallHierarchyItem>
2316 std::vector<CallHierarchyItem> Result;
2317 const auto &SM = AST.getSourceManager();
2318 auto Loc = sourceLocationInMainFile(SM, Pos);
2319 if (!Loc) {
2320 elog("prepareCallHierarchy failed to convert position to source location: "
2321 "{0}",
2322 Loc.takeError());
2323 return Result;
2324 }
2325 for (const NamedDecl *Decl : getDeclAtPosition(AST, *Loc, {})) {
2326 if (!(isa<DeclContext>(Decl) &&
2327 cast<DeclContext>(Decl)->isFunctionOrMethod()) &&
2328 Decl->getKind() != Decl::Kind::FunctionTemplate &&
2329 !(Decl->getKind() == Decl::Kind::Var &&
2330 !cast<VarDecl>(Decl)->isLocalVarDecl()) &&
2331 Decl->getKind() != Decl::Kind::Field &&
2332 Decl->getKind() != Decl::Kind::EnumConstant)
2333 continue;
2334 if (auto CHI = declToCallHierarchyItem(*Decl, AST.tuPath()))
2335 Result.emplace_back(std::move(*CHI));
2336 }
2337 return Result;
2338}
2339
2340std::vector<CallHierarchyIncomingCall>
2341incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
2342 std::vector<CallHierarchyIncomingCall> Results;
2343 if (!Index || Item.data.empty())
2344 return Results;
2345 auto ID = SymbolID::fromStr(Item.data);
2346 if (!ID) {
2347 elog("incomingCalls failed to find symbol: {0}", ID.takeError());
2348 return Results;
2349 }
2350 // In this function, we find incoming calls based on the index only.
2351 // In principle, the AST could have more up-to-date information about
2352 // occurrences within the current file. However, going from a SymbolID
2353 // to an AST node isn't cheap, particularly when the declaration isn't
2354 // in the main file.
2355 // FIXME: Consider also using AST information when feasible.
2356 auto QueryIndex = [&](llvm::DenseSet<SymbolID> IDs, bool MightNeverCall) {
2357 RefsRequest Request;
2358 Request.IDs = std::move(IDs);
2359 Request.WantContainer = true;
2360 // We could restrict more specifically to calls by introducing a new
2361 // RefKind, but non-call references (such as address-of-function) can still
2362 // be interesting as they can indicate indirect calls.
2363 Request.Filter = RefKind::Reference;
2364 // Initially store the ranges in a map keyed by SymbolID of the caller.
2365 // This allows us to group different calls with the same caller
2366 // into the same CallHierarchyIncomingCall.
2367 llvm::DenseMap<SymbolID, std::vector<Location>> CallsIn;
2368 // We can populate the ranges based on a refs request only. As we do so, we
2369 // also accumulate the container IDs into a lookup request.
2370 LookupRequest ContainerLookup;
2371 Index->refs(Request, [&](const Ref &R) {
2372 auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
2373 if (!Loc) {
2374 elog("incomingCalls failed to convert location: {0}", Loc.takeError());
2375 return;
2376 }
2377 CallsIn[R.Container].push_back(*Loc);
2378
2379 ContainerLookup.IDs.insert(R.Container);
2380 });
2381 // Perform the lookup request and combine its results with CallsIn to
2382 // get complete CallHierarchyIncomingCall objects.
2383 Index->lookup(ContainerLookup, [&](const Symbol &Caller) {
2384 auto It = CallsIn.find(Caller.ID);
2385 assert(It != CallsIn.end());
2386 if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) {
2387 std::vector<Range> FromRanges;
2388 for (const Location &L : It->second) {
2389 if (L.uri != CHI->uri) {
2390 // Call location not in same file as caller.
2391 // This can happen in some edge cases. There's not much we can do,
2392 // since the protocol only allows returning ranges interpreted as
2393 // being in the caller's file.
2394 continue;
2395 }
2396 FromRanges.push_back(L.range);
2397 }
2398 Results.push_back(CallHierarchyIncomingCall{
2399 std::move(*CHI), std::move(FromRanges), MightNeverCall});
2400 }
2401 });
2402 };
2403 QueryIndex({ID.get()}, false);
2404 // In the case of being a virtual function we also want to return
2405 // potential calls through the base function.
2406 if (Item.kind == SymbolKind::Method) {
2407 llvm::DenseSet<SymbolID> IDs;
2408 RelationsRequest Req{{ID.get()}, RelationKind::OverriddenBy, std::nullopt};
2409 Index->reverseRelations(Req, [&](const SymbolID &, const Symbol &Caller) {
2410 IDs.insert(Caller.ID);
2411 });
2412 QueryIndex(std::move(IDs), true);
2413 }
2414 // Sort results by name of container.
2415 llvm::sort(Results, [](const CallHierarchyIncomingCall &A,
2416 const CallHierarchyIncomingCall &B) {
2417 return A.from.name < B.from.name;
2418 });
2419 return Results;
2420}
2421
2422std::vector<CallHierarchyOutgoingCall>
2423outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
2424 std::vector<CallHierarchyOutgoingCall> Results;
2425 if (!Index || Item.data.empty())
2426 return Results;
2427 auto ID = SymbolID::fromStr(Item.data);
2428 if (!ID) {
2429 elog("outgoingCalls failed to find symbol: {0}", ID.takeError());
2430 return Results;
2431 }
2432 // In this function, we find outgoing calls based on the index only.
2433 ContainedRefsRequest Request;
2434 Request.ID = *ID;
2435 // Initially store the ranges in a map keyed by SymbolID of the callee.
2436 // This allows us to group different calls to the same function
2437 // into the same CallHierarchyOutgoingCall.
2438 llvm::DenseMap<SymbolID, std::vector<Location>> CallsOut;
2439 // We can populate the ranges based on a refs request only. As we do so, we
2440 // also accumulate the callee IDs into a lookup request.
2441 LookupRequest CallsOutLookup;
2442 Index->containedRefs(Request, [&](const auto &R) {
2443 auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
2444 if (!Loc) {
2445 elog("outgoingCalls failed to convert location: {0}", Loc.takeError());
2446 return;
2447 }
2448 auto It = CallsOut.try_emplace(R.Symbol, std::vector<Location>{}).first;
2449 It->second.push_back(*Loc);
2450
2451 CallsOutLookup.IDs.insert(R.Symbol);
2452 });
2453 // Perform the lookup request and combine its results with CallsOut to
2454 // get complete CallHierarchyOutgoingCall objects.
2455 Index->lookup(CallsOutLookup, [&](const Symbol &Callee) {
2456 // The containedRefs request should only return symbols which are
2457 // function-like, i.e. symbols for which references to them can be "calls".
2458 using SK = index::SymbolKind;
2459 auto Kind = Callee.SymInfo.Kind;
2460 assert(Kind == SK::Function || Kind == SK::InstanceMethod ||
2461 Kind == SK::ClassMethod || Kind == SK::StaticMethod ||
2462 Kind == SK::Constructor || Kind == SK::Destructor ||
2463 Kind == SK::ConversionFunction);
2464 (void)Kind;
2465 (void)SK::Function;
2466
2467 auto It = CallsOut.find(Callee.ID);
2468 assert(It != CallsOut.end());
2469 if (auto CHI = symbolToCallHierarchyItem(Callee, Item.uri.file())) {
2470 std::vector<Range> FromRanges;
2471 for (const Location &L : It->second) {
2472 if (L.uri != Item.uri) {
2473 // Call location not in same file as the item that outgoingCalls was
2474 // requested for. This can happen when Item is a declaration separate
2475 // from the implementation. There's not much we can do, since the
2476 // protocol only allows returning ranges interpreted as being in
2477 // Item's file.
2478 continue;
2479 }
2480 FromRanges.push_back(L.range);
2481 }
2482 Results.push_back(
2483 CallHierarchyOutgoingCall{std::move(*CHI), std::move(FromRanges)});
2484 }
2485 });
2486 // Sort results by name of the callee.
2487 llvm::sort(Results, [](const CallHierarchyOutgoingCall &A,
2488 const CallHierarchyOutgoingCall &B) {
2489 return A.to.name < B.to.name;
2490 });
2491 return Results;
2492}
2493
2494llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
2495 const FunctionDecl *FD) {
2496 if (!FD->hasBody())
2497 return {};
2498 llvm::DenseSet<const Decl *> DeclRefs;
2500 FD,
2501 [&](ReferenceLoc Ref) {
2502 for (const Decl *D : Ref.Targets) {
2503 if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter() &&
2504 !Ref.IsDecl)
2505 DeclRefs.insert(D);
2506 }
2507 },
2508 AST.getHeuristicResolver());
2509 return DeclRefs;
2510}
2511
2512} // namespace clangd
2513} // namespace clang
Include Cleaner is clangd functionality for providing diagnostics for misuse of transitive headers an...
#define dlog(...)
Definition Logger.h:101
void elog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:61
Stores and provides access to parsed AST.
Definition ParsedAST.h:46
static bool createEach(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End, llvm::function_ref< bool(SelectionTree)> Func)
static const Decl * getRefContainer(const Decl *Enclosing, const SymbolCollector::Options &Opts)
static llvm::Expected< SymbolID > fromStr(llvm::StringRef)
Definition SymbolID.cpp:37
std::string str() const
Definition SymbolID.cpp:35
Interface for symbol indexes that can be used for searching or matching symbols among a set of symbol...
Definition Index.h:134
virtual bool fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref< void(const Symbol &)> Callback) const =0
Matches symbols in the index fuzzily and applies Callback on each matched symbol before returning.
virtual bool containedRefs(const ContainedRefsRequest &Req, llvm::function_ref< void(const ContainedRefsResult &)> Callback) const =0
Find all symbols that are referenced by a symbol and apply Callback on each result.
virtual void relations(const RelationsRequest &Req, llvm::function_ref< void(const SymbolID &Subject, const Symbol &Object)> Callback) const =0
Finds all relations (S, P, O) stored in the index such that S is among Req.Subjects and P is Req....
virtual bool refs(const RefsRequest &Req, llvm::function_ref< void(const Ref &)> Callback) const =0
Finds all occurrences (e.g.
virtual void lookup(const LookupRequest &Req, llvm::function_ref< void(const Symbol &)> Callback) const =0
Looks up symbols with any of the given symbol IDs and applies Callback on each matched symbol.
virtual void reverseRelations(const RelationsRequest &Req, llvm::function_ref< void(const SymbolID &Subject, const Symbol &Object)> Callback) const =0
Finds all relations (O, P, S) stored in the index such that S is among Req.Subjects and P is Req....
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:45
std::vector< TypeHierarchyItem > subTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index)
Returns direct children of a TypeHierarchyItem.
Definition XRefs.cpp:2291
std::pair< StringRef, StringRef > splitQualifiedName(StringRef QName)
std::optional< std::vector< TypeHierarchyItem > > superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index)
Returns direct parents of a TypeHierarchyItem using SymbolIDs stored inside the item.
Definition XRefs.cpp:2270
llvm::Expected< Location > indexToLSPLocation(const SymbolLocation &Loc, llvm::StringRef TUPath)
Helper function for deriving an LSP Location from an index SymbolLocation.
std::vector< CallHierarchyIncomingCall > incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index)
Definition XRefs.cpp:2341
SymbolID getSymbolID(const Decl *D)
Gets the symbol ID for a declaration. Returned SymbolID might be null.
Definition AST.cpp:354
static std::optional< TypeHierarchyItem > symbolToTypeHierarchyItem(const Symbol &S, PathRef TUPath)
Definition XRefs.cpp:1846
std::optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
static std::optional< CallHierarchyItem > symbolToCallHierarchyItem(const Symbol &S, PathRef TUPath)
Definition XRefs.cpp:1856
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R)
std::string printName(const ASTContext &Ctx, const NamedDecl &ND)
Prints unqualified name of the decl for the purpose of displaying it to the user.
Definition AST.cpp:248
llvm::SmallVector< std::pair< const NamedDecl *, DeclRelationSet >, 1 > allTargetDecls(const DynTypedNode &N, const HeuristicResolver *Resolver)
Similar to targetDecl(), however instead of applying a filter, all possible decls are returned along ...
std::vector< DocumentHighlight > findDocumentHighlights(ParsedAST &AST, Position Pos)
Returns highlights for all usages of a symbol at Pos.
Definition XRefs.cpp:1282
llvm::SmallVector< const NamedDecl *, 1 > explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask, const HeuristicResolver *Resolver)
Find declarations explicitly referenced in the source code defined by N.
std::vector< LocatedSymbol > locateSymbolTextually(const SpelledWord &Word, ParsedAST &AST, const SymbolIndex *Index, llvm::StringRef MainFilePath, ASTNodeKind NodeKind)
Definition XRefs.cpp:578
std::vector< DocumentLink > getDocumentLinks(ParsedAST &AST)
Get all document links.
Definition XRefs.cpp:860
Symbol mergeSymbol(const Symbol &L, const Symbol &R)
Definition Merge.cpp:266
std::vector< SymbolDetails > getSymbolInfo(ParsedAST &AST, Position Pos)
Get info about symbols at Pos.
Definition XRefs.cpp:1671
std::vector< include_cleaner::SymbolReference > collectMacroReferences(ParsedAST &AST)
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
llvm::Expected< Location > symbolToLocation(const Symbol &Sym, llvm::StringRef TUPath)
Helper function for deriving an LSP Location for a Symbol.
SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM)
Find the source location of the identifier for D.
Definition AST.cpp:197
void vlog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:72
include_cleaner::Includes convertIncludes(const ParsedAST &AST)
Converts the clangd include representation to include-cleaner include representation.
std::vector< LocatedSymbol > findType(ParsedAST &AST, Position Pos, const SymbolIndex *Index)
Returns symbols for types referenced at Pos.
Definition XRefs.cpp:2149
void findExplicitReferences(const Stmt *S, llvm::function_ref< void(ReferenceLoc)> Out, const HeuristicResolver *Resolver)
Recursively traverse S and report all references explicitly written in the code.
static QualType typeForNode(const ASTContext &Ctx, const HeuristicResolver *H, const SelectionTree::Node *N)
Definition XRefs.cpp:1973
std::vector< TypeHierarchyItem > getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, TypeHierarchyDirection Direction, const SymbolIndex *Index, PathRef TUPath)
Get type hierarchy information at Pos.
Definition XRefs.cpp:2226
static std::optional< TypeHierarchyItem > declToTypeHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath)
Definition XRefs.cpp:1799
std::optional< QualType > getDeducedType(ASTContext &ASTCtx, const HeuristicResolver *Resolver, SourceLocation Loc)
Retrieves the deduced type at a given location (auto, decltype).
Definition AST.cpp:623
llvm::SmallVector< const NamedDecl *, 1 > targetDecl(const DynTypedNode &N, DeclRelationSet Mask, const HeuristicResolver *Resolver)
targetDecl() finds the declaration referred to by an AST node.
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit, const SymbolIndex *Index, bool AddContext)
Returns references of the symbol at a specified Pos.
Definition XRefs.cpp:1448
static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath, TypeHierarchyItem &Item, RecursionProtectionSet &RPSet)
Definition XRefs.cpp:1887
std::optional< DefinedMacro > locateMacroAt(const syntax::Token &SpelledTok, Preprocessor &PP)
Gets the macro referenced by SpelledTok.
std::vector< LocatedSymbol > locateSymbolAt(ParsedAST &AST, Position Pos, const SymbolIndex *Index)
Get definition of symbol at a specified Pos.
Definition XRefs.cpp:779
std::vector< std::string > visibleNamespaces(llvm::StringRef Code, const LangOptions &LangOpts)
Heuristically determine namespaces visible at a point, without parsing Code.
static std::optional< HierarchyItem > declToHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath)
Definition XRefs.cpp:1754
std::optional< std::string > getCanonicalPath(const FileEntryRef F, FileManager &FileMgr)
Get the canonical path of F.
static void unwrapFindType(QualType T, const HeuristicResolver *H, llvm::SmallVector< QualType > &Out)
Definition XRefs.cpp:2102
static std::optional< HierarchyItem > symbolToHierarchyItem(const Symbol &S, PathRef TUPath)
Definition XRefs.cpp:1825
const syntax::Token * findNearbyIdentifier(const SpelledWord &Word, const syntax::TokenBuffer &TB)
Definition XRefs.cpp:688
llvm::SmallPtrSet< const CXXRecordDecl *, 4 > RecursionProtectionSet
Definition XRefs.cpp:1884
static void fillSubTypes(const SymbolID &ID, std::vector< TypeHierarchyItem > &SubTypes, const SymbolIndex *Index, int Levels, PathRef TUPath)
Definition XRefs.cpp:1866
void log(const char *Fmt, Ts &&... Vals)
Definition Logger.h:67
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind)
Definition Protocol.cpp:298
llvm::Expected< SourceLocation > sourceLocationInMainFile(const SourceManager &SM, Position P)
Return the file location, corresponding to P.
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition Path.h:29
@ Type
An inlay hint that for a type annotation.
Definition Protocol.h:1678
std::vector< LocatedSymbol > findImplementations(ParsedAST &AST, Position Pos, const SymbolIndex *Index)
Returns implementations at a specified Pos:
Definition XRefs.cpp:1323
const ObjCImplDecl * getCorrespondingObjCImpl(const ObjCContainerDecl *D)
Return the corresponding implementation/definition for the given ObjC container if it has one,...
Definition AST.cpp:371
void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, TypeHierarchyDirection Direction, const SymbolIndex *Index)
Definition XRefs.cpp:2300
llvm::DenseSet< const Decl * > getNonLocalDeclRefs(ParsedAST &AST, const FunctionDecl *FD)
Returns all decls that are referenced in the FD except local symbols.
Definition XRefs.cpp:2494
clangd::Range rangeTillEOL(llvm::StringRef Code, unsigned HashOffset)
Returns the range starting at offset and spanning the whole line.
float evaluateSymbolAndRelevance(float SymbolQuality, float SymbolRelevance)
Combine symbol quality and relevance into a single score.
Definition Quality.cpp:534
std::string printQualifiedName(const NamedDecl &ND)
Returns the qualified name of ND.
Definition AST.cpp:207
std::vector< CallHierarchyOutgoingCall > outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index)
Definition XRefs.cpp:2423
void elog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:61
@ Underlying
This is the underlying declaration for a renaming-alias, decltype etc.
Definition FindTarget.h:121
@ TemplatePattern
This is the pattern the template specialization was instantiated from.
Definition FindTarget.h:104
@ Alias
This declaration is an alias that was referred to.
Definition FindTarget.h:112
static std::optional< CallHierarchyItem > declToCallHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath)
Definition XRefs.cpp:1813
std::vector< const CXXRecordDecl * > findRecordTypeAt(ParsedAST &AST, Position Pos)
Find the record types referenced at Pos.
Definition XRefs.cpp:1918
std::vector< CallHierarchyItem > prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath)
Get call hierarchy information at Pos.
Definition XRefs.cpp:2315
std::vector< const CXXRecordDecl * > typeParents(const CXXRecordDecl *CXXRD)
Given a record type declaration, find its base (parent) types.
Definition XRefs.cpp:2184
SymbolKind
A symbol kind.
Definition Protocol.h:380
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Represents an incoming call, e.g. a caller of a method or constructor.
Definition Protocol.h:1623
Represents programming constructs like functions or constructors in the context of call hierarchy.
Definition Protocol.h:1583
URIForFile uri
The resource identifier of this item.
Definition Protocol.h:1597
SymbolKind kind
The kind of this item.
Definition Protocol.h:1588
std::string data
An optional 'data' field, which can be used to identify a call hierarchy item in an incomingCalls or ...
Definition Protocol.h:1610
Represents an outgoing call, e.g.
Definition Protocol.h:1648
A document highlight is a range inside a text document which deserves special attention.
Definition Protocol.h:1468
Range range
The range this highlight applies to.
Definition Protocol.h:1470
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:36
std::string Query
A query string for the fuzzy find.
Definition Index.h:29
std::vector< std::string > ProximityPaths
Contextually relevant files (e.g.
Definition Index.h:47
bool AnyScope
If set to true, allow symbols from any scope.
Definition Index.h:39
std::optional< uint32_t > Limit
The number of top candidates to return.
Definition Index.h:42
Location PreferredDeclaration
Definition XRefs.h:45
std::optional< Location > Definition
Definition XRefs.h:47
URIForFile uri
The text document's URI.
Definition Protocol.h:213
llvm::DenseSet< SymbolID > IDs
Definition Index.h:65
Represents a symbol occurrence in the source file.
Definition Ref.h:88
RefKind Kind
Definition Ref.h:91
SymbolID Container
The ID of the symbol whose definition contains this reference.
Definition Ref.h:95
SymbolLocation Location
The source location where the symbol is named.
Definition Ref.h:90
Information about a reference written in the source code, independent of the actual AST node that thi...
Definition FindTarget.h:128
std::optional< std::string > containerName
clangd extension: contains the name of the function or class in which the reference occurs
Definition Protocol.h:236
std::vector< Reference > References
Definition XRefs.h:94
bool WantContainer
If set, populates the container of the reference.
Definition Index.h:77
llvm::DenseSet< SymbolID > IDs
Definition Index.h:69
std::optional< uint32_t > Limit
If set, limit the number of refers returned from the index.
Definition Index.h:74
llvm::DenseSet< SymbolID > Subjects
Definition Index.h:94
const DeclContext & getDeclContext() const
static std::optional< SpelledWord > touching(SourceLocation SpelledLoc, const syntax::TokenBuffer &TB, const LangOptions &LangOpts)
const syntax::Token * ExpandedToken
Definition SourceCode.h:256
const syntax::Token * PartOfSpelledToken
Definition SourceCode.h:251
Represents information about identifier.
Definition Protocol.h:1168
std::optional< Location > definitionRange
Definition Protocol.h:1184
std::optional< Location > declarationRange
Definition Protocol.h:1182
std::string USR
Unified Symbol Resolution identifier This is an opaque string uniquely identifying a symbol.
Definition Protocol.h:1178
Attributes of a symbol that affect how much we like it.
Definition Quality.h:56
void merge(const CodeCompletionResult &SemaCCResult)
Definition Quality.cpp:178
Attributes of a symbol-query pair that affect how much we like it.
Definition Quality.h:86
llvm::StringRef Name
The name of the symbol (for ContextWords). Must be explicitly assigned.
Definition Quality.h:88
void merge(const CodeCompletionResult &SemaResult)
Definition Quality.cpp:329
enum clang::clangd::SymbolRelevanceSignals::QueryType Query
The class presents a C++ symbol, e.g.
Definition Symbol.h:39
SymbolFlag Flags
Definition Symbol.h:151
@ Deprecated
Indicates if the symbol is deprecated.
Definition Symbol.h:143
SymbolLocation Definition
The location of the symbol's definition, if one was found.
Definition Symbol.h:50
index::SymbolInfo SymInfo
The symbol information, like symbol kind.
Definition Symbol.h:43
llvm::StringRef Name
The unqualified name of the symbol, e.g. "bar" (for ns::bar).
Definition Symbol.h:45
llvm::StringRef Scope
The containing namespace. e.g. "" (global), "ns::" (top-level namespace).
Definition Symbol.h:47
llvm::StringRef TemplateSpecializationArgs
Argument list in human-readable format, will be displayed to help disambiguate between different spec...
Definition Symbol.h:72
SymbolLocation CanonicalDeclaration
The location of the preferred declaration of the symbol.
Definition Symbol.h:59
SymbolID ID
The ID of the symbol.
Definition Symbol.h:41
std::optional< std::vector< ResolveParams > > parents
std::nullopt means parents aren't resolved and empty is no parents.
Definition Protocol.h:1534
URIForFile uri
The resource identifier of this item.
Definition Protocol.h:1520
std::optional< std::vector< TypeHierarchyItem > > children
If this type hierarchy item is resolved, it contains the direct children of the current item.
Definition Protocol.h:1553
std::optional< std::vector< TypeHierarchyItem > > parents
This is a clangd exntesion.
Definition Protocol.h:1547
ResolveParams data
A data entry field that is preserved between a type hierarchy prepare and supertypes or subtypes requ...
Definition Protocol.h:1540
std::string uri() const
Definition Protocol.h:107
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
Definition Protocol.cpp:46
llvm::StringRef file() const
Retrieves absolute path to the file.
Definition Protocol.h:104
Represents measurements of clangd events, e.g.
Definition Trace.h:38
@ Counter
An aggregate number whose rate of change over time is meaningful.
Definition Trace.h:46