From: Argyrios Kyrtzidis Date: Thu, 3 Nov 2011 02:20:32 +0000 (+0000) Subject: [libclang] Add infrastructure to be able to only deserialize decls in a file region and X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dfb332d0081c6641d1dbae6a2aeff757c99cc740;p=clang [libclang] Add infrastructure to be able to only deserialize decls in a file region and use it for clang_getCursor. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@143605 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h index 96d14b2954..8fea568c17 100644 --- a/include/clang/AST/ExternalASTSource.h +++ b/include/clang/AST/ExternalASTSource.h @@ -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 &Decls) {} + /// \brief Gives the external AST source an opportunity to complete /// an incomplete type. virtual void CompleteType(TagDecl *Tag) {} diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index cbd293f72e..26828a4427 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -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 &Decls); + /// \brief Add a new top-level declaration, identified by its ID in /// the precompiled preamble. void addTopLevelDeclFromPreamble(serialization::DeclID D) { diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index e0950192e3..d10f9ba18f 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -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 &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 &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. diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 0600203eae..015e92dfcd 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -2518,6 +2518,42 @@ void ASTUnit::addFileLevelDecl(Decl *D) { Decls->insert(I, LocDecl); } +void ASTUnit::findFileRegionDecls(FileID File, unsigned Offset, unsigned Length, + SmallVectorImpl &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(); diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 153b0e0dfe..cbf6cd0c5c 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -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 &Decls) { + SourceManager &SM = getSourceManager(); + + llvm::DenseMap::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::iterator + BeginIt = std::lower_bound(DInfo.Decls.begin(), DInfo.Decls.end(), + BeginLoc, DIDComp); + if (BeginIt != DInfo.Decls.begin()) + --BeginIt; + + ArrayRef::iterator + EndIt = std::upper_bound(DInfo.Decls.begin(), DInfo.Decls.end(), + EndLoc, DIDComp); + if (EndIt != DInfo.Decls.end()) + ++EndIt; + + for (ArrayRef::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. diff --git a/test/Index/targeted-cursor.c b/test/Index/targeted-cursor.c new file mode 100644 index 0000000000..ec03156c61 --- /dev/null +++ b/test/Index/targeted-cursor.c @@ -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 diff --git a/test/Index/targeted-fields.h b/test/Index/targeted-fields.h new file mode 100644 index 0000000000..7da57f3ef2 --- /dev/null +++ b/test/Index/targeted-fields.h @@ -0,0 +1,3 @@ + + int z; + int w; diff --git a/test/Index/targeted-nested1.h b/test/Index/targeted-nested1.h new file mode 100644 index 0000000000..d5a019b698 --- /dev/null +++ b/test/Index/targeted-nested1.h @@ -0,0 +1,2 @@ + +extern int NestedVar1; diff --git a/test/Index/targeted-preamble.h b/test/Index/targeted-preamble.h new file mode 100644 index 0000000000..19b953933f --- /dev/null +++ b/test/Index/targeted-preamble.h @@ -0,0 +1,2 @@ + +extern int PreambleVar; diff --git a/test/Index/targeted-top.h b/test/Index/targeted-top.h new file mode 100644 index 0000000000..0f3c97586e --- /dev/null +++ b/test/Index/targeted-top.h @@ -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 diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index efc216b39f..5fe9bf049a 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -219,6 +219,12 @@ class CursorVisitor : public DeclVisitor, /// \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(TU->TUData); + SourceManager &SM = Unit->getSourceManager(); + + std::pair + 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(TU->TUData); + SourceManager &SM = Unit->getSourceManager(); + + SourceRange Range = RegionOfInterest; + CXCursor Parent = clang_getTranslationUnitCursor(TU); + + SmallVector 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::iterator DIt = Decls.begin(); + for (SmallVector::iterator DE = Decls.end(); DIt != DE; ++DIt) { + Decl *D = *DIt; + + // We handle forward decls via ObjCClassDecl. + if (ObjCInterfaceDecl *InterD = dyn_cast(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(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;