Fix <rdar://problem/5917992> clang ObjC rewriter: #end from #if statement lost in translation.

Some fancy footwork to accommodate embedded preprocessor directives within an interface.

llvm-svn: 51815
This commit is contained in:
Steve Naroff 2008-05-31 14:15:04 +00:00
parent f970edf9b7
commit cd92aebd1f
1 changed files with 60 additions and 3 deletions

View File

@ -161,6 +161,7 @@ namespace {
ObjCInterfaceDecl *isSuperReceiver(Expr *recExpr);
QualType getSuperStructType();
QualType getConstantStringStructType();
bool BufferContainsPPDirectives(const char *startBuf, const char *endBuf);
// Expression Rewriting.
Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S);
@ -2247,6 +2248,32 @@ Stmt *RewriteObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) {
}
bool RewriteObjC::BufferContainsPPDirectives(const char *startBuf,
const char *endBuf) {
while (startBuf < endBuf) {
if (*startBuf == '#') {
// Skip whitespace.
for (++startBuf; startBuf[0] == ' ' || startBuf[0] == '\t'; ++startBuf)
;
if (!strncmp(startBuf, "if", strlen("if")) ||
!strncmp(startBuf, "ifdef", strlen("ifdef")) ||
!strncmp(startBuf, "ifndef", strlen("ifndef")) ||
!strncmp(startBuf, "define", strlen("define")) ||
!strncmp(startBuf, "undef", strlen("undef")) ||
!strncmp(startBuf, "else", strlen("else")) ||
!strncmp(startBuf, "elif", strlen("elif")) ||
!strncmp(startBuf, "endif", strlen("endif")) ||
!strncmp(startBuf, "pragma", strlen("pragma")) ||
!strncmp(startBuf, "include", strlen("include")) ||
!strncmp(startBuf, "import", strlen("import")) ||
!strncmp(startBuf, "include_next", strlen("include_next")))
return true;
}
startBuf++;
}
return false;
}
/// SynthesizeObjCInternalStruct - Rewrite one internal struct corresponding to
/// an objective-c class with ivars.
void RewriteObjC::SynthesizeObjCInternalStruct(ObjCInterfaceDecl *CDecl,
@ -2263,6 +2290,7 @@ void RewriteObjC::SynthesizeObjCInternalStruct(ObjCInterfaceDecl *CDecl,
const char *startBuf = SM->getCharacterData(LocStart);
const char *endBuf = SM->getCharacterData(LocEnd);
// If no ivars and no root or if its root, directly or indirectly,
// have no ivars (thus not synthesized) then no need to synthesize this class.
if ((CDecl->isForwardDecl() || NumIvars == 0) &&
@ -2283,9 +2311,38 @@ void RewriteObjC::SynthesizeObjCInternalStruct(ObjCInterfaceDecl *CDecl,
const char *cursor = strchr(startBuf, '{');
assert((cursor && endBuf)
&& "SynthesizeObjCInternalStruct - malformed @interface");
// rewrite the original header *without* disturbing the '{'
ReplaceText(LocStart, cursor-startBuf-1, Result.c_str(), Result.size());
// If the buffer contains preprocessor directives, we do more fine-grained
// rewrites. This is intended to fix code that looks like (which occurs in
// NSURL.h, for example):
//
// #ifdef XYZ
// @interface Foo : NSObject
// #else
// @interface FooBar : NSObject
// #endif
// {
// int i;
// }
// @end
//
// This clause is segregated to avoid breaking the common case.
if (BufferContainsPPDirectives(startBuf, cursor)) {
SourceLocation L = RCDecl ? CDecl->getSuperClassLoc() :
CDecl->getClassLoc();
const char *endHeader = SM->getCharacterData(L);
endHeader += Lexer::MeasureTokenLength(L, *SM);
if (CDecl->getNumIntfRefProtocols()) {
// advance to the end of the referenced protocols.
while (endHeader < cursor && *endHeader != '>') endHeader++;
endHeader++;
}
// rewrite the original header
ReplaceText(LocStart, endHeader-startBuf, Result.c_str(), Result.size());
} else {
// rewrite the original header *without* disturbing the '{'
ReplaceText(LocStart, cursor-startBuf-1, Result.c_str(), Result.size());
}
if (RCDecl && ObjCSynthesizedStructs.count(RCDecl)) {
Result = "\n struct ";
Result += RCDecl->getName();