//===--- XRefs.cpp ----------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===---------------------------------------------------------------------===// #include "XRefs.h" #include "clang/Index/IndexDataConsumer.h" #include "clang/Index/IndexingAction.h" namespace clang { namespace clangd { using namespace llvm; namespace { /// Finds declarations locations that a given source location refers to. class DeclarationAndMacrosFinder : public index::IndexDataConsumer { std::vector Decls; std::vector MacroInfos; const SourceLocation &SearchedLocation; const ASTContext * Preprocessor &PP; public: DeclarationAndMacrosFinder(raw_ostream &OS, const SourceLocation &SearchedLocation, ASTContext &AST, Preprocessor &PP) : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {} std::vector takeDecls() { // Don't keep the same declaration multiple times. // This can happen when nodes in the AST are visited twice. std::sort(Decls.begin(), Decls.end()); auto Last = std::unique(Decls.begin(), Decls.end()); Decls.erase(Last, Decls.end()); return std::move(Decls); } std::vector takeMacroInfos() { // Don't keep the same Macro info multiple times. std::sort(MacroInfos.begin(), MacroInfos.end()); auto Last = std::unique(MacroInfos.begin(), MacroInfos.end()); MacroInfos.erase(Last, MacroInfos.end()); return std::move(MacroInfos); } bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, ArrayRef Relations, FileID FID, unsigned Offset, index::IndexDataConsumer::ASTNodeInfo ASTNode) override { if (isSearchedLocation(FID, Offset)) Decls.push_back(D); return true; } private: bool isSearchedLocation(FileID FID, unsigned Offset) const { const SourceManager &SourceMgr = AST.getSourceManager(); return SourceMgr.getFileOffset(SearchedLocation) == Offset && SourceMgr.getFileID(SearchedLocation) == FID; } void finish() override { // Also handle possible macro at the searched location. Token Result; auto &Mgr = AST.getSourceManager(); if (!Lexer::getRawToken(SearchedLocation, Result, Mgr, AST.getLangOpts(), false)) { if (Result.is(tok::raw_identifier)) { PP.LookUpIdentifierInfo(Result); } IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo(); if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) { std::pair DecLoc = Mgr.getDecomposedExpansionLoc(SearchedLocation); // Get the definition just before the searched location so that a macro // referenced in a '#undef MACRO' can still be found. SourceLocation BeforeSearchedLocation = Mgr.getMacroArgExpandedLocation( Mgr.getLocForStartOfFile(DecLoc.first) .getLocWithOffset(DecLoc.second - 1)); MacroDefinition MacroDef = PP.getMacroDefinitionAtLoc(IdentifierInfo, BeforeSearchedLocation); MacroInfo *MacroInf = MacroDef.getMacroInfo(); if (MacroInf) { MacroInfos.push_back(MacroInf); } } } } }; llvm::Optional getDeclarationLocation(ParsedAST &AST, const SourceRange &ValSourceRange) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); const LangOptions &LangOpts = AST.getASTContext().getLangOpts(); SourceLocation LocStart = ValSourceRange.getBegin(); const FileEntry *F = SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart)); if (!F) return llvm::None; SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0, SourceMgr, LangOpts); Position Begin; Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1; Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1; Position End; End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1; End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1; Range R = {Begin, End}; Location L; StringRef FilePath = F->tryGetRealPathName(); if (FilePath.empty()) FilePath = F->getName(); L.uri = URI::fromFile(FilePath); L.range = R; return L; } } // namespace std::vector findDefinitions(const Context &Ctx, ParsedAST &AST, Position Pos) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); if (!FE) return {}; SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE); auto DeclMacrosFinder = std::make_shared( llvm::errs(), SourceLocationBeg, AST.getASTContext(), AST.getPreprocessor()); index::IndexingOptions IndexOpts; IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; IndexOpts.IndexFunctionLocals = true; indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(), DeclMacrosFinder, IndexOpts); std::vector Decls = DeclMacrosFinder->takeDecls(); std::vector MacroInfos = DeclMacrosFinder->takeMacroInfos(); std::vector Result; for (auto Item : Decls) { auto L = getDeclarationLocation(AST, Item->getSourceRange()); if (L) Result.push_back(*L); } for (auto Item : MacroInfos) { SourceRange SR(Item->getDefinitionLoc(), Item->getDefinitionEndLoc()); auto L = getDeclarationLocation(AST, SR); if (L) Result.push_back(*L); } return Result; } namespace { /// Finds document highlights that a given list of declarations refers to. class DocumentHighlightsFinder : public index::IndexDataConsumer { std::vector &Decls; std::vector DocumentHighlights; const ASTContext * public: DocumentHighlightsFinder(raw_ostream &OS, ASTContext &AST, Preprocessor &PP, std::vector &Decls) : Decls(Decls), AST(AST) {} std::vector takeHighlights() { // Don't keep the same highlight multiple times. // This can happen when nodes in the AST are visited twice. std::sort(DocumentHighlights.begin(), DocumentHighlights.end()); auto Last = std::unique(DocumentHighlights.begin(), DocumentHighlights.end()); DocumentHighlights.erase(Last, DocumentHighlights.end()); return std::move(DocumentHighlights); } bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, ArrayRef Relations, FileID FID, unsigned Offset, index::IndexDataConsumer::ASTNodeInfo ASTNode) override { const SourceManager &SourceMgr = AST.getSourceManager(); if (SourceMgr.getMainFileID() != FID || std::find(Decls.begin(), Decls.end(), D) == Decls.end()) { return true; } SourceLocation End; const LangOptions &LangOpts = AST.getLangOpts(); SourceLocation StartOfFileLoc = SourceMgr.getLocForStartOfFile(FID); SourceLocation HightlightStartLoc = StartOfFileLoc.getLocWithOffset(Offset); End = Lexer::getLocForEndOfToken(HightlightStartLoc, 0, SourceMgr, LangOpts); SourceRange SR(HightlightStartLoc, End); DocumentHighlightKind Kind = DocumentHighlightKind::Text; if (static_cast(index::SymbolRole::Write) & Roles) Kind = DocumentHighlightKind::Write; else if (static_cast(index::SymbolRole::Read) & Roles) Kind = DocumentHighlightKind::Read; DocumentHighlights.push_back(getDocumentHighlight(SR, Kind)); return true; } private: DocumentHighlight getDocumentHighlight(SourceRange SR, DocumentHighlightKind Kind) { const SourceManager &SourceMgr = AST.getSourceManager(); SourceLocation LocStart = SR.getBegin(); Position Begin; Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1; Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1; Position End; End.line = SourceMgr.getSpellingLineNumber(SR.getEnd()) - 1; End.character = SourceMgr.getSpellingColumnNumber(SR.getEnd()) - 1; Range R = {Begin, End}; DocumentHighlight DH; DH.range = R; DH.kind = Kind; return DH; } }; } // namespace std::vector findDocumentHighlights(const Context &Ctx, ParsedAST &AST, Position Pos) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); if (!FE) return {}; SourceLocation SourceLocationBeg = getBeginningOfIdentifier(AST, Pos, FE); auto DeclMacrosFinder = std::make_shared( llvm::errs(), SourceLocationBeg, AST.getASTContext(), AST.getPreprocessor()); index::IndexingOptions IndexOpts; IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; IndexOpts.IndexFunctionLocals = true; // Macro occurences are not currently handled. indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(), DeclMacrosFinder, IndexOpts); std::vector SelectedDecls = DeclMacrosFinder->takeDecls(); auto DocHighlightsFinder = std::make_shared( llvm::errs(), AST.getASTContext(), AST.getPreprocessor(), SelectedDecls); indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(), DocHighlightsFinder, IndexOpts); return DocHighlightsFinder->takeHighlights(); } } // namespace clangd } // namespace clang