]> granicus.if.org Git - clang/commitdiff
[libclang] Add infrastructure to be able to only deserialize decls in a file region and
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Thu, 3 Nov 2011 02:20:32 +0000 (02:20 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Thu, 3 Nov 2011 02:20:32 +0000 (02:20 +0000)
use it for clang_getCursor.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@143605 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ExternalASTSource.h
include/clang/Frontend/ASTUnit.h
include/clang/Serialization/ASTReader.h
lib/Frontend/ASTUnit.cpp
lib/Serialization/ASTReader.cpp
test/Index/targeted-cursor.c [new file with mode: 0644]
test/Index/targeted-fields.h [new file with mode: 0644]
test/Index/targeted-nested1.h [new file with mode: 0644]
test/Index/targeted-preamble.h [new file with mode: 0644]
test/Index/targeted-top.h [new file with mode: 0644]
tools/libclang/CIndex.cpp

index 96d14b29549bd818075b39c404adff2eedb52c76..8fea568c17b766b65d55140b530c244f9439e6c6 100644 (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) {}
index cbd293f72ed2f26156cda7e709d06baca04c906b..26828a4427778bbd84795b081d91bf859ae86fa6 100644 (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) {
index e0950192e35e0ce0ccaad3a4e0c192d5f28c8c5b..d10f9ba18f8dd7a819038049db99fb24be244034 100644 (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.
index 0600203eae556d4ca40b4e0933f3357593136a57..015e92dfcd77a96165c768bb3d5672e6f36d7f2c 100644 (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();
index 153b0e0dfe6e45877e19e7cb68c2e0fd96d9692c..cbf6cd0c5cc7b59b3beb6b9b63016252d45f44e9 100644 (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.
diff --git a/test/Index/targeted-cursor.c b/test/Index/targeted-cursor.c
new file mode 100644 (file)
index 0000000..ec03156
--- /dev/null
@@ -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 (file)
index 0000000..7da57f3
--- /dev/null
@@ -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 (file)
index 0000000..d5a019b
--- /dev/null
@@ -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 (file)
index 0000000..19b9539
--- /dev/null
@@ -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 (file)
index 0000000..0f3c975
--- /dev/null
@@ -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
index efc216b39f7892388c9005a126043f1e9c18a353..5fe9bf049ae3feba9a83968a2a8107622228f780 100644 (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;