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 <rdar://problem/8595462>.

llvm-svn: 118109
This commit is contained in:
Ted Kremenek 2010-11-02 23:10:24 +00:00
parent 6c2afc8d5d
commit d40a439067
2 changed files with 173 additions and 29 deletions

View File

@ -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
// <rdar://problem/8595462> - 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]

View File

@ -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<CursorVisitor, bool>,
/// 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<CursorVisitor, bool>::Visit;
using TypeLocVisitor<CursorVisitor, bool>::Visit;
using StmtVisitor<CursorVisitor, bool>::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<bool> 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<bool> CursorVisitor::shouldVisitCursor(CXCursor Cursor) {
if (RegionOfInterest.isValid()) {
SourceRange Range = getRawCursorExtent(Cursor);
if (Range.isInvalid())
return llvm::Optional<bool>();
switch (CompareRegionOfInterest(Range)) {
case RangeBefore:
// This declaration comes before the region of interest; skip it.
return llvm::Optional<bool>();
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<DeclContext::decl_iterator*> DI_saved(DI_current, &I);
SaveAndRestore<DeclContext::decl_iterator> 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<bool> &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<Decl *, 24> 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<Decl*>::iterator I = DeclsInContainer.begin(),
E = DeclsInContainer.end(); I != E; ++I) {
CXCursor Cursor = MakeCXCursor(*I, TU);
const llvm::Optional<bool> &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) {