forked from OSchip/llvm-project
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:
parent
6c2afc8d5d
commit
d40a439067
|
@ -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]
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue