diff options
Diffstat (limited to 'clang-tools-extra/clangd/XRefs.cpp')
| -rw-r--r-- | clang-tools-extra/clangd/XRefs.cpp | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp new file mode 100644 index 00000000000..9bd4131b03f --- /dev/null +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -0,0 +1,270 @@ +//===--- 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<const Decl *> Decls; + std::vector<const MacroInfo *> MacroInfos; + const SourceLocation &SearchedLocation; + const ASTContext &AST; + Preprocessor &PP; + +public: + DeclarationAndMacrosFinder(raw_ostream &OS, + const SourceLocation &SearchedLocation, + ASTContext &AST, Preprocessor &PP) + : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {} + + std::vector<const Decl *> 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<const MacroInfo *> 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<index::SymbolRelation> 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<FileID, unsigned int> 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<Location> +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<Location> 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<DeclarationAndMacrosFinder>( + 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<const Decl *> Decls = DeclMacrosFinder->takeDecls(); + std::vector<const MacroInfo *> MacroInfos = + DeclMacrosFinder->takeMacroInfos(); + std::vector<Location> 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<const Decl *> &Decls; + std::vector<DocumentHighlight> DocumentHighlights; + const ASTContext &AST; + +public: + DocumentHighlightsFinder(raw_ostream &OS, ASTContext &AST, Preprocessor &PP, + std::vector<const Decl *> &Decls) + : Decls(Decls), AST(AST) {} + std::vector<DocumentHighlight> 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<index::SymbolRelation> 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::SymbolRoleSet>(index::SymbolRole::Write) & Roles) + Kind = DocumentHighlightKind::Write; + else if (static_cast<index::SymbolRoleSet>(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<DocumentHighlight> +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<DeclarationAndMacrosFinder>( + 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<const Decl *> SelectedDecls = DeclMacrosFinder->takeDecls(); + + auto DocHighlightsFinder = std::make_shared<DocumentHighlightsFinder>( + llvm::errs(), AST.getASTContext(), AST.getPreprocessor(), SelectedDecls); + + indexTopLevelDecls(AST.getASTContext(), AST.getTopLevelDecls(), + DocHighlightsFinder, IndexOpts); + + return DocHighlightsFinder->takeHighlights(); +} + +} // namespace clangd +} // namespace clang |

