From d40a439067a1405aaf3390468082460eec5f4138 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 2 Nov 2010 23:10:24 +0000 Subject: [PATCH] Hack to workaround deficiency in ObjC ASTs. Functions and variables may be declared within an @implementation, but we have no way to record that information in the AST. This may cause CursorVisitor to miss these Decls when doing a AST walk. Fixes . llvm-svn: 118109 --- clang/test/Index/annotate-tokens.m | 52 +++++++++- clang/tools/libclang/CIndex.cpp | 150 +++++++++++++++++++++++------ 2 files changed, 173 insertions(+), 29 deletions(-) diff --git a/clang/test/Index/annotate-tokens.m b/clang/test/Index/annotate-tokens.m index 6b8a39c55d53..a0115d65ced4 100644 --- a/clang/test/Index/annotate-tokens.m +++ b/clang/test/Index/annotate-tokens.m @@ -88,7 +88,21 @@ void f() { (void)@protocol(Proto); } -// RUN: c-index-test -test-annotate-tokens=%s:1:1:89:2 %s -DIBOutlet='__attribute__((iboutlet))' -DIBAction='void)__attribute__((ibaction)' | FileCheck %s +// - Properly annotate functions and variables +// declared within an @implementation. +@class Rdar8595462_A; +@interface Rdar8595462_B +@end + +@implementation Rdar8595462_B +Rdar8595462_A * Rdar8595462_aFunction() { + Rdar8595462_A * localVar = 0; + return localVar; +} +static Rdar8595462_A * Rdar8595462_staticVar; +@end + +// RUN: c-index-test -test-annotate-tokens=%s:1:1:104:1 %s -DIBOutlet='__attribute__((iboutlet))' -DIBAction='void)__attribute__((ibaction)' | FileCheck %s // CHECK: Punctuation: "@" [1:1 - 1:2] ObjCInterfaceDecl=Foo:1:12 // CHECK: Keyword: "interface" [1:2 - 1:11] ObjCInterfaceDecl=Foo:1:12 // CHECK: Identifier: "Foo" [1:12 - 1:15] ObjCInterfaceDecl=Foo:1:12 @@ -397,3 +411,39 @@ void f() { // CHECK: Punctuation: ")" [88:24 - 88:25] UnexposedExpr=Proto:85:1 // CHECK: Punctuation: ";" [88:25 - 88:26] UnexposedStmt= // CHECK: Punctuation: "}" [89:1 - 89:2] UnexposedStmt= +// CHECK: Punctuation: "@" [93:1 - 93:2] UnexposedDecl=[93:8] +// CHECK: Keyword: "class" [93:2 - 93:7] UnexposedDecl=[93:8] +// CHECK: Identifier: "Rdar8595462_A" [93:8 - 93:21] ObjCClassRef=Rdar8595462_A:93:8 +// CHECK: Punctuation: ";" [93:21 - 93:22] +// CHECK: Punctuation: "@" [94:1 - 94:2] ObjCInterfaceDecl=Rdar8595462_B:94:12 +// CHECK: Keyword: "interface" [94:2 - 94:11] ObjCInterfaceDecl=Rdar8595462_B:94:12 +// CHECK: Identifier: "Rdar8595462_B" [94:12 - 94:25] ObjCInterfaceDecl=Rdar8595462_B:94:12 +// CHECK: Punctuation: "@" [95:1 - 95:2] ObjCInterfaceDecl=Rdar8595462_B:94:12 +// CHECK: Keyword: "end" [95:2 - 95:5] ObjCInterfaceDecl=Rdar8595462_B:94:12 +// CHECK: Punctuation: "@" [97:1 - 97:2] ObjCImplementationDecl=Rdar8595462_B:97:1 (Definition) +// CHECK: Keyword: "implementation" [97:2 - 97:16] ObjCImplementationDecl=Rdar8595462_B:97:1 (Definition) +// CHECK: Identifier: "Rdar8595462_B" [97:17 - 97:30] ObjCImplementationDecl=Rdar8595462_B:97:1 (Definition) +// CHECK: Identifier: "Rdar8595462_A" [98:1 - 98:14] ObjCClassRef=Rdar8595462_A:93:8 +// CHECK: Punctuation: "*" [98:15 - 98:16] FunctionDecl=Rdar8595462_aFunction:98:17 (Definition) +// CHECK: Identifier: "Rdar8595462_aFunction" [98:17 - 98:38] FunctionDecl=Rdar8595462_aFunction:98:17 (Definition) +// CHECK: Punctuation: "(" [98:38 - 98:39] FunctionDecl=Rdar8595462_aFunction:98:17 (Definition) +// CHECK: Punctuation: ")" [98:39 - 98:40] FunctionDecl=Rdar8595462_aFunction:98:17 (Definition) +// CHECK: Punctuation: "{" [98:41 - 98:42] UnexposedStmt= +// CHECK: Identifier: "Rdar8595462_A" [99:3 - 99:16] ObjCClassRef=Rdar8595462_A:93:8 +// CHECK: Punctuation: "*" [99:17 - 99:18] VarDecl=localVar:99:19 (Definition) +// CHECK: Identifier: "localVar" [99:19 - 99:27] VarDecl=localVar:99:19 (Definition) +// CHECK: Punctuation: "=" [99:28 - 99:29] VarDecl=localVar:99:19 (Definition) +// CHECK: Literal: "0" [99:30 - 99:31] UnexposedExpr= +// CHECK: Punctuation: ";" [99:31 - 99:32] UnexposedStmt= +// CHECK: Keyword: "return" [100:3 - 100:9] UnexposedStmt= +// CHECK: Identifier: "localVar" [100:10 - 100:18] DeclRefExpr=localVar:99:19 +// CHECK: Punctuation: ";" [100:18 - 100:19] UnexposedStmt= +// CHECK: Punctuation: "}" [101:1 - 101:2] UnexposedStmt= +// CHECK: Keyword: "static" [102:1 - 102:7] ObjCImplementationDecl=Rdar8595462_B:97:1 (Definition) +// CHECK: Identifier: "Rdar8595462_A" [102:8 - 102:21] ObjCClassRef=Rdar8595462_A:93:8 +// CHECK: Punctuation: "*" [102:22 - 102:23] VarDecl=Rdar8595462_staticVar:102:24 +// CHECK: Identifier: "Rdar8595462_staticVar" [102:24 - 102:45] VarDecl=Rdar8595462_staticVar:102:24 +// CHECK: Punctuation: ";" [102:45 - 102:46] ObjCImplementationDecl=Rdar8595462_B:97:1 (Definition) +// CHECK: Punctuation: "@" [103:1 - 103:2] ObjCImplementationDecl=Rdar8595462_B:97:1 (Definition) +// CHECK: Keyword: "end" [103:2 - 103:5] + diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 4413766b028e..f4cae89f8fc9 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -31,6 +31,8 @@ #include "clang/Lex/PreprocessingRecord.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Optional.h" +#include "clang/Analysis/Support/SaveAndRestore.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/MemoryBuffer.h" @@ -151,6 +153,11 @@ class CursorVisitor : public DeclVisitor, /// its search. SourceRange RegionOfInterest; + // FIXME: Eventually remove. This part of a hack to support proper + // iteration over all Decls contained lexically within an ObjC container. + DeclContext::decl_iterator *DI_current; + DeclContext::decl_iterator DE_current; + using DeclVisitor::Visit; using TypeLocVisitor::Visit; using StmtVisitor::Visit; @@ -187,7 +194,8 @@ public: unsigned MaxPCHLevel, SourceRange RegionOfInterest = SourceRange()) : TU(TU), Visitor(Visitor), ClientData(ClientData), - MaxPCHLevel(MaxPCHLevel), RegionOfInterest(RegionOfInterest) + MaxPCHLevel(MaxPCHLevel), RegionOfInterest(RegionOfInterest), + DI_current(0) { Parent.kind = CXCursor_NoDeclFound; Parent.data[0] = 0; @@ -207,6 +215,7 @@ public: bool VisitAttributes(Decl *D); bool VisitBlockDecl(BlockDecl *B); bool VisitCXXRecordDecl(CXXRecordDecl *D); + llvm::Optional shouldVisitCursor(CXCursor C); bool VisitDeclContext(DeclContext *DC); bool VisitTranslationUnitDecl(TranslationUnitDecl *D); bool VisitTypedefDecl(TypedefDecl *D); @@ -497,40 +506,50 @@ bool CursorVisitor::VisitBlockDecl(BlockDecl *B) { return false; } -bool CursorVisitor::VisitDeclContext(DeclContext *DC) { - for (DeclContext::decl_iterator - I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) { +llvm::Optional CursorVisitor::shouldVisitCursor(CXCursor Cursor) { + if (RegionOfInterest.isValid()) { + SourceRange Range = getRawCursorExtent(Cursor); + if (Range.isInvalid()) + return llvm::Optional(); + switch (CompareRegionOfInterest(Range)) { + case RangeBefore: + // This declaration comes before the region of interest; skip it. + return llvm::Optional(); + + case RangeAfter: + // This declaration comes after the region of interest; we're done. + return false; + + case RangeOverlap: + // This declaration overlaps the region of interest; visit it. + break; + } + } + return true; +} + +bool CursorVisitor::VisitDeclContext(DeclContext *DC) { + DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); + + // FIXME: Eventually remove. This part of a hack to support proper + // iteration over all Decls contained lexically within an ObjC container. + SaveAndRestore DI_saved(DI_current, &I); + SaveAndRestore DE_saved(DE_current, E); + + for ( ; I != E; ++I) { Decl *D = *I; if (D->getLexicalDeclContext() != DC) continue; - CXCursor Cursor = MakeCXCursor(D, TU); - - if (RegionOfInterest.isValid()) { - SourceRange Range = getRawCursorExtent(Cursor); - if (Range.isInvalid()) - continue; - - switch (CompareRegionOfInterest(Range)) { - case RangeBefore: - // This declaration comes before the region of interest; skip it. - continue; - - case RangeAfter: - // This declaration comes after the region of interest; we're done. - return false; - - case RangeOverlap: - // This declaration overlaps the region of interest; visit it. - break; - } - } - + const llvm::Optional &V = shouldVisitCursor(Cursor); + if (!V.hasValue()) + continue; + if (!V.getValue()) + return false; if (Visit(Cursor, true)) return true; } - return false; } @@ -794,8 +813,83 @@ bool CursorVisitor::VisitObjCMethodDecl(ObjCMethodDecl *ND) { return false; } +namespace { + struct ContainerDeclsSort { + SourceManager &SM; + ContainerDeclsSort(SourceManager &sm) : SM(sm) {} + bool operator()(Decl *A, Decl *B) { + SourceLocation L_A = A->getLocStart(); + SourceLocation L_B = B->getLocStart(); + assert(L_A.isValid() && L_B.isValid()); + return SM.isBeforeInTranslationUnit(L_A, L_B); + } + }; +} + bool CursorVisitor::VisitObjCContainerDecl(ObjCContainerDecl *D) { - return VisitDeclContext(D); + // FIXME: Eventually convert back to just 'VisitDeclContext()'. Essentially + // an @implementation can lexically contain Decls that are not properly + // nested in the AST. When we identify such cases, we need to retrofit + // this nesting here. + if (!DI_current) + return VisitDeclContext(D); + + // Scan the Decls that immediately come after the container + // in the current DeclContext. If any fall within the + // container's lexical region, stash them into a vector + // for later processing. + llvm::SmallVector DeclsInContainer; + SourceLocation EndLoc = D->getSourceRange().getEnd(); + SourceManager &SM = TU->getSourceManager(); + if (EndLoc.isValid()) { + DeclContext::decl_iterator next = *DI_current; + while (++next != DE_current) { + Decl *D_next = *next; + if (!D_next) + break; + SourceLocation L = D_next->getLocStart(); + if (!L.isValid()) + break; + if (SM.isBeforeInTranslationUnit(L, EndLoc)) { + *DI_current = next; + DeclsInContainer.push_back(D_next); + continue; + } + break; + } + } + + // The common case. + if (DeclsInContainer.empty()) + return VisitDeclContext(D); + + // Get all the Decls in the DeclContext, and sort them with the + // additional ones we've collected. Then visit them. + for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); + I!=E; ++I) { + Decl *subDecl = *I; + if (!subDecl || subDecl->getLexicalDeclContext() != D) + continue; + DeclsInContainer.push_back(subDecl); + } + + // Now sort the Decls so that they appear in lexical order. + std::sort(DeclsInContainer.begin(), DeclsInContainer.end(), + ContainerDeclsSort(SM)); + + // Now visit the decls. + for (llvm::SmallVectorImpl::iterator I = DeclsInContainer.begin(), + E = DeclsInContainer.end(); I != E; ++I) { + CXCursor Cursor = MakeCXCursor(*I, TU); + const llvm::Optional &V = shouldVisitCursor(Cursor); + if (!V.hasValue()) + continue; + if (!V.getValue()) + return false; + if (Visit(Cursor, true)) + return true; + } + return false; } bool CursorVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *ND) {