mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-14 03:29:57 +00:00
[libclang] Add infrastructure to be able to only deserialize decls in a file region and
use it for clang_getCursor. llvm-svn: 143605
This commit is contained in:
parent
cb373e3f31
commit
e968152564
@ -153,6 +153,12 @@ public:
|
||||
return FindExternalLexicalDecls(DC, DeclTy::classofKind, Result);
|
||||
}
|
||||
|
||||
/// \brief Get the decls that are contained in a file in the Offset/Length
|
||||
/// range. \arg Length can be 0 to indicate a point at \arg Offset instead of
|
||||
/// a range.
|
||||
virtual void FindFileRegionDecls(FileID File, unsigned Offset,unsigned Length,
|
||||
SmallVectorImpl<Decl *> &Decls) {}
|
||||
|
||||
/// \brief Gives the external AST source an opportunity to complete
|
||||
/// an incomplete type.
|
||||
virtual void CompleteType(TagDecl *Tag) {}
|
||||
|
@ -519,6 +519,12 @@ public:
|
||||
/// \brief Add a new local file-level declaration.
|
||||
void addFileLevelDecl(Decl *D);
|
||||
|
||||
/// \brief Get the decls that are contained in a file in the Offset/Length
|
||||
/// range. \arg Length can be 0 to indicate a point at \arg Offset instead of
|
||||
/// a range.
|
||||
void findFileRegionDecls(FileID File, unsigned Offset, unsigned Length,
|
||||
SmallVectorImpl<Decl *> &Decls);
|
||||
|
||||
/// \brief Add a new top-level declaration, identified by its ID in
|
||||
/// the precompiled preamble.
|
||||
void addTopLevelDeclFromPreamble(serialization::DeclID D) {
|
||||
|
@ -921,6 +921,9 @@ public:
|
||||
/// \brief Returns true if global DeclID \arg ID originated from module
|
||||
/// \arg M.
|
||||
bool isDeclIDFromModule(serialization::GlobalDeclID ID, Module &M) const;
|
||||
|
||||
/// \brief Returns the source location for the decl \arg ID.
|
||||
SourceLocation getSourceLocationForDeclID(serialization::GlobalDeclID ID);
|
||||
|
||||
/// \brief Resolve a declaration ID into a declaration, potentially
|
||||
/// building a new declaration.
|
||||
@ -1006,6 +1009,12 @@ public:
|
||||
bool (*isKindWeWant)(Decl::Kind),
|
||||
SmallVectorImpl<Decl*> &Decls);
|
||||
|
||||
/// \brief Get the decls that are contained in a file in the Offset/Length
|
||||
/// range. \arg Length can be 0 to indicate a point at \arg Offset instead of
|
||||
/// a range.
|
||||
virtual void FindFileRegionDecls(FileID File, unsigned Offset,unsigned Length,
|
||||
SmallVectorImpl<Decl *> &Decls);
|
||||
|
||||
/// \brief Notify ASTReader that we started deserialization of
|
||||
/// a decl or type so until FinishedDeserializing is called there may be
|
||||
/// decls that are initializing. Must be paired with FinishedDeserializing.
|
||||
|
@ -2518,6 +2518,42 @@ void ASTUnit::addFileLevelDecl(Decl *D) {
|
||||
Decls->insert(I, LocDecl);
|
||||
}
|
||||
|
||||
void ASTUnit::findFileRegionDecls(FileID File, unsigned Offset, unsigned Length,
|
||||
SmallVectorImpl<Decl *> &Decls) {
|
||||
if (File.isInvalid())
|
||||
return;
|
||||
|
||||
if (SourceMgr->isLoadedFileID(File)) {
|
||||
assert(Ctx->getExternalSource() && "No external source!");
|
||||
return Ctx->getExternalSource()->FindFileRegionDecls(File, Offset, Length,
|
||||
Decls);
|
||||
}
|
||||
|
||||
FileDeclsTy::iterator I = FileDecls.find(File);
|
||||
if (I == FileDecls.end())
|
||||
return;
|
||||
|
||||
LocDeclsTy &LocDecls = *I->second;
|
||||
if (LocDecls.empty())
|
||||
return;
|
||||
|
||||
LocDeclsTy::iterator
|
||||
BeginIt = std::lower_bound(LocDecls.begin(), LocDecls.end(),
|
||||
std::make_pair(Offset, (Decl*)0), compLocDecl);
|
||||
if (BeginIt != LocDecls.begin())
|
||||
--BeginIt;
|
||||
|
||||
LocDeclsTy::iterator
|
||||
EndIt = std::upper_bound(LocDecls.begin(), LocDecls.end(),
|
||||
std::make_pair(Offset+Length, (Decl*)0),
|
||||
compLocDecl);
|
||||
if (EndIt != LocDecls.end())
|
||||
++EndIt;
|
||||
|
||||
for (LocDeclsTy::iterator DIt = BeginIt; DIt != EndIt; ++DIt)
|
||||
Decls.push_back(DIt->second);
|
||||
}
|
||||
|
||||
SourceLocation ASTUnit::getLocation(const FileEntry *File,
|
||||
unsigned Line, unsigned Col) const {
|
||||
const SourceManager &SM = getSourceManager();
|
||||
|
@ -4020,6 +4020,25 @@ bool ASTReader::isDeclIDFromModule(serialization::GlobalDeclID ID,
|
||||
return &M == I->second;
|
||||
}
|
||||
|
||||
SourceLocation ASTReader::getSourceLocationForDeclID(GlobalDeclID ID) {
|
||||
if (ID < NUM_PREDEF_DECL_IDS)
|
||||
return SourceLocation();
|
||||
|
||||
unsigned Index = ID - NUM_PREDEF_DECL_IDS;
|
||||
|
||||
if (Index > DeclsLoaded.size()) {
|
||||
Error("declaration ID out-of-range for AST file");
|
||||
return SourceLocation();
|
||||
}
|
||||
|
||||
if (Decl *D = DeclsLoaded[Index])
|
||||
return D->getLocation();
|
||||
|
||||
unsigned RawLocation = 0;
|
||||
RecordLocation Rec = DeclCursorForID(ID, RawLocation);
|
||||
return ReadSourceLocation(*Rec.F, RawLocation);
|
||||
}
|
||||
|
||||
Decl *ASTReader::GetDecl(DeclID ID) {
|
||||
if (ID < NUM_PREDEF_DECL_IDS) {
|
||||
switch ((PredefinedDeclIDs)ID) {
|
||||
@ -4162,6 +4181,74 @@ ExternalLoadResult ASTReader::FindExternalLexicalDecls(const DeclContext *DC,
|
||||
return ELR_Success;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class DeclIDComp {
|
||||
ASTReader &Reader;
|
||||
Module &Mod;
|
||||
|
||||
public:
|
||||
DeclIDComp(ASTReader &Reader, Module &M) : Reader(Reader), Mod(M) {}
|
||||
|
||||
bool operator()(LocalDeclID L, LocalDeclID R) const {
|
||||
SourceLocation LHS = getLocation(L);
|
||||
SourceLocation RHS = getLocation(R);
|
||||
return Reader.getSourceManager().isBeforeInTranslationUnit(LHS, RHS);
|
||||
}
|
||||
|
||||
bool operator()(SourceLocation LHS, LocalDeclID R) const {
|
||||
SourceLocation RHS = getLocation(R);
|
||||
return Reader.getSourceManager().isBeforeInTranslationUnit(LHS, RHS);
|
||||
}
|
||||
|
||||
bool operator()(LocalDeclID L, SourceLocation RHS) const {
|
||||
SourceLocation LHS = getLocation(L);
|
||||
return Reader.getSourceManager().isBeforeInTranslationUnit(LHS, RHS);
|
||||
}
|
||||
|
||||
SourceLocation getLocation(LocalDeclID ID) const {
|
||||
return Reader.getSourceManager().getFileLoc(
|
||||
Reader.getSourceLocationForDeclID(Reader.getGlobalDeclID(Mod, ID)));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void ASTReader::FindFileRegionDecls(FileID File,
|
||||
unsigned Offset, unsigned Length,
|
||||
SmallVectorImpl<Decl *> &Decls) {
|
||||
SourceManager &SM = getSourceManager();
|
||||
|
||||
llvm::DenseMap<FileID, FileDeclsInfo>::iterator I = FileDeclIDs.find(File);
|
||||
if (I == FileDeclIDs.end())
|
||||
return;
|
||||
|
||||
FileDeclsInfo &DInfo = I->second;
|
||||
if (DInfo.Decls.empty())
|
||||
return;
|
||||
|
||||
SourceLocation
|
||||
BeginLoc = SM.getLocForStartOfFile(File).getLocWithOffset(Offset);
|
||||
SourceLocation EndLoc = BeginLoc.getLocWithOffset(Length);
|
||||
|
||||
DeclIDComp DIDComp(*this, *DInfo.Mod);
|
||||
ArrayRef<serialization::LocalDeclID>::iterator
|
||||
BeginIt = std::lower_bound(DInfo.Decls.begin(), DInfo.Decls.end(),
|
||||
BeginLoc, DIDComp);
|
||||
if (BeginIt != DInfo.Decls.begin())
|
||||
--BeginIt;
|
||||
|
||||
ArrayRef<serialization::LocalDeclID>::iterator
|
||||
EndIt = std::upper_bound(DInfo.Decls.begin(), DInfo.Decls.end(),
|
||||
EndLoc, DIDComp);
|
||||
if (EndIt != DInfo.Decls.end())
|
||||
++EndIt;
|
||||
|
||||
for (ArrayRef<serialization::LocalDeclID>::iterator
|
||||
DIt = BeginIt; DIt != EndIt; ++DIt)
|
||||
Decls.push_back(GetDecl(getGlobalDeclID(*DInfo.Mod, *DIt)));
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// \brief Module visitor used to perform name lookup into a
|
||||
/// declaration context.
|
||||
|
52
clang/test/Index/targeted-cursor.c
Normal file
52
clang/test/Index/targeted-cursor.c
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
#include "targeted-top.h"
|
||||
#include "targeted-preamble.h"
|
||||
|
||||
int LocalVar1;
|
||||
int LocalVar2;
|
||||
|
||||
// RUN: c-index-test -write-pch %t.h.pch %S/targeted-top.h
|
||||
// RUN: env CINDEXTEST_FAILONERROR=1 c-index-test -cursor-at=%s:5:10 %s -include %t.h \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=NestedVar1 \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=TopVar \
|
||||
// RUN: | FileCheck %s -check-prefix=LOCAL-CURSOR1
|
||||
|
||||
// RUN: env CINDEXTEST_FAILONERROR=1 c-index-test -cursor-at=%S/targeted-top.h:11:15 %s -include %t.h \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=NestedVar1 \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=vector_get_x \
|
||||
// RUN: | FileCheck %s -check-prefix=TOP-CURSOR1
|
||||
|
||||
// RUN: env CINDEXTEST_FAILONERROR=1 c-index-test -cursor-at=%S/targeted-nested1.h:2:16 %s -include %t.h \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=TopVar \
|
||||
// RUN: | FileCheck %s -check-prefix=NESTED-CURSOR1
|
||||
|
||||
// RUN: env CINDEXTEST_FAILONERROR=1 CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_NO_CACHING=1 \
|
||||
// RUN: c-index-test -cursor-at=%s:5:10 %s -include %t.h \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=PreambleVar \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=NestedVar1 \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=TopVar \
|
||||
// RUN: | FileCheck %s -check-prefix=LOCAL-CURSOR1
|
||||
|
||||
// RUN: env CINDEXTEST_FAILONERROR=1 CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_NO_CACHING=1 \
|
||||
// RUN: c-index-test -cursor-at=%S/targeted-top.h:11:15 %s -include %t.h \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=PreambleVar \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=NestedVar1 \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=vector_get_x \
|
||||
// RUN: | FileCheck %s -check-prefix=TOP-CURSOR1
|
||||
|
||||
// RUN: env CINDEXTEST_FAILONERROR=1 CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_NO_CACHING=1 \
|
||||
// RUN: c-index-test -cursor-at=%S/targeted-nested1.h:2:16 %s -include %t.h \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=PreambleVar \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=TopVar \
|
||||
// RUN: | FileCheck %s -check-prefix=NESTED-CURSOR1
|
||||
|
||||
// RUN: env CINDEXTEST_FAILONERROR=1 CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_NO_CACHING=1 \
|
||||
// RUN: c-index-test -cursor-at=%S/targeted-preamble.h:2:15 %s -include %t.h \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=NestedVar1 \
|
||||
// RUN: -Xclang -error-on-deserialized-decl=TopVar \
|
||||
// RUN: | FileCheck %s -check-prefix=PREAMBLE-CURSOR1
|
||||
|
||||
// LOCAL-CURSOR1: VarDecl=LocalVar1:5:5
|
||||
// TOP-CURSOR1: VarDecl=TopVar:11:12
|
||||
// NESTED-CURSOR1: VarDecl=NestedVar1:2:12
|
||||
// PREAMBLE-CURSOR1: VarDecl=PreambleVar:2:12
|
3
clang/test/Index/targeted-fields.h
Normal file
3
clang/test/Index/targeted-fields.h
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
int z;
|
||||
int w;
|
2
clang/test/Index/targeted-nested1.h
Normal file
2
clang/test/Index/targeted-nested1.h
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
extern int NestedVar1;
|
2
clang/test/Index/targeted-preamble.h
Normal file
2
clang/test/Index/targeted-preamble.h
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
extern int PreambleVar;
|
24
clang/test/Index/targeted-top.h
Normal file
24
clang/test/Index/targeted-top.h
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
#ifndef TARGETED_TOP_H
|
||||
#define TARGETED_TOP_H
|
||||
|
||||
#include "targeted-nested1.h"
|
||||
|
||||
enum {
|
||||
VALUE = 3
|
||||
};
|
||||
|
||||
extern int TopVar;
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
#include "targeted-fields.h"
|
||||
} Vector;
|
||||
|
||||
static inline int vector_get_x(Vector v) {
|
||||
int x = v.x;
|
||||
return x;
|
||||
}
|
||||
|
||||
#endif
|
@ -219,6 +219,12 @@ class CursorVisitor : public DeclVisitor<CursorVisitor, bool>,
|
||||
/// \param R a half-open source range retrieved from the abstract syntax tree.
|
||||
RangeComparisonResult CompareRegionOfInterest(SourceRange R);
|
||||
|
||||
CXChildVisitResult invokeVisitor(CXCursor cursor, CXCursor parent) {
|
||||
return Visitor(cursor, parent, ClientData);
|
||||
}
|
||||
|
||||
void visitDeclsFromFileRegion(FileID File, unsigned Offset, unsigned Length);
|
||||
|
||||
class SetParentRAII {
|
||||
CXCursor &Parent;
|
||||
Decl *&StmtParent;
|
||||
@ -271,6 +277,10 @@ public:
|
||||
CXTranslationUnit getTU() const { return TU; }
|
||||
|
||||
bool Visit(CXCursor Cursor, bool CheckedRegionOfInterest = false);
|
||||
|
||||
/// \brief Visit declarations and preprocessed entities for the file region
|
||||
/// designated by \see RegionOfInterest.
|
||||
void visitFileRegion();
|
||||
|
||||
bool visitPreprocessedEntitiesInRegion();
|
||||
|
||||
@ -431,6 +441,139 @@ static bool visitPreprocessedEntitiesInRange(SourceRange R,
|
||||
PPRec, FID);
|
||||
}
|
||||
|
||||
void CursorVisitor::visitFileRegion() {
|
||||
if (RegionOfInterest.isInvalid())
|
||||
return;
|
||||
|
||||
ASTUnit *Unit = static_cast<ASTUnit *>(TU->TUData);
|
||||
SourceManager &SM = Unit->getSourceManager();
|
||||
|
||||
std::pair<FileID, unsigned>
|
||||
Begin = SM.getDecomposedLoc(SM.getFileLoc(RegionOfInterest.getBegin())),
|
||||
End = SM.getDecomposedLoc(SM.getFileLoc(RegionOfInterest.getEnd()));
|
||||
|
||||
if (End.first != Begin.first) {
|
||||
// If the end does not reside in the same file, try to recover by
|
||||
// picking the end of the file of begin location.
|
||||
End.first = Begin.first;
|
||||
End.second = SM.getFileIDSize(Begin.first);
|
||||
}
|
||||
|
||||
assert(Begin.first == End.first);
|
||||
if (Begin.second > End.second)
|
||||
return;
|
||||
|
||||
FileID File = Begin.first;
|
||||
unsigned Offset = Begin.second;
|
||||
unsigned Length = End.second - Begin.second;
|
||||
|
||||
if (!VisitPreprocessorLast &&
|
||||
Unit->getPreprocessor().getPreprocessingRecord())
|
||||
visitPreprocessedEntitiesInRegion();
|
||||
|
||||
visitDeclsFromFileRegion(File, Offset, Length);
|
||||
|
||||
if (VisitPreprocessorLast &&
|
||||
Unit->getPreprocessor().getPreprocessingRecord())
|
||||
visitPreprocessedEntitiesInRegion();
|
||||
}
|
||||
|
||||
void CursorVisitor::visitDeclsFromFileRegion(FileID File,
|
||||
unsigned Offset, unsigned Length) {
|
||||
ASTUnit *Unit = static_cast<ASTUnit *>(TU->TUData);
|
||||
SourceManager &SM = Unit->getSourceManager();
|
||||
|
||||
SourceRange Range = RegionOfInterest;
|
||||
CXCursor Parent = clang_getTranslationUnitCursor(TU);
|
||||
|
||||
SmallVector<Decl *, 16> Decls;
|
||||
Unit->findFileRegionDecls(File, Offset, Length, Decls);
|
||||
|
||||
// If we didn't find any file level decls for the file, try looking at the
|
||||
// file that it was included from.
|
||||
while (Decls.empty()) {
|
||||
bool Invalid = false;
|
||||
const SrcMgr::SLocEntry &SLEntry = SM.getSLocEntry(File, &Invalid);
|
||||
if (Invalid)
|
||||
return;
|
||||
|
||||
SourceLocation Outer;
|
||||
if (SLEntry.isFile())
|
||||
Outer = SLEntry.getFile().getIncludeLoc();
|
||||
else
|
||||
Outer = SLEntry.getExpansion().getExpansionLocStart();
|
||||
if (Outer.isInvalid())
|
||||
return;
|
||||
|
||||
llvm::tie(File, Offset) = SM.getDecomposedExpansionLoc(Outer);
|
||||
Length = 0;
|
||||
Unit->findFileRegionDecls(File, Offset, Length, Decls);
|
||||
}
|
||||
|
||||
assert(!Decls.empty());
|
||||
|
||||
bool VisitedAtLeastOnce = false;
|
||||
SmallVector<Decl *, 16>::iterator DIt = Decls.begin();
|
||||
for (SmallVector<Decl *, 16>::iterator DE = Decls.end(); DIt != DE; ++DIt) {
|
||||
Decl *D = *DIt;
|
||||
|
||||
// We handle forward decls via ObjCClassDecl.
|
||||
if (ObjCInterfaceDecl *InterD = dyn_cast<ObjCInterfaceDecl>(D)) {
|
||||
if (InterD->isForwardDecl())
|
||||
continue;
|
||||
// An interface that started as a forward decl may have changed location
|
||||
// because its @interface was parsed.
|
||||
if (InterD->isInitiallyForwardDecl() &&
|
||||
!SM.isInFileID(SM.getFileLoc(InterD->getLocation()), File))
|
||||
continue;
|
||||
}
|
||||
|
||||
RangeComparisonResult CompRes = RangeCompare(SM, D->getSourceRange(),Range);
|
||||
if (CompRes == RangeBefore)
|
||||
continue;
|
||||
if (CompRes == RangeAfter)
|
||||
break;
|
||||
|
||||
assert(CompRes == RangeOverlap);
|
||||
VisitedAtLeastOnce = true;
|
||||
CXCursor C = MakeCXCursor(D, TU, Range);
|
||||
CXChildVisitResult
|
||||
Res = invokeVisitor(C, Parent);
|
||||
if (Res == CXChildVisit_Break)
|
||||
break;
|
||||
if (Res == CXChildVisit_Recurse)
|
||||
if (VisitChildren(C))
|
||||
break;
|
||||
}
|
||||
|
||||
if (VisitedAtLeastOnce)
|
||||
return;
|
||||
|
||||
// No Decls overlapped with the range. Move up the lexical context until there
|
||||
// is a context that contains the range or we reach the translation unit
|
||||
// level.
|
||||
DeclContext *DC = DIt == Decls.begin() ? (*DIt)->getLexicalDeclContext()
|
||||
: (*(DIt-1))->getLexicalDeclContext();
|
||||
|
||||
while (DC && !DC->isTranslationUnit()) {
|
||||
Decl *D = cast<Decl>(DC);
|
||||
SourceRange CurDeclRange = D->getSourceRange();
|
||||
if (CurDeclRange.isInvalid())
|
||||
break;
|
||||
|
||||
if (RangeCompare(SM, CurDeclRange, Range) == RangeOverlap) {
|
||||
CXCursor C = MakeCXCursor(D, TU, Range);
|
||||
CXChildVisitResult
|
||||
Res = invokeVisitor(C, Parent);
|
||||
if (Res == CXChildVisit_Recurse)
|
||||
VisitChildren(C);
|
||||
break;
|
||||
}
|
||||
|
||||
DC = D->getLexicalDeclContext();
|
||||
}
|
||||
}
|
||||
|
||||
bool CursorVisitor::visitPreprocessedEntitiesInRegion() {
|
||||
PreprocessingRecord &PPRec
|
||||
= *AU->getPreprocessor().getPreprocessingRecord();
|
||||
@ -3741,16 +3884,12 @@ CXCursor cxcursor::getCursor(CXTranslationUnit TU, SourceLocation SLoc) {
|
||||
|
||||
CXCursor Result = MakeCXCursorInvalid(CXCursor_NoDeclFound);
|
||||
if (SLoc.isValid()) {
|
||||
// FIXME: Would be great to have a "hint" cursor, then walk from that
|
||||
// hint cursor upward until we find a cursor whose source range encloses
|
||||
// the region of interest, rather than starting from the translation unit.
|
||||
GetCursorData ResultData(CXXUnit->getSourceManager(), SLoc, Result);
|
||||
CXCursor Parent = clang_getTranslationUnitCursor(TU);
|
||||
CursorVisitor CursorVis(TU, GetCursorVisitor, &ResultData,
|
||||
/*VisitPreprocessorLast=*/true,
|
||||
/*VisitIncludedEntities=*/false,
|
||||
SourceLocation(SLoc));
|
||||
CursorVis.VisitChildren(Parent);
|
||||
CursorVis.visitFileRegion();
|
||||
}
|
||||
|
||||
return Result;
|
||||
|
Loading…
Reference in New Issue
Block a user