20#include "clang-include-cleaner/Analysis.h"
21#include "clang-include-cleaner/Types.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"
86const NamedDecl *getDefinition(
const NamedDecl *D) {
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())
102 auto *DeclCtx = cast<Decl>(MD->getDeclContext());
103 if (DeclCtx->isInvalidDecl())
106 if (
const auto *CD = dyn_cast<ObjCContainerDecl>(DeclCtx))
108 return Impl->getMethod(MD->getSelector(), MD->isInstanceMethod());
110 if (
const auto *CD = dyn_cast<ObjCContainerDecl>(D))
113 if (isa<ValueDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
114 isa<TemplateTemplateParmDecl>(D))
121 if (Loc.Start.hasOverflow() || Loc.End.hasOverflow())
122 log(
"Possible overflow in symbol location: {0}", Loc);
128 llvm::StringRef TUPath) {
133 elog(
"{0}", LSPLoc.takeError());
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);
154 std::string &Scratch) {
158 ASTSym.ID = IdxSym.ID =
SymbolID(
"mock_symbol_id");
159 ASTSym.CanonicalDeclaration = toIndexLocation(ASTLoc, Scratch);
160 IdxSym.CanonicalDeclaration = IdxLoc;
162 return Merged.CanonicalDeclaration;
165std::vector<std::pair<const NamedDecl *, DeclRelationSet>>
166getDeclAtPositionWithRelations(
ParsedAST &
AST, SourceLocation Pos,
168 ASTNodeKind *NodeKind =
nullptr) {
169 unsigned Offset =
AST.getSourceManager().getDecomposedSpellingLoc(Pos).second;
170 std::vector<std::pair<const NamedDecl *, DeclRelationSet>> Result;
174 *NodeKind = N->ASTNode.getNodeKind();
179 if (N->ASTNode.get<Attr>() && N->Parent)
182 std::back_inserter(Result),
183 [&](
auto &Entry) { return !(Entry.second & ~Relations); });
185 return !Result.empty();
188 Offset, ResultFromTree);
192std::vector<const NamedDecl *>
194 ASTNodeKind *NodeKind =
nullptr) {
195 std::vector<const NamedDecl *> Result;
197 getDeclAtPositionWithRelations(
AST, Pos, Relations, NodeKind))
198 Result.push_back(Entry.first);
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));
212 log(
"failed to get path!");
219 auto TokLen = Lexer::MeasureTokenLength(Loc, SM,
AST.getLangOpts());
221 SM, CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(TokLen)));
226std::optional<LocatedSymbol> locateFileReferent(
const Position &Pos,
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 = {
235 File.Definition =
File.PreferredDeclaration;
245std::optional<LocatedSymbol>
246locateMacroReferent(
const syntax::Token &TouchedIdentifier,
ParsedAST &
AST,
247 llvm::StringRef MainFilePath) {
250 makeLocation(
AST.getASTContext(), M->NameLoc, MainFilePath)) {
252 Macro.Name = std::string(M->Name);
253 Macro.PreferredDeclaration = *Loc;
254 Macro.Definition = Loc;
275const NamedDecl *getPreferredDecl(
const NamedDecl *D) {
280 D = llvm::cast<NamedDecl>(
D->getCanonicalDecl());
283 if (
const auto *ID = dyn_cast<ObjCInterfaceDecl>(D))
284 if (
const auto *DefinitionID = ID->getDefinition())
286 if (
const auto *PD = dyn_cast<ObjCProtocolDecl>(D))
287 if (
const auto *DefinitionID = PD->getDefinition())
293std::vector<LocatedSymbol> findImplementors(llvm::DenseSet<SymbolID> IDs,
296 llvm::StringRef MainFilePath) {
297 if (IDs.empty() || !Index)
303 FindImplementorsMetric.record(1,
"find-base");
306 FindImplementorsMetric.record(1,
"find-override");
312 Req.Subjects = std::move(IDs);
313 std::vector<LocatedSymbol> Results;
318 elog(
"Find overrides: {0}", DeclLoc.takeError());
321 Results.emplace_back();
322 Results.back().Name =
Object.Name.str();
323 Results.back().PreferredDeclaration = *DeclLoc;
326 elog(
"Failed to convert location: {0}", DefLoc.takeError());
329 Results.back().Definition = *DefLoc;
336void enhanceLocatedSymbolsFromIndex(llvm::MutableArrayRef<LocatedSymbol> Result,
338 llvm::StringRef MainFilePath) {
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);
347 if (!Index || QueryRequest.IDs.empty())
350 Index->lookup(QueryRequest, [&](
const Symbol &Sym) {
351 auto &R = Result[ResultIndex.lookup(Sym.ID)];
356 if (
auto Loc = toLSPLocation(Sym.CanonicalDeclaration, MainFilePath))
357 R.PreferredDeclaration = *Loc;
361 if (
auto Loc = toLSPLocation(
362 getPreferredLocation(*R.Definition, Sym.Definition, Scratch),
366 R.Definition = toLSPLocation(Sym.Definition, MainFilePath);
369 if (
auto Loc = toLSPLocation(
370 getPreferredLocation(R.PreferredDeclaration,
371 Sym.CanonicalDeclaration, Scratch),
373 R.PreferredDeclaration = *Loc;
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)
391std::vector<LocatedSymbol>
392locateASTReferent(SourceLocation CurLoc,
const syntax::Token *TouchedIdentifier,
395 const SourceManager &SM =
AST.getSourceManager();
397 std::vector<LocatedSymbol> Result;
401 auto AddResultDecl = [&](
const NamedDecl *
D) {
402 D = getPreferredDecl(D);
408 Result.emplace_back();
409 Result.back().Name =
printName(
AST.getASTContext(), *D);
410 Result.back().PreferredDeclaration = *Loc;
412 if (
const NamedDecl *Def = getDefinition(D))
413 Result.back().Definition = makeLocation(
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)) {
429 if (CMD->isPureVirtual()) {
430 if (TouchedIdentifier && SM.getSpellingLoc(CMD->getLocation()) ==
431 TouchedIdentifier->location()) {
433 LocateASTReferentMetric.record(1,
"method-to-override");
437 if (NodeKind.isSame(ASTNodeKind::getFromNodeKind<OverrideAttr>()) ||
438 NodeKind.isSame(ASTNodeKind::getFromNodeKind<FinalAttr>())) {
440 for (
const NamedDecl *ND : CMD->overridden_methods())
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");
473 SM.isPointWithin(TouchedIdentifier ? TouchedIdentifier->location()
475 D->getBeginLoc(),
D->getEndLoc()))
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());
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");
504 LocateASTReferentMetric.record(1,
"regular");
508 enhanceLocatedSymbolsFromIndex(Result, Index, MainFilePath);
511 Index, MainFilePath);
512 Result.insert(Result.end(), Overrides.begin(), Overrides.end());
516std::vector<LocatedSymbol> locateSymbolForType(
const ParsedAST &
AST,
517 const QualType &
Type,
519 const auto &SM =
AST.getSourceManager();
520 auto MainFilePath =
AST.tuPath();
524 auto Decls =
targetDecl(DynTypedNode::create(
Type.getNonReferenceType()),
526 AST.getHeuristicResolver());
530 std::vector<LocatedSymbol> Results;
531 const auto &ASTContext =
AST.getASTContext();
533 for (
const NamedDecl *D : Decls) {
534 D = getPreferredDecl(D);
536 auto Loc = makeLocation(ASTContext,
nameLocation(*D, SM), MainFilePath);
540 Results.emplace_back();
541 Results.back().Name =
printName(ASTContext, *D);
542 Results.back().PreferredDeclaration = *Loc;
544 if (
const NamedDecl *Def = getDefinition(D))
545 Results.back().Definition =
546 makeLocation(ASTContext,
nameLocation(*Def, SM), MainFilePath);
548 enhanceLocatedSymbolsFromIndex(Results, Index, MainFilePath);
553bool tokenSpelledAt(SourceLocation SpellingLoc,
const syntax::TokenBuffer &TB) {
554 auto ExpandedTokens = TB.expandedTokens(
555 TB.sourceManager().getMacroArgExpandedLocation(SpellingLoc));
556 return !ExpandedTokens.empty();
559llvm::StringRef sourcePrefix(SourceLocation Loc,
const SourceManager &SM) {
560 auto D = SM.getDecomposedLoc(Loc);
562 llvm::StringRef Buf = SM.getBufferData(
D.first, &Invalid);
563 if (Invalid ||
D.second > Buf.size())
565 return Buf.substr(0,
D.second);
568bool isDependentName(ASTNodeKind NodeKind) {
569 return NodeKind.isSame(ASTNodeKind::getFromNodeKind<OverloadExpr>()) ||
571 ASTNodeKind::getFromNodeKind<CXXDependentScopeMemberExpr>()) ||
573 ASTNodeKind::getFromNodeKind<DependentScopeDeclRefExpr>());
581 llvm::StringRef MainFilePath,
582 ASTNodeKind NodeKind) {
596 const auto &SM =
AST.getSourceManager();
610 bool TooMany =
false;
611 using ScoredLocatedSymbol = std::pair<float, LocatedSymbol>;
612 std::vector<ScoredLocatedSymbol> ScoredResults;
623 if (Sym.
SymInfo.Kind == index::SymbolKind::Constructor)
629 log(
"locateSymbolNamedTextuallyAt: {0}", MaybeDeclLoc.takeError());
639 log(
"locateSymbolNamedTextuallyAt: {0}", MaybeDefLoc.takeError());
646 if (ScoredResults.size() >= 5) {
659 Relevance.
merge(Sym);
662 dlog(
"locateSymbolNamedTextuallyAt: {0}{1} = {2}\n{3}{4}\n", Sym.
Scope,
663 Sym.
Name, Score, Quality, Relevance);
665 ScoredResults.push_back({Score, std::move(Located)});
669 vlog(
"Heuristic index lookup for {0} returned too many candidates, ignored",
674 llvm::sort(ScoredResults,
675 [](
const ScoredLocatedSymbol &A,
const ScoredLocatedSymbol &B) {
676 return A.first > B.first;
678 std::vector<LocatedSymbol> Results;
679 for (
auto &Res : std::move(ScoredResults))
680 Results.push_back(std::move(Res.second));
682 vlog(
"No heuristic index definition for {0}", Word.
Text);
684 log(
"Found definition heuristically in index for {0}", Word.
Text);
689 const syntax::TokenBuffer &TB) {
700 const SourceManager &SM = TB.sourceManager();
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);
710 const syntax::Token *BestTok =
nullptr;
711 unsigned BestCost = -1;
715 unsigned MaxDistance =
716 1U << std::min<unsigned>(Word.
Text.size(),
717 std::numeric_limits<unsigned>::digits - 1);
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());
733 auto Consider = [&](
const syntax::Token &Tok) {
734 if (Tok.location() < LocMin || Tok.location() > LocMax)
736 if (!(Tok.kind() == tok::identifier && Tok.text(SM) == Word.
Text))
739 if (Tok.location() == Word.
Location)
742 unsigned TokCost = Cost(Tok.location());
743 if (TokCost >= BestCost)
747 if (!(tokenSpelledAt(Tok.location(), TB) || TB.expansionStartingAt(&Tok)))
754 auto SpelledTokens = TB.spelledTokens(
File);
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;
761 for (
const syntax::Token &Tok : llvm::ArrayRef(I, SpelledTokens.end()))
765 for (
const syntax::Token &Tok :
766 llvm::reverse(llvm::ArrayRef(SpelledTokens.begin(), I)))
772 "Word {0} under cursor {1} isn't a token (after PP), trying nearby {2}",
774 BestTok->location().printToString(SM));
781 const auto &SM =
AST.getSourceManager();
782 auto MainFilePath =
AST.tuPath();
784 if (
auto File = locateFileReferent(Pos,
AST, MainFilePath))
785 return {std::move(*
File)};
789 elog(
"locateSymbolAt failed to convert position to source location: {0}",
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))
803 return {*std::move(
Macro)};
805 TouchedIdentifier = &Tok;
809 if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) {
815 auto LocSym = locateSymbolForType(
AST, *
Deduced, Index);
822 ASTNodeKind NodeKind;
823 auto ASTResults = locateASTReferent(*CurLoc, TouchedIdentifier,
AST,
824 MainFilePath, Index, NodeKind);
825 if (!ASTResults.empty())
833 if (
const syntax::Token *NearbyIdent =
835 if (
auto Macro = locateMacroReferent(*NearbyIdent,
AST, MainFilePath)) {
836 log(
"Found macro definition heuristically using nearby identifier {0}",
838 return {*std::move(
Macro)};
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));
847 vlog(
"No definition found using nearby identifier {0} at {1}", Word->Text,
848 Word->Location.printToString(SM));
851 auto TextualResults =
853 if (!TextualResults.empty())
854 return TextualResults;
861 const auto &SM =
AST.getSourceManager();
863 std::vector<DocumentLink> Result;
864 for (
auto &Inc :
AST.getIncludeStructure().MainFileIncludes) {
865 if (Inc.Resolved.empty())
869 auto HashLoc = SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset);
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);
884 CharSourceRange FileRange;
886 if (FileTok->kind() == tok::TokenKind::less) {
890 syntax::FileRange(SM, FileTok->location(), Inc.Written.length())
892 }
else if (FileTok->kind() == tok::TokenKind::string_literal) {
895 FileRange = FileTok->range(SM).toCharRange(SM);
906 FileRange = FileTok->range(SM).toCharRange(SM);
920class ReferenceFinder :
public index::IndexDataConsumer {
923 syntax::Token SpelledTok;
924 index::SymbolRoleSet Role;
925 const Decl *Container;
927 Range range(
const SourceManager &SM)
const {
932 ReferenceFinder(
const ParsedAST &AST,
933 const llvm::ArrayRef<const NamedDecl *> Targets,
935 : PerToken(PerToken), AST(AST) {
936 for (
const NamedDecl *ND : Targets)
937 TargetDecls.insert(ND->getCanonicalDecl());
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);
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);
955 return std::move(References);
959 handleDeclOccurrence(
const Decl *D, index::SymbolRoleSet Roles,
960 llvm::ArrayRef<index::SymbolRelation>
Relations,
962 index::IndexDataConsumer::ASTNodeInfo ASTNode)
override {
963 if (!TargetDecls.contains(
D->getCanonicalDecl()))
965 const SourceManager &SM = AST.getSourceManager();
968 const auto &TB = AST.getTokens();
970 llvm::SmallVector<SourceLocation, 1> Locs;
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);
982 if (!Locs.empty() && Locs.front() != Loc)
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(
1002 std::vector<Reference> References;
1003 const ParsedAST &AST;
1004 llvm::DenseSet<const Decl *> TargetDecls;
1007std::vector<ReferenceFinder::Reference>
1008findRefs(
const llvm::ArrayRef<const NamedDecl *> TargetDecls,
ParsedAST &
AST,
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();
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();
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();
1049class FindControlFlow :
public RecursiveASTVisitor<FindControlFlow> {
1058 All = Break | Continue | Return | Case | Throw | Goto,
1062 std::vector<SourceLocation> &Result;
1063 const SourceManager &SM;
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))
1073 else if (getLoopBody(D))
1074 Ignore |= Continue | Break;
1075 else if (
D.get<SwitchStmt>())
1076 Ignore |= Break | Case;
1078 return (Ignore == All) ? true : Delegate();
1081 void found(Target T, SourceLocation Loc) {
1084 if (SM.isBeforeInTranslationUnit(Loc, Bounds.getBegin()) ||
1085 SM.isBeforeInTranslationUnit(Bounds.getEnd(), Loc))
1087 Result.push_back(Loc);
1091 FindControlFlow(SourceRange Bounds, std::vector<SourceLocation> &Result,
1092 const SourceManager &SM)
1093 : Bounds(Bounds), Result(Result), SM(SM) {}
1097 bool TraverseDecl(Decl *D) {
1098 return !
D || filterAndTraverse(DynTypedNode::create(*D), [&] {
1099 return RecursiveASTVisitor::TraverseDecl(D);
1102 bool TraverseStmt(Stmt *S) {
1103 return !S || filterAndTraverse(DynTypedNode::create(*S), [&] {
1104 return RecursiveASTVisitor::TraverseStmt(S);
1109 bool VisitReturnStmt(ReturnStmt *R) {
1110 found(Return, R->getReturnLoc());
1113 bool VisitBreakStmt(BreakStmt *B) {
1114 found(Break,
B->getKwLoc());
1117 bool VisitContinueStmt(ContinueStmt *C) {
1118 found(Continue,
C->getKwLoc());
1121 bool VisitSwitchCase(SwitchCase *C) {
1122 found(Case,
C->getKeywordLoc());
1125 bool VisitCXXThrowExpr(CXXThrowExpr *T) {
1126 found(Throw,
T->getThrowLoc());
1129 bool VisitGotoStmt(GotoStmt *G) {
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());
1143SourceRange findCaseBounds(
const SwitchStmt &Switch, SourceLocation Loc,
1144 const SourceManager &SM) {
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());
1156 auto CaseAfter = llvm::partition_point(Cases, [&](
const SwitchCase *C) {
1157 return !SM.isBeforeInTranslationUnit(Loc,
C->getKeywordLoc());
1159 SourceLocation End = CaseAfter == Cases.end() ? Switch.getEndLoc()
1160 : (*CaseAfter)->getKeywordLoc();
1163 if (CaseAfter == Cases.begin())
1164 return SourceRange(Switch.getBeginLoc(), End);
1166 auto CaseBefore = std::prev(CaseAfter);
1168 while (CaseBefore != Cases.begin() &&
1169 (*std::prev(CaseBefore))->getSubStmt() == *CaseBefore)
1171 return SourceRange((*CaseBefore)->getKeywordLoc(), End);
1182 const SourceManager &SM =
1183 N.getDeclContext().getParentASTContext().getSourceManager();
1184 std::vector<SourceLocation> Result;
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>()) {
1198 }
else if (
const GotoStmt *GS = N.ASTNode.get<GotoStmt>()) {
1200 Result.push_back(GS->getGotoLoc());
1201 if (
const auto *LD = GS->getLabel())
1202 Result.push_back(LD->getLocation());
1208 const Stmt *Root =
nullptr;
1211 for (
const auto *P = &N;
P;
P =
P->Parent) {
1213 if (
const Stmt *FunctionBody = getFunctionBody(
P->ASTNode)) {
1214 if (Cursor == Cur::Return || Cursor == Cur::Throw) {
1215 Root = FunctionBody;
1220 if (
const Stmt *LoopBody = getLoopBody(
P->ASTNode)) {
1221 if (Cursor == Cur::None || Cursor == Cur::Break ||
1222 Cursor == Cur::Continue) {
1226 Result.push_back(
P->ASTNode.getSourceRange().getBegin());
1233 if (
const auto *SS =
P->ASTNode.get<SwitchStmt>()) {
1234 if (Cursor == Cur::Break || Cursor == Cur::Case) {
1235 Result.push_back(SS->getSwitchLoc());
1236 Root = SS->getBody();
1238 Bounds = findCaseBounds(*SS, N.ASTNode.getSourceRange().getBegin(), SM);
1243 if (Cursor == Cur::None)
1247 if (!Bounds.isValid())
1248 Bounds = Root->getSourceRange();
1249 FindControlFlow(Bounds, Result, SM).TraverseStmt(
const_cast<Stmt *
>(Root));
1255 const SourceManager &SM) {
1258 if (
Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write))
1260 else if (
Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read))
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)) {
1274 CharSourceRange::getCharRange(Tok->location(), Tok->endLocation()));
1277 return std::nullopt;
1284 const SourceManager &SM =
AST.getSourceManager();
1288 llvm::consumeError(CurLoc.takeError());
1291 std::vector<DocumentHighlight> Result;
1297 targetDecl(N->ASTNode, Relations,
AST.getHeuristicResolver());
1298 if (!TargetDecls.empty()) {
1301 for (
const auto &
Ref : findRefs(TargetDecls,
AST,
true))
1302 Result.push_back(toHighlight(
Ref, SM));
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));
1317 AST.getSourceManager().getDecomposedSpellingLoc(*CurLoc).second;
1330 const SourceManager &SM =
AST.getSourceManager();
1333 elog(
"Failed to convert position to source location: {0}",
1334 CurLoc.takeError());
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()) {
1347 }
else if (
const auto *RD = dyn_cast<CXXRecordDecl>(ND)) {
1350 }
else if (
const auto *OMD = dyn_cast<ObjCMethodDecl>(ND)) {
1353 }
else if (
const auto *ID = dyn_cast<ObjCInterfaceDecl>(ND)) {
1358 return findImplementors(std::move(IDs), QueryKind, Index,
AST.tuPath());
1364void getOverriddenMethods(
const CXXMethodDecl *CMD,
1365 llvm::DenseSet<SymbolID> &OverriddenMethods) {
1368 for (
const CXXMethodDecl *Base : CMD->overridden_methods()) {
1370 OverriddenMethods.insert(ID);
1371 getOverriddenMethods(Base, OverriddenMethods);
1377void getOverriddenMethods(
const ObjCMethodDecl *OMD,
1378 llvm::DenseSet<SymbolID> &OverriddenMethods) {
1381 llvm::SmallVector<const ObjCMethodDecl *, 4> Overrides;
1382 OMD->getOverriddenMethods(Overrides);
1383 for (
const ObjCMethodDecl *Base : Overrides) {
1385 OverriddenMethods.insert(ID);
1386 getOverriddenMethods(Base, OverriddenMethods);
1390std::optional<std::string>
1391stringifyContainerForMainFileRef(
const Decl *Container) {
1394 if (
auto *ND = llvm::dyn_cast_if_present<NamedDecl>(Container))
1399std::optional<ReferencesResult>
1402 const auto &Includes =
AST.getIncludeStructure().MainFileIncludes;
1403 auto IncludeOnLine = llvm::find_if(Includes, [&Pos](
const Inclusion &Inc) {
1404 return Inc.HashLine == Pos.line;
1406 if (IncludeOnLine == Includes.end())
1407 return std::nullopt;
1409 const SourceManager &SM =
AST.getSourceManager();
1412 include_cleaner::walkUsed(
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))
1421 auto Loc = SM.getFileLoc(Ref.RefLocation);
1424 while (SM.getFileID(Loc) != SM.getMainFileID())
1425 Loc = SM.getIncludeLoc(SM.getFileID(Loc));
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));
1435 if (Results.References.empty())
1436 return std::nullopt;
1441 IncludeOnLine->HashOffset);
1442 Result.Loc.uri = URIMainFile;
1443 Results.References.push_back(std::move(Result));
1451 const SourceManager &SM =
AST.getSourceManager();
1452 auto MainFilePath =
AST.tuPath();
1456 llvm::consumeError(CurLoc.takeError());
1460 const auto IncludeReferences =
1461 maybeFindIncludeReferences(
AST, Pos, URIMainFile);
1462 if (IncludeReferences)
1463 return *IncludeReferences;
1465 llvm::DenseSet<SymbolID> IDsToQuery, OverriddenMethods;
1467 const auto *IdentifierAtCursor =
1468 syntax::spelledIdentifierTouching(*CurLoc,
AST.getTokens());
1469 std::optional<DefinedMacro>
Macro;
1470 if (IdentifierAtCursor)
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) {
1482 Result.
Loc.
uri = URIMainFile;
1483 if (
Ref.IsDefinition) {
1487 Results.
References.push_back(std::move(Result));
1490 IDsToQuery.insert(MacroSID);
1497 std::vector<const NamedDecl *> Decls =
1498 getDeclAtPosition(
AST, *CurLoc, Relations);
1499 llvm::SmallVector<const NamedDecl *> TargetsInMainFile;
1500 for (
const NamedDecl *D : Decls) {
1504 TargetsInMainFile.push_back(D);
1508 if (D->getParentFunctionOrMethod())
1510 IDsToQuery.insert(ID);
1516 for (
const NamedDecl *ND : Decls) {
1519 if (
const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(ND)) {
1520 if (CMD->isVirtual()) {
1523 getOverriddenMethods(CMD, OverriddenMethods);
1528 if (
const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(ND)) {
1530 getOverriddenMethods(OMD, OverriddenMethods);
1536 auto MainFileRefs = findRefs(TargetsInMainFile,
AST,
false);
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();
1546 MainFileRefs.end());
1547 for (
const auto &
Ref : MainFileRefs) {
1550 Result.
Loc.
uri = URIMainFile;
1554 if (
Ref.Role &
static_cast<unsigned>(index::SymbolRole::Declaration))
1557 if (
Ref.Role &
static_cast<unsigned>(index::SymbolRole::Definition))
1560 Results.
References.push_back(std::move(Result));
1568 llvm::DenseMap<SymbolID, size_t> RefIndexForContainer;
1571 if (Limit && Results.
References.size() >= Limit) {
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};
1585 Results.
References.push_back(std::move(Result));
1589 Result.
Loc = {std::move(*LSPLocDef), std::nullopt};
1595 Results.
References.push_back(std::move(Result));
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());
1604 Container.Scope.str() + Container.Name.str();
1609 auto QueryIndex = [&](llvm::DenseSet<SymbolID> IDs,
bool AllowAttributes,
1610 bool AllowMainFileSymbols) {
1611 if (IDs.empty() || !Index || Results.
HasMore)
1614 Req.
IDs = std::move(IDs);
1626 llvm::DenseMap<SymbolID, std::vector<size_t>> RefIndicesForContainer;
1628 auto LSPLoc = toLSPLocation(R.
Location, MainFilePath);
1631 (!AllowMainFileSymbols && LSPLoc->uri.file() == MainFilePath))
1634 Result.
Loc = {std::move(*LSPLoc), std::nullopt};
1635 if (AllowAttributes) {
1645 ContainerLookup.
IDs.insert(Container);
1646 RefIndicesForContainer[Container].push_back(Results.
References.size());
1648 Results.
References.push_back(std::move(Result));
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;
1661 QueryIndex(std::move(IDsToQuery),
true,
1666 QueryIndex(std::move(OverriddenMethods),
false,
1672 const SourceManager &SM =
AST.getSourceManager();
1675 llvm::consumeError(CurLoc.takeError());
1678 auto MainFilePath =
AST.tuPath();
1679 std::vector<SymbolDetails> Results;
1685 for (
const NamedDecl *D : getDeclAtPosition(
AST, *CurLoc, Relations)) {
1686 D = getPreferredDecl(D);
1692 NewSymbol.
name = std::string(SplitQName.second);
1695 if (
const auto *ParentND =
1696 dyn_cast_or_null<NamedDecl>(D->getDeclContext()))
1699 llvm::SmallString<32> USR;
1700 if (!index::generateUSRForDecl(D, USR)) {
1701 NewSymbol.
USR = std::string(USR);
1704 if (
const NamedDecl *Def = getDefinition(D))
1708 makeLocation(
AST.getASTContext(),
nameLocation(*D, SM), MainFilePath);
1710 Results.push_back(std::move(NewSymbol));
1713 const auto *IdentifierAtCursor =
1714 syntax::spelledIdentifierTouching(*CurLoc,
AST.getTokens());
1715 if (!IdentifierAtCursor)
1720 NewMacro.
name = std::string(M->Name);
1721 llvm::SmallString<32> USR;
1722 if (!index::generateUSRForMacro(NewMacro.
name, M->Info->getDefinitionLoc(),
1724 NewMacro.
USR = std::string(USR);
1727 Results.push_back(std::move(NewMacro));
1748 OS <<
" [override]";
1752template <
typename HierarchyItem>
1753static std::optional<HierarchyItem>
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 =
1763 return std::nullopt;
1764 const auto FE = SM.getFileEntryRefForID(SM.getFileID(NameLoc));
1766 return std::nullopt;
1769 return std::nullopt;
1773 SM, Lexer::getLocForEndOfToken(NameLoc, 0, SM, Ctx.getLangOpts()));
1775 index::SymbolInfo SymInfo = index::getSymbolInfo(&ND);
1786 HI.selectionRange =
Range{NameBegin, NameEnd};
1787 if (!HI.range.contains(HI.selectionRange)) {
1790 HI.range = HI.selectionRange;
1798static std::optional<TypeHierarchyItem>
1802 Result->deprecated = ND.isDeprecated();
1812static std::optional<CallHierarchyItem>
1817 if (ND.isDeprecated())
1820 Result->data = ID.str();
1824template <
typename HierarchyItem>
1829 elog(
"Failed to convert symbol to hierarchy item: {0}", Loc.takeError());
1830 return std::nullopt;
1833 HI.name = std::string(S.
Name);
1836 HI.selectionRange = Loc->range;
1839 HI.range = HI.selectionRange;
1845static std::optional<TypeHierarchyItem>
1850 Result->data.symbolID = S.
ID;
1855static std::optional<CallHierarchyItem>
1860 Result->data = S.
ID.
str();
1867 std::vector<TypeHierarchyItem> &SubTypes,
1873 if (std::optional<TypeHierarchyItem> ChildSym =
1876 ChildSym->children.emplace();
1879 SubTypes.emplace_back(std::move(*ChildSym));
1897 auto *Pattern = CXXRD.getDescribedTemplate() ? &CXXRD :
nullptr;
1899 if (!RPSet.insert(Pattern).second) {
1904 for (
const CXXRecordDecl *ParentDecl :
typeParents(&CXXRD)) {
1905 if (std::optional<TypeHierarchyItem> ParentSym =
1909 Item.
parents->emplace_back(std::move(*ParentSym));
1914 RPSet.erase(Pattern);
1921 std::vector<const CXXRecordDecl *> Records;
1930 AST.getHeuristicResolver());
1931 for (
const NamedDecl *D : Decls) {
1933 if (
const VarDecl *VD = dyn_cast<VarDecl>(D)) {
1935 if (
const auto *RD = VD->getType().getTypePtr()->getAsCXXRecordDecl())
1936 Records.push_back(RD);
1940 if (
const CXXMethodDecl *
Method = dyn_cast<CXXMethodDecl>(D)) {
1942 Records.push_back(
Method->getParent());
1950 if (
auto *RD = dyn_cast<CXXRecordDecl>(D))
1951 Records.push_back(RD);
1956 const SourceManager &SM =
AST.getSourceManager();
1957 std::vector<const CXXRecordDecl *> Result;
1960 llvm::consumeError(Offset.takeError());
1965 Result = RecordFromNode(ST.commonAncestor());
1966 return !Result.empty();
1973static QualType
typeForNode(
const ASTContext &Ctx,
const HeuristicResolver *H,
1977 while (N && N->
ASTNode.get<NestedNameSpecifierLoc>())
1983 if (
const TypeLoc *TL = N->
ASTNode.get<TypeLoc>()) {
1984 if (llvm::isa<DeducedType>(TL->getTypePtr()))
1986 N->
getDeclContext().getParentASTContext(), H, TL->getBeginLoc()))
1989 if (llvm::isa<TypedefType>(TL->getTypePtr()))
1990 return TL->getTypePtr()->getLocallyUnqualifiedSingleStepDesugaredType();
1991 return TL->getType();
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);
2003 if (
const auto *CBS = N->
ASTNode.get<CXXBaseSpecifier>())
2004 return CBS->getType();
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) {}
2011 QualType VisitValueDecl(
const ValueDecl *D) {
return D->getType(); }
2013 QualType VisitTypeDecl(
const TypeDecl *D) {
2014 return Ctx.getTypeDeclType(D);
2017 QualType VisitTypedefNameDecl(
const TypedefNameDecl *D) {
2018 return D->getUnderlyingType();
2021 QualType VisitTemplateDecl(
const TemplateDecl *D) {
2022 return Visit(D->getTemplatedDecl());
2028 if (
const Stmt *S = N->
ASTNode.get<Stmt>()) {
2029 struct Visitor : ConstStmtVisitor<Visitor, QualType> {
2031 QualType type(
const Stmt *S) {
return S ? Visit(S) : QualType(); }
2034 QualType VisitExpr(
const Expr *S) {
2035 return S->IgnoreImplicitAsWritten()->getType();
2037 QualType VisitMemberExpr(
const MemberExpr *S) {
2039 if (S->getType()->isSpecificBuiltinType(BuiltinType::BoundMember))
2040 return Expr::findBoundMemberType(S);
2041 return VisitExpr(S);
2044 QualType VisitCXXDeleteExpr(
const CXXDeleteExpr *S) {
2045 return S->getDestroyedType();
2047 QualType VisitCXXPseudoDestructorExpr(
const CXXPseudoDestructorExpr *S) {
2048 return S->getDestroyedType();
2050 QualType VisitCXXThrowExpr(
const CXXThrowExpr *S) {
2051 return S->getSubExpr()->getType();
2053 QualType VisitCoyieldExpr(
const CoyieldExpr *S) {
2054 return type(S->getOperand());
2057 QualType VisitDesignatedInitExpr(
const DesignatedInitExpr *S) {
2059 for (
auto &D : llvm::reverse(S->designators()))
2060 if (D.isFieldDesignator())
2061 if (
const auto *FD = D.getFieldDecl())
2062 return FD->getType();
2067 QualType VisitSwitchStmt(
const SwitchStmt *S) {
2068 return type(S->getCond());
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();
2077 QualType VisitReturnStmt(
const ReturnStmt *S) {
2078 return type(S->getRetValue());
2080 QualType VisitCoreturnStmt(
const CoreturnStmt *S) {
2081 return type(S->getOperand());
2083 QualType VisitCXXCatchStmt(
const CXXCatchStmt *S) {
2084 return S->getCaughtType();
2086 QualType VisitObjCAtThrowStmt(
const ObjCAtThrowStmt *S) {
2087 return type(S->getThrowExpr());
2089 QualType VisitObjCAtCatchStmt(
const ObjCAtCatchStmt *S) {
2090 return S->getCatchParamDecl() ? S->getCatchParamDecl()->getType()
2103 QualType T,
const HeuristicResolver* H, llvm::SmallVector<QualType>& Out) {
2108 if (
const auto* TDT = T->getAs<TypedefType>())
2109 return Out.push_back(QualType(TDT, 0));
2112 if (
const auto *PT = T->getAs<PointerType>())
2114 if (
const auto *RT = T->getAs<ReferenceType>())
2116 if (
const auto *AT = T->getAsArrayTypeUnsafe())
2120 if (
auto *FT = T->getAs<FunctionType>())
2122 if (
auto *CRD = T->getAsCXXRecordDecl()) {
2123 if (CRD->isLambda())
2124 return unwrapFindType(CRD->getLambdaCallOperator()->getReturnType(), H,
2132 if (
auto PointeeType = H->getPointeeType(T.getNonReferenceType());
2133 !PointeeType.isNull()) {
2135 return Out.push_back(T);
2138 return Out.push_back(T);
2143 QualType T,
const HeuristicResolver* H) {
2144 llvm::SmallVector<QualType> Result;
2151 const SourceManager &SM =
AST.getSourceManager();
2153 std::vector<LocatedSymbol> Result;
2155 elog(
"failed to convert position {0} for findTypes: {1}", Pos,
2156 Offset.takeError());
2160 auto SymbolsFromNode =
2162 std::vector<LocatedSymbol> LocatedSymbols;
2170 AST.getHeuristicResolver()))
2171 llvm::copy(locateSymbolForType(
AST,
Type, Index),
2172 std::back_inserter(LocatedSymbols));
2174 return LocatedSymbols;
2178 Result = SymbolsFromNode(ST.commonAncestor());
2179 return !Result.empty();
2184std::vector<const CXXRecordDecl *>
typeParents(
const CXXRecordDecl *CXXRD) {
2185 std::vector<const CXXRecordDecl *> Result;
2189 if (
auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD)) {
2190 if (CTSD->isInvalidDecl())
2191 CXXRD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
2195 if (!CXXRD->hasDefinition())
2198 for (
auto Base : CXXRD->bases()) {
2199 const CXXRecordDecl *ParentDecl =
nullptr;
2201 const Type *
Type = Base.getType().getTypePtr();
2202 if (
const RecordType *RT =
Type->getAs<RecordType>()) {
2203 ParentDecl = RT->getAsCXXRecordDecl();
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());
2219 Result.push_back(ParentDecl);
2225std::vector<TypeHierarchyItem>
2229 std::vector<TypeHierarchyItem> Results;
2243 if (
auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
2244 CXXRD = CTSD->getTemplateInstantiationPattern();
2247 std::optional<TypeHierarchyItem> Result =
2255 if (WantChildren && ResolveLevels > 0) {
2256 Result->children.emplace();
2260 fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath);
2263 Results.emplace_back(std::move(*Result));
2269std::optional<std::vector<TypeHierarchyItem>>
2271 std::vector<TypeHierarchyItem> Results;
2273 return std::nullopt;
2277 llvm::DenseMap<SymbolID, const TypeHierarchyItem::ResolveParams *> IDToData;
2279 Req.
IDs.insert(Parent.symbolID);
2280 IDToData[Parent.symbolID] = &Parent;
2282 Index->
lookup(Req, [&Item, &Results, &IDToData](
const Symbol &S) {
2284 THI->data = *IDToData.lookup(S.
ID);
2285 Results.emplace_back(std::move(*THI));
2293 std::vector<TypeHierarchyItem> Results;
2295 for (
auto &ChildSym : Results)
2296 ChildSym.data.parents = {Item.
data};
2314std::vector<CallHierarchyItem>
2316 std::vector<CallHierarchyItem> Result;
2317 const auto &SM =
AST.getSourceManager();
2320 elog(
"prepareCallHierarchy failed to convert position to source location: "
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)
2335 Result.emplace_back(std::move(*CHI));
2340std::vector<CallHierarchyIncomingCall>
2342 std::vector<CallHierarchyIncomingCall> Results;
2343 if (!Index || Item.
data.empty())
2347 elog(
"incomingCalls failed to find symbol: {0}", ID.takeError());
2356 auto QueryIndex = [&](llvm::DenseSet<SymbolID> IDs,
bool MightNeverCall) {
2358 Request.
IDs = std::move(IDs);
2367 llvm::DenseMap<SymbolID, std::vector<Location>> CallsIn;
2371 Index->
refs(Request, [&](
const Ref &R) {
2374 elog(
"incomingCalls failed to convert location: {0}", Loc.takeError());
2383 Index->
lookup(ContainerLookup, [&](
const Symbol &Caller) {
2384 auto It = CallsIn.find(Caller.
ID);
2385 assert(It != CallsIn.end());
2387 std::vector<Range> FromRanges;
2388 for (
const Location &L : It->second) {
2396 FromRanges.push_back(L.
range);
2399 std::move(*CHI), std::move(FromRanges), MightNeverCall});
2403 QueryIndex({ID.get()},
false);
2406 if (Item.
kind == SymbolKind::Method) {
2407 llvm::DenseSet<SymbolID> IDs;
2410 IDs.insert(Caller.
ID);
2412 QueryIndex(std::move(IDs),
true);
2417 return A.from.name < B.from.name;
2422std::vector<CallHierarchyOutgoingCall>
2424 std::vector<CallHierarchyOutgoingCall> Results;
2425 if (!Index || Item.
data.empty())
2429 elog(
"outgoingCalls failed to find symbol: {0}", ID.takeError());
2438 llvm::DenseMap<SymbolID, std::vector<Location>> CallsOut;
2445 elog(
"outgoingCalls failed to convert location: {0}", Loc.takeError());
2448 auto It = CallsOut.try_emplace(R.Symbol, std::vector<Location>{}).first;
2449 It->second.push_back(*Loc);
2451 CallsOutLookup.
IDs.insert(R.Symbol);
2455 Index->
lookup(CallsOutLookup, [&](
const Symbol &Callee) {
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);
2467 auto It = CallsOut.find(Callee.ID);
2468 assert(It != CallsOut.end());
2470 std::vector<Range> FromRanges;
2471 for (
const Location &L : It->second) {
2480 FromRanges.push_back(L.
range);
2489 return A.to.name < B.to.name;
2495 const FunctionDecl *FD) {
2498 llvm::DenseSet<const Decl *> DeclRefs;
2502 for (
const Decl *D :
Ref.Targets) {
2503 if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter() &&
2508 AST.getHeuristicResolver());
Include Cleaner is clangd functionality for providing diagnostics for misuse of transitive headers an...
void elog(const char *Fmt, Ts &&... Vals)
Stores and provides access to parsed AST.
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)
Interface for symbol indexes that can be used for searching or matching symbols among a set of symbol...
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.
std::vector< TypeHierarchyItem > subTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index)
Returns direct children of a TypeHierarchyItem.
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.
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)
SymbolID getSymbolID(const Decl *D)
Gets the symbol ID for a declaration. Returned SymbolID might be null.
static std::optional< TypeHierarchyItem > symbolToTypeHierarchyItem(const Symbol &S, PathRef TUPath)
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)
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.
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.
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)
std::vector< DocumentLink > getDocumentLinks(ParsedAST &AST)
Get all document links.
Symbol mergeSymbol(const Symbol &L, const Symbol &R)
std::vector< SymbolDetails > getSymbolInfo(ParsedAST &AST, Position Pos)
Get info about symbols at Pos.
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.
void vlog(const char *Fmt, Ts &&... Vals)
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.
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)
std::vector< TypeHierarchyItem > getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels, TypeHierarchyDirection Direction, const SymbolIndex *Index, PathRef TUPath)
Get type hierarchy information at Pos.
static std::optional< TypeHierarchyItem > declToTypeHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath)
std::optional< QualType > getDeducedType(ASTContext &ASTCtx, const HeuristicResolver *Resolver, SourceLocation Loc)
Retrieves the deduced type at a given location (auto, decltype).
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.
static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath, TypeHierarchyItem &Item, RecursionProtectionSet &RPSet)
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.
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)
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)
static std::optional< HierarchyItem > symbolToHierarchyItem(const Symbol &S, PathRef TUPath)
const syntax::Token * findNearbyIdentifier(const SpelledWord &Word, const syntax::TokenBuffer &TB)
llvm::SmallPtrSet< const CXXRecordDecl *, 4 > RecursionProtectionSet
static void fillSubTypes(const SymbolID &ID, std::vector< TypeHierarchyItem > &SubTypes, const SymbolIndex *Index, int Levels, PathRef TUPath)
void log(const char *Fmt, Ts &&... Vals)
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)
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.
@ Type
An inlay hint that for a type annotation.
std::vector< LocatedSymbol > findImplementations(ParsedAST &AST, Position Pos, const SymbolIndex *Index)
Returns implementations at a specified Pos:
const ObjCImplDecl * getCorrespondingObjCImpl(const ObjCContainerDecl *D)
Return the corresponding implementation/definition for the given ObjC container if it has one,...
void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels, TypeHierarchyDirection Direction, const SymbolIndex *Index)
llvm::DenseSet< const Decl * > getNonLocalDeclRefs(ParsedAST &AST, const FunctionDecl *FD)
Returns all decls that are referenced in the FD except local symbols.
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.
std::string printQualifiedName(const NamedDecl &ND)
Returns the qualified name of ND.
std::vector< CallHierarchyOutgoingCall > outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index)
void elog(const char *Fmt, Ts &&... Vals)
@ Underlying
This is the underlying declaration for a renaming-alias, decltype etc.
@ TemplatePattern
This is the pattern the template specialization was instantiated from.
@ Alias
This declaration is an alias that was referred to.
static std::optional< CallHierarchyItem > declToCallHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath)
std::vector< const CXXRecordDecl * > findRecordTypeAt(ParsedAST &AST, Position Pos)
Find the record types referenced at Pos.
std::vector< CallHierarchyItem > prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath)
Get call hierarchy information at Pos.
std::vector< const CXXRecordDecl * > typeParents(const CXXRecordDecl *CXXRD)
Given a record type declaration, find its base (parent) types.
@ Invalid
Sentinel bit pattern. DO NOT USE!
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Represents an incoming call, e.g. a caller of a method or constructor.
Represents programming constructs like functions or constructors in the context of call hierarchy.
URIForFile uri
The resource identifier of this item.
SymbolKind kind
The kind of this item.
std::string data
An optional 'data' field, which can be used to identify a call hierarchy item in an incomingCalls or ...
Represents an outgoing call, e.g.
A document highlight is a range inside a text document which deserves special attention.
Range range
The range this highlight applies to.
A range in a text document that links to an internal or external resource, like another text document...
std::vector< std::string > Scopes
If this is non-empty, symbols must be in at least one of the scopes (e.g.
std::string Query
A query string for the fuzzy find.
std::vector< std::string > ProximityPaths
Contextually relevant files (e.g.
bool AnyScope
If set to true, allow symbols from any scope.
std::optional< uint32_t > Limit
The number of top candidates to return.
Location PreferredDeclaration
std::optional< Location > Definition
URIForFile uri
The text document's URI.
llvm::DenseSet< SymbolID > IDs
Represents a symbol occurrence in the source file.
SymbolID Container
The ID of the symbol whose definition contains this reference.
SymbolLocation Location
The source location where the symbol is named.
Information about a reference written in the source code, independent of the actual AST node that thi...
std::optional< std::string > containerName
clangd extension: contains the name of the function or class in which the reference occurs
std::vector< Reference > References
bool WantContainer
If set, populates the container of the reference.
llvm::DenseSet< SymbolID > IDs
std::optional< uint32_t > Limit
If set, limit the number of refers returned from the index.
llvm::DenseSet< SymbolID > Subjects
const DeclContext & getDeclContext() const
static std::optional< SpelledWord > touching(SourceLocation SpelledLoc, const syntax::TokenBuffer &TB, const LangOptions &LangOpts)
const syntax::Token * ExpandedToken
const syntax::Token * PartOfSpelledToken
Represents information about identifier.
std::optional< Location > definitionRange
std::string containerName
std::optional< Location > declarationRange
std::string USR
Unified Symbol Resolution identifier This is an opaque string uniquely identifying a symbol.
Attributes of a symbol that affect how much we like it.
float evaluateHeuristics() const
void merge(const CodeCompletionResult &SemaCCResult)
Attributes of a symbol-query pair that affect how much we like it.
llvm::StringRef Name
The name of the symbol (for ContextWords). Must be explicitly assigned.
void merge(const CodeCompletionResult &SemaResult)
enum clang::clangd::SymbolRelevanceSignals::QueryType Query
float evaluateHeuristics() const
The class presents a C++ symbol, e.g.
@ Deprecated
Indicates if the symbol is deprecated.
SymbolLocation Definition
The location of the symbol's definition, if one was found.
index::SymbolInfo SymInfo
The symbol information, like symbol kind.
llvm::StringRef Name
The unqualified name of the symbol, e.g. "bar" (for ns::bar).
llvm::StringRef Scope
The containing namespace. e.g. "" (global), "ns::" (top-level namespace).
llvm::StringRef TemplateSpecializationArgs
Argument list in human-readable format, will be displayed to help disambiguate between different spec...
SymbolLocation CanonicalDeclaration
The location of the preferred declaration of the symbol.
SymbolID ID
The ID of the symbol.
std::optional< std::vector< ResolveParams > > parents
std::nullopt means parents aren't resolved and empty is no parents.
URIForFile uri
The resource identifier of this item.
std::optional< std::vector< TypeHierarchyItem > > children
If this type hierarchy item is resolved, it contains the direct children of the current item.
std::optional< std::vector< TypeHierarchyItem > > parents
This is a clangd exntesion.
ResolveParams data
A data entry field that is preserved between a type hierarchy prepare and supertypes or subtypes requ...
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Canonicalizes AbsPath via URI.
llvm::StringRef file() const
Retrieves absolute path to the file.
Represents measurements of clangd events, e.g.
@ Counter
An aggregate number whose rate of change over time is meaningful.