objective-c - This patch buffers method implementations

and does the Sema on their body after the entire 
class/category @implementation is seen. This change allows messaging 
of forward private methods, as well as, access to 
synthesized ivars of properties with foward synthesize
declarations; among others. In effect, this patch removes
several restrictions placed on objective-c due to in-place
semantics processing of methods.
This is part of // rdar://8843851.

llvm-svn: 138865
This commit is contained in:
Fariborz Jahanian 2011-08-31 17:37:55 +00:00
parent cdef71f4f3
commit bd0642fede
19 changed files with 136 additions and 53 deletions

View File

@ -1022,6 +1022,7 @@ private:
void ParseLexedMethodDef(LexedMethod &LM);
void ParseLexedMemberInitializers(ParsingClass &Class);
void ParseLexedMemberInitializer(LateParsedMemberInitializer &MI);
Decl *ParseLexedObjCMethodDefs(LexedMethod &LM);
bool ConsumeAndStoreUntil(tok::TokenKind T1,
CachedTokens &Toks,
bool StopAtSemi = true,
@ -1080,9 +1081,11 @@ private:
Decl *ObjCImpDecl;
SmallVector<Decl *, 4> PendingObjCImpDecl;
typedef SmallVector<LexedMethod*, 2> LateParsedObjCMethodContainer;
LateParsedObjCMethodContainer LateParsedObjCMethods;
Decl *ParseObjCAtImplementationDeclaration(SourceLocation atLoc);
Decl *ParseObjCAtEndDeclaration(SourceRange atEnd);
DeclGroupPtrTy ParseObjCAtEndDeclaration(SourceRange atEnd);
Decl *ParseObjCAtAliasDeclaration(SourceLocation atLoc);
Decl *ParseObjCPropertySynthesize(SourceLocation atLoc);
Decl *ParseObjCPropertyDynamic(SourceLocation atLoc);

View File

@ -1958,6 +1958,10 @@ public:
AddMethodToGlobalPool(Method, impl, /*instance*/false);
}
/// AddAnyMethodToGlobalPool - Add any method, instance or factory to global
/// pool.
void AddAnyMethodToGlobalPool(Decl *D);
/// LookupInstanceMethodInGlobalPool - Returns the method and warns if
/// there are multiple signatures.
ObjCMethodDecl *LookupInstanceMethodInGlobalPool(Selector Sel, SourceRange R,

View File

@ -56,7 +56,7 @@ Parser::DeclGroupPtrTy Parser::ParseObjCAtDirectives() {
SingleDecl = ParseObjCAtImplementationDeclaration(AtLoc);
break;
case tok::objc_end:
SingleDecl = ParseObjCAtEndDeclaration(AtLoc);
return ParseObjCAtEndDeclaration(AtLoc);
break;
case tok::objc_compatibility_alias:
SingleDecl = ParseObjCAtAliasDeclaration(AtLoc);
@ -1395,11 +1395,19 @@ Decl *Parser::ParseObjCAtImplementationDeclaration(
return 0;
}
Decl *Parser::ParseObjCAtEndDeclaration(SourceRange atEnd) {
Parser::DeclGroupPtrTy
Parser::ParseObjCAtEndDeclaration(SourceRange atEnd) {
assert(Tok.isObjCAtKeyword(tok::objc_end) &&
"ParseObjCAtEndDeclaration(): Expected @end");
Decl *Result = ObjCImpDecl;
ConsumeToken(); // the "end" identifier
SmallVector<Decl *, 8> DeclsInGroup;
for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i) {
Decl *D = ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i]);
DeclsInGroup.push_back(D);
}
LateParsedObjCMethods.clear();
DeclsInGroup.push_back(ObjCImpDecl);
if (ObjCImpDecl) {
Actions.ActOnAtEnd(getCurScope(), atEnd);
ObjCImpDecl = 0;
@ -1409,7 +1417,8 @@ Decl *Parser::ParseObjCAtEndDeclaration(SourceRange atEnd) {
// missing @implementation
Diag(atEnd.getBegin(), diag::err_expected_implementation);
}
return Result;
return Actions.BuildDeclaratorGroup(
DeclsInGroup.data(), DeclsInGroup.size(), false);
}
Parser::DeclGroupPtrTy Parser::FinishPendingObjCActions() {
@ -1772,35 +1781,19 @@ Decl *Parser::ParseObjCMethodDefinition() {
if (Tok.isNot(tok::l_brace))
return 0;
}
SourceLocation BraceLoc = Tok.getLocation();
// Enter a scope for the method body.
ParseScope BodyScope(this,
Scope::ObjCMethodScope|Scope::FnScope|Scope::DeclScope);
// Tell the actions module that we have entered a method definition with the
// specified Declarator for the method.
Actions.ActOnStartOfObjCMethodDef(getCurScope(), MDecl);
if (PP.isCodeCompletionEnabled()) {
if (trySkippingFunctionBodyForCodeCompletion()) {
BodyScope.Exit();
return Actions.ActOnFinishFunctionBody(MDecl, 0);
}
}
StmtResult FnBody(ParseCompoundStatementBody());
// If the function body could not be parsed, make a bogus compoundstmt.
if (FnBody.isInvalid())
FnBody = Actions.ActOnCompoundStmt(BraceLoc, BraceLoc,
MultiStmtArg(Actions), false);
// Leave the function body scope.
BodyScope.Exit();
// TODO: Pass argument information.
Actions.ActOnFinishFunctionBody(MDecl, FnBody.take());
// Allow the rest of sema to find private method decl implementations.
if (MDecl)
Actions.AddAnyMethodToGlobalPool(MDecl);
// Consume the tokens and store them for later parsing.
LexedMethod* LM = new LexedMethod(this, MDecl);
LateParsedObjCMethods.push_back(LM);
CachedTokens &Toks = LM->Toks;
// Begin by storing the '{' token.
Toks.push_back(Tok);
ConsumeBrace();
// Consume everything up to (and including) the matching right brace.
ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
return MDecl;
}
@ -2432,3 +2425,46 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) {
return Owned(Actions.ParseObjCSelectorExpression(Sel, AtLoc, SelectorLoc,
LParenLoc, RParenLoc));
}
Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) {
assert(!LM.Toks.empty() && "ParseLexedObjCMethodDef - Empty body!");
// Append the current token at the end of the new token stream so that it
// doesn't get lost.
LM.Toks.push_back(Tok);
PP.EnterTokenStream(LM.Toks.data(), LM.Toks.size(), true, false);
// MDecl might be null due to error in method prototype, etc.
Decl *MDecl = LM.D;
// Consume the previously pushed token.
ConsumeAnyToken();
assert(Tok.is(tok::l_brace) && "Inline objective-c method not starting with '{'");
SourceLocation BraceLoc = Tok.getLocation();
// Enter a scope for the method body.
ParseScope BodyScope(this,
Scope::ObjCMethodScope|Scope::FnScope|Scope::DeclScope);
// Tell the actions module that we have entered a method definition with the
// specified Declarator for the method.
Actions.ActOnStartOfObjCMethodDef(getCurScope(), MDecl);
if (PP.isCodeCompletionEnabled()) {
if (trySkippingFunctionBodyForCodeCompletion()) {
BodyScope.Exit();
return Actions.ActOnFinishFunctionBody(MDecl, 0);
}
}
StmtResult FnBody(ParseCompoundStatementBody());
// If the function body could not be parsed, make a bogus compoundstmt.
if (FnBody.isInvalid())
FnBody = Actions.ActOnCompoundStmt(BraceLoc, BraceLoc,
MultiStmtArg(Actions), false);
// Leave the function body scope.
BodyScope.Exit();
return Actions.ActOnFinishFunctionBody(MDecl, FnBody.take());
}

View File

@ -214,6 +214,20 @@ static void DiagnoseObjCImplementedDeprecations(Sema &S,
}
}
/// AddAnyMethodToGlobalPool - Add any method, instance or factory to global
/// pool.
void Sema::AddAnyMethodToGlobalPool(Decl *D) {
ObjCMethodDecl *MDecl = dyn_cast_or_null<ObjCMethodDecl>(D);
// If we don't have a valid method decl, simply return.
if (!MDecl)
return;
if (MDecl->isInstanceMethod())
AddInstanceMethodToGlobalPool(MDecl, true);
else
AddFactoryMethodToGlobalPool(MDecl, true);
}
/// ActOnStartOfObjCMethodDef - This routine sets up parameters; invisible
/// and user declared, in the method definition's AST.
void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) {
@ -224,12 +238,6 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) {
if (!MDecl)
return;
// Allow the rest of sema to find private method decl implementations.
if (MDecl->isInstanceMethod())
AddInstanceMethodToGlobalPool(MDecl, true);
else
AddFactoryMethodToGlobalPool(MDecl, true);
// Allow all of Sema to see that we are entering a method definition.
PushDeclContext(FnBodyScope, MDecl);
PushFunctionScope();

View File

@ -1,6 +1,7 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result
// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t
// RUN: diff %t %s.result
// XFAIL: *
#include "Common.h"

View File

@ -1,5 +1,6 @@
/* The run lines are below, because this test is line- and
column-number sensitive. */
// XFAIL: *
@interface MyClass { int ivar; }
- (int)myMethod:(int)arg;
@end

View File

@ -1,4 +1,5 @@
// This test is line- and column-sensitive, so test commands are at the bottom.
// XFAIL: *
@protocol P
- (int)method:(id)param1;
@end

View File

@ -1,3 +1,4 @@
// XFAIL: *
typedef signed char BOOL;
#define YES ((BOOL)1)
#define NO ((BOOL)0)

View File

@ -1,6 +1,7 @@
// Note: the run lines follow their respective tests, since line/column
// matter in this test.
// XFAIL: *
@interface A
+ (id)alloc;
+ (id)init;

View File

@ -1,5 +1,6 @@
// Note: the run lines follow their respective tests, since line/column
// matter in this test.
// XFAIL: *
#define nil (void*)0
@protocol FooTestProtocol
+ protocolClassMethod;

View File

@ -1,5 +1,6 @@
/* Run lines are at the end, since line/column matter in this test. */
// XFAIL: *
@interface A
- (void)method:(int)x;
@end

View File

@ -1,5 +1,6 @@
// Note: the run lines follow their respective tests, since line/column
// matter in this test.
// XFAIL: *
typedef int Bool;

View File

@ -1,5 +1,6 @@
// Note: this test is line- and column-sensitive. Test commands are at
// the end.
// XFAIL: *
@interface A

View File

@ -1,10 +1,9 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
- (NSDictionary*) _executeScript:(NSString *)source { // expected-error 2 {{expected a type}} \
// expected-error {{missing context for method declaration}}
Exit: [nilArgs release]; // expected-error {{use of undeclared identifier}}
- (NSDictionary*) _executeScript:(NSString *)source { // expected-error 2 {{expected a type}} \
// expected-error {{missing context for method declaration}}
Exit: [nilArgs release];
}
- (NSDictionary *) _setupKernelStandardMode:(NSString *)source { // expected-error 2 {{expected a type}} \
expected-error {{missing context for method declaration}} \
expected-note{{to match this '{'}}
Exit: if(_ciKernel && !success ) { // expected-error {{use of undeclared identifier}} // expected-error 2 {{expected}} expected-note{{to match this '{'}} expected-error{{use of undeclared identifier 'success'}}
// expected-error {{missing context for method declaration}}
Exit: if(_ciKernel && !success ) {

View File

@ -1,5 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
- im0 { // expected-note{{to match this '{'}} expected-error{{missing context for method declaration}}
- im0 { // expected-error{{missing context for method declaration}}
int a; return 0;
// expected-error{{expected '}'}}

View File

@ -10,7 +10,7 @@
@end
@implementation I1 // expected-error {{'@end' is missing in implementation context}}
-(void) im0 { self = [super init]; } // expected-warning {{nstance method '-init' not found }}
-(void) im0 { self = [super init]; }
@interface I2 : I0
- I2meth;

View File

@ -0,0 +1,25 @@
// RUN: %clang_cc1 -fsyntax-only -fobjc-nonfragile-abi -verify %s
// rdar://8843851
int* global;
@interface I
- (void) Meth;
@property int prop;
@property int prop1;
@end
@implementation I
+ (void) _defaultMinSize { };
static void _initCommon() {
Class graphicClass;
[graphicClass _defaultMinSize];
}
- (void) Meth { [self Forw]; } // No warning now
- (void) Forw {}
- (int) func { return prop; } // compiles - synthesized ivar will be accessible here.
- (int)get_g { return global; } // No warning here - synthesized ivar will be accessible here.
@synthesize prop;
@synthesize prop1=global;
@end

View File

@ -18,7 +18,7 @@ int bar;
@end
@implementation I
- (int) Meth { return PROP; } // expected-note 2{{'PROP' declared here}}
- (int) Meth { return PROP; }
@dynamic PROP1;
- (int) Meth1 { return PROP1; } // expected-error {{use of undeclared identifier 'PROP1'}}
@ -32,7 +32,7 @@ int bar;
- (int) Meth4 { return PROP4; }
@synthesize PROP4=PROP4;
- (int) Meth5 { return bar; } // expected-error {{use of undeclared identifier 'bar'}}
- (int) Meth5 { return bar; }
@synthesize bar = _bar;
- (int) Meth6 { return bar1; }
@ -45,6 +45,6 @@ int bar;
@implementation I(r8251648)
- (int) Meth1: (int) bar {
return bar; // expected-warning {{local declaration of 'bar' hides instance variable}}
return bar;
}
@end

View File

@ -18,7 +18,7 @@ typedef struct objc_selector *SEL;
+ (void) methodD
{
SEL d = @selector(methodD); /* Ok */
SEL e = @selector(methodE); // expected-warning {{undeclared selector 'methodE'}}
SEL e = @selector(methodE);
}
- (void) methodE