From 6c89eafc90f5c51a0bf185a993961170aee530c2 Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Mon, 2 Jul 2012 23:37:09 +0000 Subject: [PATCH] objective-c: just as we have done for method definitions, c-functions declared in implementation should have their parsing delayed until the end so, they can access forward declared private methods. // rdar://10387088 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159626 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Parser.h | 6 ++- include/clang/Sema/Sema.h | 6 ++- lib/Parse/ParseDecl.cpp | 8 +++ lib/Parse/ParseObjc.cpp | 63 +++++++++++++++--------- lib/Parse/Parser.cpp | 6 ++- lib/Sema/SemaDeclObjC.cpp | 34 +++++++++++-- test/SemaObjC/delay-parsing-cfunctions.m | 32 ++++++++++++ 7 files changed, 123 insertions(+), 32 deletions(-) create mode 100644 test/SemaObjC/delay-parsing-cfunctions.m diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 8fd597e1cf..66e8662aeb 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1019,7 +1019,7 @@ private: void ParseLexedMethodDef(LexedMethod &LM); void ParseLexedMemberInitializers(ParsingClass &Class); void ParseLexedMemberInitializer(LateParsedMemberInitializer &MI); - Decl *ParseLexedObjCMethodDefs(LexedMethod &LM); + void ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod); bool ConsumeAndStoreFunctionPrologue(CachedTokens &Toks); bool ConsumeAndStoreUntil(tok::TokenKind T1, CachedTokens &Toks, @@ -1084,11 +1084,12 @@ private: struct ObjCImplParsingDataRAII { Parser &P; Decl *Dcl; + bool HasCFunction; typedef SmallVector LateParsedObjCMethodContainer; LateParsedObjCMethodContainer LateParsedObjCMethods; ObjCImplParsingDataRAII(Parser &parser, Decl *D) - : P(parser), Dcl(D) { + : P(parser), Dcl(D), HasCFunction(false) { P.CurParsedObjCImpl = this; Finished = false; } @@ -1101,6 +1102,7 @@ private: bool Finished; }; ObjCImplParsingDataRAII *CurParsedObjCImpl; + void StashAwayMethodOrFunctionBodyTokens(Decl *MDecl); DeclGroupPtrTy ParseObjCAtImplementationDeclaration(SourceLocation AtLoc); DeclGroupPtrTy ParseObjCAtEndDeclaration(SourceRange atEnd); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index dad6e3aee7..2f06ee696a 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1317,7 +1317,11 @@ public: void CheckForFunctionRedefinition(FunctionDecl *FD); Decl *ActOnStartOfFunctionDef(Scope *S, Declarator &D); Decl *ActOnStartOfFunctionDef(Scope *S, Decl *D); - void ActOnStartOfObjCMethodDef(Scope *S, Decl *D); + void ActOnStartOfObjCMethodOrCFunctionDef(Scope *S, Decl *D, + bool parseMethod); + bool isObjCMethodDecl(Decl *D) { + return D && isa(D); + } void computeNRVO(Stmt *Body, sema::FunctionScopeInfo *Scope); Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index c61f5543a6..ac221117b5 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1379,6 +1379,14 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, bool ExpectSemi = Context != Declarator::ForContext; + if (CurParsedObjCImpl && D.isFunctionDeclarator() && + Tok.is(tok::l_brace)) { + // Consume the tokens and store them for later parsing. + StashAwayMethodOrFunctionBodyTokens(FirstDecl); + CurParsedObjCImpl->HasCFunction = true; + ExpectSemi = false; + } + // If we don't have a comma, it is either the end of the list (a ';') or an // error, bail out. while (Tok.is(tok::comma)) { diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 2540ad940d..35e4222c52 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1575,10 +1575,16 @@ void Parser::ObjCImplParsingDataRAII::finish(SourceRange AtEnd) { assert(!Finished); P.Actions.DefaultSynthesizeProperties(P.getCurScope(), Dcl); for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i) - P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i]); + P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i], + true/*Methods*/); P.Actions.ActOnAtEnd(P.getCurScope(), AtEnd); + if (HasCFunction) + for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i) + P.ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i], + false/*c-functions*/); + /// \brief Clear and free the cached objc methods. for (LateParsedObjCMethodContainer::iterator I = LateParsedObjCMethods.begin(), @@ -1915,6 +1921,19 @@ Parser::ParseObjCAutoreleasePoolStmt(SourceLocation atLoc) { AutoreleasePoolBody.take()); } +/// StashAwayMethodOrFunctionBodyTokens - Consume the tokens and store them +/// for later parsing. +void Parser::StashAwayMethodOrFunctionBodyTokens(Decl *MDecl) { + LexedMethod* LM = new LexedMethod(this, MDecl); + CurParsedObjCImpl->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); +} + /// objc-method-def: objc-method-proto ';'[opt] '{' body '}' /// Decl *Parser::ParseObjCMethodDefinition() { @@ -1955,15 +1974,7 @@ Decl *Parser::ParseObjCMethodDefinition() { if (CurParsedObjCImpl) { // Consume the tokens and store them for later parsing. - LexedMethod* LM = new LexedMethod(this, MDecl); - CurParsedObjCImpl->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); - + StashAwayMethodOrFunctionBodyTokens(MDecl); } else { ConsumeBrace(); SkipUntil(tok::r_brace, /*StopAtSemi=*/false); @@ -2821,8 +2832,15 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) { T.getCloseLocation())); } -Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) { - +void Parser::ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod) { + // MCDecl might be null due to error in method or c-function prototype, etc. + Decl *MCDecl = LM.D; + bool skip = MCDecl && + ((parseMethod && !Actions.isObjCMethodDecl(MCDecl)) || + (!parseMethod && Actions.isObjCMethodDecl(MCDecl))); + if (skip) + return; + // Save the current token position. SourceLocation OrigLoc = Tok.getLocation(); @@ -2832,24 +2850,25 @@ Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) { 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. + // Enter a scope for the method or c-fucntion body. ParseScope BodyScope(this, - Scope::ObjCMethodScope|Scope::FnScope|Scope::DeclScope); + parseMethod + ? Scope::ObjCMethodScope|Scope::FnScope|Scope::DeclScope + : 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); + // Tell the actions module that we have entered a method or c-function definition + // with the specified Declarator for the method/function. + Actions.ActOnStartOfObjCMethodOrCFunctionDef(getCurScope(), MCDecl, parseMethod); if (SkipFunctionBodies && trySkippingFunctionBody()) { BodyScope.Exit(); - return Actions.ActOnFinishFunctionBody(MDecl, 0); + (void)Actions.ActOnFinishFunctionBody(MCDecl, 0); + return; } StmtResult FnBody(ParseCompoundStatementBody()); @@ -2864,7 +2883,7 @@ Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) { // Leave the function body scope. BodyScope.Exit(); - MDecl = Actions.ActOnFinishFunctionBody(MDecl, FnBody.take()); + (void)Actions.ActOnFinishFunctionBody(MCDecl, FnBody.take()); if (Tok.getLocation() != OrigLoc) { // Due to parsing error, we either went over the cached tokens or @@ -2878,5 +2897,5 @@ Decl *Parser::ParseLexedObjCMethodDefs(LexedMethod &LM) { ConsumeAnyToken(); } - return MDecl; + return; } diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index bec6dd717c..c2c4c90090 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -766,14 +766,16 @@ bool Parser::isDeclarationAfterDeclarator() { if (KW.is(tok::kw_default) || KW.is(tok::kw_delete)) return false; } - + return Tok.is(tok::equal) || // int X()= -> not a function def Tok.is(tok::comma) || // int X(), -> not a function def Tok.is(tok::semi) || // int X(); -> not a function def Tok.is(tok::kw_asm) || // int X() __asm__ -> not a function def Tok.is(tok::kw___attribute) || // int X() __attr__ -> not a function def (getLangOpts().CPlusPlus && - Tok.is(tok::l_paren)); // int X(0) -> not a function def [C++] + Tok.is(tok::l_paren)) || // int X(0) -> not a function def [C++] + (CurParsedObjCImpl && + Tok.is(tok::l_brace)); // C-function nested in an @implementation } /// \brief Determine whether the current token, if it occurs after a diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index a673d247ed..7342128715 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -265,12 +265,36 @@ void Sema::AddAnyMethodToGlobalPool(Decl *D) { 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) { - assert(getCurMethodDecl() == 0 && "Method parsing confused"); +/// ActOnStartOfObjCMethodOrCFunctionDef - This routine sets up parameters; invisible +/// and user declared, in the method definition's AST. This routine is also called +/// for C-functions defined in an Objective-c class implementation. +void Sema::ActOnStartOfObjCMethodOrCFunctionDef(Scope *FnBodyScope, Decl *D, + bool parseMethod) { + assert((getCurMethodDecl() == 0 && getCurFunctionDecl() == 0) && + "Method/c-function parsing confused"); + if (!parseMethod) { + FunctionDecl *FDecl = FDecl = dyn_cast_or_null(D); + // If we don't have a valid c-function decl, simply return. + if (!FDecl) + return; + PushDeclContext(FnBodyScope, FDecl); + PushFunctionScope(); + + for (FunctionDecl::param_const_iterator PI = FDecl->param_begin(), + E = FDecl->param_end(); PI != E; ++PI) { + ParmVarDecl *Param = (*PI); + if (!Param->isInvalidDecl() && + RequireCompleteType(Param->getLocation(), Param->getType(), + diag::err_typecheck_decl_incomplete_type)) + Param->setInvalidDecl(); + if ((*PI)->getIdentifier()) + PushOnScopeChains(*PI, FnBodyScope); + } + return; + } + ObjCMethodDecl *MDecl = dyn_cast_or_null(D); - + // If we don't have a valid method decl, simply return. if (!MDecl) return; diff --git a/test/SemaObjC/delay-parsing-cfunctions.m b/test/SemaObjC/delay-parsing-cfunctions.m new file mode 100644 index 0000000000..dcebcfa947 --- /dev/null +++ b/test/SemaObjC/delay-parsing-cfunctions.m @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -fsyntax-only -Werror -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -Werror -verify -Wno-objc-root-class %s +// rdar://10387088 + +@interface MyClass +- (void)someMethod; +@end + +@implementation MyClass +- (void)someMethod { + [self privateMethod]; // clang already does not warn here +} + +int bar(MyClass * myObject) { + [myObject privateMethod]; + return gorfbar(myObject); +} +- (void)privateMethod { } + +int gorfbar(MyClass * myObject) { + [myObject privateMethod]; + [myObject privateMethod1]; + return getMe + bar(myObject); +} + +- (void)privateMethod1 { + getMe = getMe+1; +} + +static int getMe; + +@end -- 2.40.0