Make parsing of objc @implementations more robust.

Parsing of @implementations was based on modifying global state from
the parser; the logic for late parsing of methods was spread in multiple places
making it difficult to have a robust error recovery.

  -it was difficult to ensure that we don't neglect parsing the lexed methods.
  -it was difficult to setup the original objc container context for parsing the lexed methods
   after completing ParseObjCAtImplementationDeclaration and returning to top level context.

Enhance parsing of @implementations by centralizing it in Parser::ParseObjCAtImplementationDeclaration().
ParseObjCAtImplementationDeclaration now returns only after an @implementation is fully parsed;
all the data and logic for late parsing of methods is now in one place.

This allows us to provide code-completion for late parsed methods with mis-matched braces.
rdar://10775381

llvm-svn: 149987
This commit is contained in:
Argyrios Kyrtzidis 2012-02-07 16:50:53 +00:00
parent 47ddf604b9
commit b6c6a58366
10 changed files with 168 additions and 104 deletions

View File

@ -258,8 +258,6 @@ public:
/// the EOF was encountered.
bool ParseTopLevelDecl(DeclGroupPtrTy &Result);
DeclGroupPtrTy FinishPendingObjCActions();
private:
//===--------------------------------------------------------------------===//
// Low-Level token peeking and consumption methods.
@ -404,9 +402,6 @@ private:
Tok.setKind(tok::eof);
}
/// \brief Clear and free the cached objc methods.
void clearLateParsedObjCMethods();
/// \brief Handle the annotation token produced for #pragma unused(...)
void HandlePragmaUnused();
@ -1227,12 +1222,28 @@ private:
DeclGroupPtrTy ParseObjCAtProtocolDeclaration(SourceLocation atLoc,
ParsedAttributes &prefixAttrs);
Decl *ObjCImpDecl;
SmallVector<Decl *, 4> PendingObjCImpDecl;
typedef SmallVector<LexedMethod*, 2> LateParsedObjCMethodContainer;
LateParsedObjCMethodContainer LateParsedObjCMethods;
struct ObjCImplParsingDataRAII {
Parser &P;
Decl *Dcl;
typedef SmallVector<LexedMethod*, 8> LateParsedObjCMethodContainer;
LateParsedObjCMethodContainer LateParsedObjCMethods;
Decl *ParseObjCAtImplementationDeclaration(SourceLocation AtLoc);
ObjCImplParsingDataRAII(Parser &parser, Decl *D)
: P(parser), Dcl(D) {
P.CurParsedObjCImpl = this;
Finished = false;
}
~ObjCImplParsingDataRAII();
void finish(SourceRange AtEnd);
bool isFinished() const { return Finished; }
private:
bool Finished;
};
ObjCImplParsingDataRAII *CurParsedObjCImpl;
DeclGroupPtrTy ParseObjCAtImplementationDeclaration(SourceLocation AtLoc);
DeclGroupPtrTy ParseObjCAtEndDeclaration(SourceRange atEnd);
Decl *ParseObjCAtAliasDeclaration(SourceLocation atLoc);
Decl *ParseObjCPropertySynthesize(SourceLocation atLoc);

View File

@ -96,10 +96,6 @@ void clang::ParseAST(Sema &S, bool PrintStats) {
if (Abort)
return;
// Check for any pending objective-c implementation decl.
while ((ADecl = P.FinishPendingObjCActions()))
Consumer->HandleTopLevelDecl(ADecl.get());
// Process any TopLevelDecls generated by #pragma weak.
for (SmallVector<Decl*,2>::iterator

View File

@ -1723,7 +1723,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
: Sema::PCC_Template;
else if (DSContext == DSC_class)
CCC = Sema::PCC_Class;
else if (ObjCImpDecl)
else if (CurParsedObjCImpl)
CCC = Sema::PCC_ObjCImplementation;
Actions.CodeCompleteOrdinaryName(getCurScope(), CCC);

View File

@ -1416,7 +1416,8 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
if (!LHS.isInvalid())
LHS = Actions.ActOnMemberAccessExpr(getCurScope(), LHS.take(), OpLoc,
OpKind, SS, TemplateKWLoc, Name,
ObjCImpDecl, Tok.is(tok::l_paren));
CurParsedObjCImpl ? CurParsedObjCImpl->Dcl : 0,
Tok.is(tok::l_paren));
break;
}
case tok::plusplus: // postfix-expression: postfix-expression '++'

View File

@ -52,8 +52,7 @@ Parser::DeclGroupPtrTy Parser::ParseObjCAtDirectives() {
return ParseObjCAtProtocolDeclaration(AtLoc, attrs);
}
case tok::objc_implementation:
SingleDecl = ParseObjCAtImplementationDeclaration(AtLoc);
break;
return ParseObjCAtImplementationDeclaration(AtLoc);
case tok::objc_end:
return ParseObjCAtEndDeclaration(AtLoc);
case tok::objc_compatibility_alias:
@ -122,15 +121,17 @@ void Parser::CheckNestedObjCContexts(SourceLocation AtLoc)
if (ock == Sema::OCK_None)
return;
Decl *Decl = Actions.ActOnAtEnd(getCurScope(), AtLoc);
Decl *Decl = Actions.getObjCDeclContext();
if (CurParsedObjCImpl) {
CurParsedObjCImpl->finish(AtLoc);
} else {
Actions.ActOnAtEnd(getCurScope(), AtLoc);
}
Diag(AtLoc, diag::err_objc_missing_end)
<< FixItHint::CreateInsertion(AtLoc, "@end\n");
if (Decl)
Diag(Decl->getLocStart(), diag::note_objc_container_start)
<< (int) ock;
if (!PendingObjCImpDecl.empty())
PendingObjCImpDecl.pop_back();
ObjCImpDecl = 0;
}
///
@ -403,7 +404,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
// Code completion within an Objective-C interface.
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteOrdinaryName(getCurScope(),
ObjCImpDecl? Sema::PCC_ObjCImplementation
CurParsedObjCImpl? Sema::PCC_ObjCImplementation
: Sema::PCC_ObjCInterface);
return cutOffParsing();
}
@ -1441,7 +1442,8 @@ Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc,
///
/// objc-category-implementation-prologue:
/// @implementation identifier ( identifier )
Decl *Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
Parser::DeclGroupPtrTy
Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
assert(Tok.isObjCAtKeyword(tok::objc_implementation) &&
"ParseObjCAtImplementationDeclaration(): Expected @implementation");
CheckNestedObjCContexts(AtLoc);
@ -1451,16 +1453,17 @@ Decl *Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCImplementationDecl(getCurScope());
cutOffParsing();
return 0;
return DeclGroupPtrTy();
}
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected_ident); // missing class or category name.
return 0;
return DeclGroupPtrTy();
}
// We have a class or category name - consume it.
IdentifierInfo *nameId = Tok.getIdentifierInfo();
SourceLocation nameLoc = ConsumeToken(); // consume class or category name
Decl *ObjCImpDecl = 0;
if (Tok.is(tok::l_paren)) {
// we have a category implementation.
@ -1471,7 +1474,7 @@ Decl *Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteObjCImplementationCategory(getCurScope(), nameId, nameLoc);
cutOffParsing();
return 0;
return DeclGroupPtrTy();
}
if (Tok.is(tok::identifier)) {
@ -1479,45 +1482,59 @@ Decl *Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
categoryLoc = ConsumeToken();
} else {
Diag(Tok, diag::err_expected_ident); // missing category name.
return 0;
return DeclGroupPtrTy();
}
if (Tok.isNot(tok::r_paren)) {
Diag(Tok, diag::err_expected_rparen);
SkipUntil(tok::r_paren, false); // don't stop at ';'
return 0;
return DeclGroupPtrTy();
}
rparenLoc = ConsumeParen();
Decl *ImplCatType = Actions.ActOnStartCategoryImplementation(
ObjCImpDecl = Actions.ActOnStartCategoryImplementation(
AtLoc, nameId, nameLoc, categoryId,
categoryLoc);
ObjCImpDecl = ImplCatType;
PendingObjCImpDecl.push_back(ObjCImpDecl);
return 0;
}
// We have a class implementation
SourceLocation superClassLoc;
IdentifierInfo *superClassId = 0;
if (Tok.is(tok::colon)) {
// We have a super class
ConsumeToken();
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected_ident); // missing super class name.
return 0;
} else {
// We have a class implementation
SourceLocation superClassLoc;
IdentifierInfo *superClassId = 0;
if (Tok.is(tok::colon)) {
// We have a super class
ConsumeToken();
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected_ident); // missing super class name.
return DeclGroupPtrTy();
}
superClassId = Tok.getIdentifierInfo();
superClassLoc = ConsumeToken(); // Consume super class name
}
superClassId = Tok.getIdentifierInfo();
superClassLoc = ConsumeToken(); // Consume super class name
ObjCImpDecl = Actions.ActOnStartClassImplementation(
AtLoc, nameId, nameLoc,
superClassId, superClassLoc);
if (Tok.is(tok::l_brace)) // we have ivars
ParseObjCClassInstanceVariables(ObjCImpDecl, tok::objc_private, AtLoc);
}
Decl *ImplClsType = Actions.ActOnStartClassImplementation(
AtLoc, nameId, nameLoc,
superClassId, superClassLoc);
assert(ObjCImpDecl);
if (Tok.is(tok::l_brace)) // we have ivars
ParseObjCClassInstanceVariables(ImplClsType, tok::objc_private, AtLoc);
SmallVector<Decl *, 8> DeclsInGroup;
ObjCImpDecl = ImplClsType;
PendingObjCImpDecl.push_back(ObjCImpDecl);
return 0;
{
ObjCImplParsingDataRAII ObjCImplParsing(*this, ObjCImpDecl);
while (!ObjCImplParsing.isFinished() && Tok.isNot(tok::eof)) {
ParsedAttributesWithRange attrs(AttrFactory);
MaybeParseCXX0XAttributes(attrs);
MaybeParseMicrosoftAttributes(attrs);
if (DeclGroupPtrTy DGP = ParseExternalDeclaration(attrs)) {
DeclGroupRef DG = DGP.get();
DeclsInGroup.append(DG.begin(), DG.end());
}
}
}
DeclsInGroup.push_back(ObjCImpDecl);
return Actions.BuildDeclaratorGroup(
DeclsInGroup.data(), DeclsInGroup.size(), false);
}
Parser::DeclGroupPtrTy
@ -1525,51 +1542,44 @@ Parser::ParseObjCAtEndDeclaration(SourceRange atEnd) {
assert(Tok.isObjCAtKeyword(tok::objc_end) &&
"ParseObjCAtEndDeclaration(): Expected @end");
ConsumeToken(); // the "end" identifier
SmallVector<Decl *, 8> DeclsInGroup;
Actions.DefaultSynthesizeProperties(getCurScope(), ObjCImpDecl);
for (size_t i = 0; i < LateParsedObjCMethods.size(); ++i) {
Decl *D = ParseLexedObjCMethodDefs(*LateParsedObjCMethods[i]);
if (D)
DeclsInGroup.push_back(D);
}
DeclsInGroup.push_back(ObjCImpDecl);
if (ObjCImpDecl) {
Actions.ActOnAtEnd(getCurScope(), atEnd);
PendingObjCImpDecl.pop_back();
}
if (CurParsedObjCImpl)
CurParsedObjCImpl->finish(atEnd);
else
// missing @implementation
Diag(atEnd.getBegin(), diag::err_expected_objc_container);
clearLateParsedObjCMethods();
ObjCImpDecl = 0;
return Actions.BuildDeclaratorGroup(
DeclsInGroup.data(), DeclsInGroup.size(), false);
return DeclGroupPtrTy();
}
Parser::DeclGroupPtrTy Parser::FinishPendingObjCActions() {
Actions.DiagnoseUseOfUnimplementedSelectors();
if (PendingObjCImpDecl.empty())
return Actions.ConvertDeclToDeclGroup(0);
Decl *ImpDecl = PendingObjCImpDecl.pop_back_val();
Actions.ActOnAtEnd(getCurScope(), SourceRange(Tok.getLocation()));
Diag(Tok, diag::err_objc_missing_end)
<< FixItHint::CreateInsertion(Tok.getLocation(), "\n@end\n");
if (ImpDecl)
Diag(ImpDecl->getLocStart(), diag::note_objc_container_start)
<< Sema::OCK_Implementation;
return Actions.ConvertDeclToDeclGroup(ImpDecl);
Parser::ObjCImplParsingDataRAII::~ObjCImplParsingDataRAII() {
if (!Finished) {
finish(P.Tok.getLocation());
if (P.Tok.is(tok::eof)) {
P.Diag(P.Tok, diag::err_objc_missing_end)
<< FixItHint::CreateInsertion(P.Tok.getLocation(), "\n@end\n");
P.Diag(Dcl->getLocStart(), diag::note_objc_container_start)
<< Sema::OCK_Implementation;
}
}
P.CurParsedObjCImpl = 0;
assert(LateParsedObjCMethods.empty());
}
void Parser::clearLateParsedObjCMethods() {
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.Actions.ActOnAtEnd(P.getCurScope(), AtEnd);
/// \brief Clear and free the cached objc methods.
for (LateParsedObjCMethodContainer::iterator
I = LateParsedObjCMethods.begin(),
E = LateParsedObjCMethods.end(); I != E; ++I)
delete *I;
LateParsedObjCMethods.clear();
Finished = true;
}
/// compatibility-alias-decl:
@ -1908,7 +1918,7 @@ Decl *Parser::ParseObjCMethodDefinition() {
// parse optional ';'
if (Tok.is(tok::semi)) {
if (ObjCImpDecl) {
if (CurParsedObjCImpl) {
Diag(Tok, diag::warn_semicolon_before_method_body)
<< FixItHint::CreateRemoval(Tok.getLocation());
}
@ -1926,19 +1936,32 @@ Decl *Parser::ParseObjCMethodDefinition() {
if (Tok.isNot(tok::l_brace))
return 0;
}
if (!MDecl) {
ConsumeBrace();
SkipUntil(tok::r_brace, /*StopAtSemi=*/false);
return 0;
}
// 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);
Actions.AddAnyMethodToGlobalPool(MDecl);
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);
} else {
ConsumeBrace();
SkipUntil(tok::r_brace, /*StopAtSemi=*/false);
}
return MDecl;
}

View File

@ -39,7 +39,7 @@ Parser::Parser(Preprocessor &pp, Sema &actions)
Actions.CurScope = 0;
NumCachedScopes = 0;
ParenCount = BracketCount = BraceCount = 0;
ObjCImpDecl = 0;
CurParsedObjCImpl = 0;
// Add #pragma handlers. These are removed and destroyed in the
// destructor.
@ -367,8 +367,6 @@ Parser::~Parser() {
it != LateParsedTemplateMap.end(); ++it)
delete it->second;
clearLateParsedObjCMethods();
// Remove the pragma handlers we installed.
PP.RemovePragmaHandler(AlignHandler.get());
AlignHandler.reset();
@ -595,7 +593,7 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
break;
case tok::code_completion:
Actions.CodeCompleteOrdinaryName(getCurScope(),
ObjCImpDecl? Sema::PCC_ObjCImplementation
CurParsedObjCImpl? Sema::PCC_ObjCImplementation
: Sema::PCC_Namespace);
cutOffParsing();
return DeclGroupPtrTy();

View File

@ -418,6 +418,8 @@ void Sema::ActOnEndOfTranslationUnit() {
// Only complete translation units define vtables and perform implicit
// instantiations.
if (TUKind == TU_Complete) {
DiagnoseUseOfUnimplementedSelectors();
// If any dynamic classes have their key function defined within
// this translation unit, then those vtables are considered "used" and must
// be emitted.

View File

@ -0,0 +1,19 @@
@interface I
-(void)foo;
@end
struct S {
int x,y;
};
@implementation I
-(void) foo {
struct S s;
if (1) {
s.
}
@end
// RUN: c-index-test -code-completion-at=%s:13:7 -fobjc-nonfragile-abi %s | FileCheck %s
// CHECK: FieldDecl:{ResultType int}{TypedText x}
// CHECK: FieldDecl:{ResultType int}{TypedText y}

View File

@ -27,3 +27,17 @@ void foo() {
return self;
}
@end
@interface I
@end
@interface I2
@end
@implementation I // expected-note {{started here}}
-(void) foo {}
@implementation I2 // expected-error {{missing '@end'}}
-(void) foo2 {}
@end
@end // expected-error {{'@end' must appear in an Objective-C context}}

View File

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