2017-12-20 01:06:07 +08:00
|
|
|
//===--- 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 {
|
|
|
|
|
2018-01-12 22:21:10 +08:00
|
|
|
// Get the definition from a given declaration `D`.
|
|
|
|
// Return nullptr if no definition is found, or the declaration type of `D` is
|
|
|
|
// not supported.
|
|
|
|
const Decl* GetDefinition(const Decl* D) {
|
|
|
|
assert(D);
|
|
|
|
if (const auto *TD = dyn_cast<TagDecl>(D))
|
|
|
|
return TD->getDefinition();
|
|
|
|
else if (const auto *VD = dyn_cast<VarDecl>(D))
|
|
|
|
return VD->getDefinition();
|
|
|
|
else if (const auto *FD = dyn_cast<FunctionDecl>(D))
|
|
|
|
return FD->getDefinition();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-12-20 01:06:07 +08:00
|
|
|
/// 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 {
|
2018-01-12 22:21:10 +08:00
|
|
|
if (isSearchedLocation(FID, Offset)) {
|
|
|
|
// Find and add definition declarations (for GoToDefinition).
|
|
|
|
// We don't use parameter `D`, as Parameter `D` is the canonical
|
|
|
|
// declaration, which is the first declaration of a redeclarable
|
|
|
|
// declaration, and it could be a forward declaration.
|
|
|
|
if (const auto* Def = GetDefinition(D)) {
|
|
|
|
Decls.push_back(Def);
|
|
|
|
} else {
|
|
|
|
// Couldn't find a definition, fall back to use `D`.
|
|
|
|
Decls.push_back(D);
|
|
|
|
}
|
|
|
|
}
|
2017-12-20 01:06:07 +08:00
|
|
|
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
|