[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:
Argyrios Kyrtzidis 2011-11-03 02:20:32 +00:00
parent cb373e3f31
commit e968152564
11 changed files with 371 additions and 5 deletions

View File

@ -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) {}

View File

@ -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) {

View File

@ -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.

View File

@ -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();

View File

@ -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.

View 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

View File

@ -0,0 +1,3 @@
int z;
int w;

View File

@ -0,0 +1,2 @@
extern int NestedVar1;

View File

@ -0,0 +1,2 @@
extern int PreambleVar;

View 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

View File

@ -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;