diff options
author | Erik Verbruggen <erikjv@me.com> | 2011-12-06 09:25:23 +0000 |
---|---|---|
committer | Erik Verbruggen <erikjv@me.com> | 2011-12-06 09:25:23 +0000 |
commit | d64251fd56577dd5c78903454632361e094c6dc1 (patch) | |
tree | 998c7c959649aae3022c18f33c834f6c345b45c8 | |
parent | 26b45d86085a125af036dbcf85dad3087b664ab2 (diff) |
Extend warnings for missing '@end'.
Fixes PR2709.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@145928 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticIDs.h | 2 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticParseKinds.td | 9 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 1 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 5 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 19 | ||||
-rw-r--r-- | lib/Parse/ParseObjc.cpp | 67 | ||||
-rw-r--r-- | lib/Sema/SemaDeclObjC.cpp | 47 | ||||
-rw-r--r-- | test/Index/cindex-on-invalid-usrs.m | 2 | ||||
-rw-r--r-- | test/Parser/missing-end-2.m | 12 | ||||
-rw-r--r-- | test/Parser/missing-end-3.m | 4 | ||||
-rw-r--r-- | test/Parser/missing-end.m | 4 | ||||
-rw-r--r-- | test/Parser/objc-missing-impl.m | 2 | ||||
-rw-r--r-- | test/Parser/objc-quirks.m | 4 | ||||
-rw-r--r-- | test/SemaObjC/missing-atend-metadata.m | 12 |
14 files changed, 128 insertions, 62 deletions
diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h index da85ea491b..fd31c17406 100644 --- a/include/clang/Basic/DiagnosticIDs.h +++ b/include/clang/Basic/DiagnosticIDs.h @@ -35,7 +35,7 @@ namespace clang { DIAG_START_FRONTEND = DIAG_START_DRIVER + 100, DIAG_START_LEX = DIAG_START_FRONTEND + 120, DIAG_START_PARSE = DIAG_START_LEX + 300, - DIAG_START_AST = DIAG_START_PARSE + 300, + DIAG_START_AST = DIAG_START_PARSE + 350, DIAG_START_SEMA = DIAG_START_AST + 100, DIAG_START_ANALYSIS = DIAG_START_SEMA + 3000, DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + 100 diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 7d8f21394c..972ae3341f 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -313,7 +313,10 @@ def err_expected_minus_or_plus : Error< "method type specifier must start with '-' or '+'">; def err_objc_no_attributes_on_category : Error< "attributes may not be specified on a category">; -def err_objc_missing_end : Error<"missing @end">; +def err_objc_missing_end : Error<"missing '@end'">; +def note_objc_container_start : Note< + "%select{class|protocol|category|class extension|implementation" + "|category implementation}0 started here">; def warn_objc_protocol_qualifier_missing_id : Warning< "protocol qualifiers without 'id' is archaic">; def err_objc_unknown_at : Error<"expected an Objective-C directive after '@'">; @@ -352,8 +355,8 @@ def err_missing_id_definition : Error<"cannot find definition of 'id'">; def err_missing_proto_definition : Error< "cannot find definition of 'Protocol'">; def err_missing_class_definition : Error<"cannot find definition of 'Class'">; -def err_expected_implementation : Error< - "@end must appear in an @implementation context">; +def err_expected_objc_container : Error< + "'@end' must appear in an Objective-C context">; def error_property_ivar_decl : Error< "property synthesize requires specification of an ivar">; def err_synthesized_property_name : Error< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 4d7f60b8c9..76051f7b64 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -488,7 +488,6 @@ def note_declared_at : Note<"declared here">; def note_method_declared_at : Note<"method declared here">; def err_setter_type_void : Error<"type of setter must be void">; def err_duplicate_method_decl : Error<"duplicate declaration of method %0">; -def err_missing_atend : Error<"'@end' is missing in implementation context">; def err_objc_var_decl_inclass : Error<"cannot declare variable inside @interface or @protocol">; def error_missing_method_context : Error< diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 5dfb99ec07..8e283d1c02 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -716,6 +716,7 @@ public: private: void SuggestParentheses(SourceLocation Loc, unsigned DK, SourceRange ParenRange); + void CheckNestedObjCContexts(SourceLocation AtLoc); /// SkipUntil - Read tokens until we get to the specified token, then consume /// it (unless DontConsume is true). Because we cannot guarantee that the @@ -1204,7 +1205,7 @@ private: // Objective-C External Declarations Parser::DeclGroupPtrTy ParseObjCAtDirectives(); Parser::DeclGroupPtrTy ParseObjCAtClassDeclaration(SourceLocation atLoc); - Decl *ParseObjCAtInterfaceDeclaration(SourceLocation atLoc, + Decl *ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, ParsedAttributes &prefixAttrs); void ParseObjCClassInstanceVariables(Decl *interfaceDecl, tok::ObjCKeywordKind visibility, @@ -1225,7 +1226,7 @@ private: typedef SmallVector<LexedMethod*, 2> LateParsedObjCMethodContainer; LateParsedObjCMethodContainer LateParsedObjCMethods; - Decl *ParseObjCAtImplementationDeclaration(SourceLocation atLoc); + Decl *ParseObjCAtImplementationDeclaration(SourceLocation AtLoc); DeclGroupPtrTy ParseObjCAtEndDeclaration(SourceRange atEnd); Decl *ParseObjCAtAliasDeclaration(SourceLocation atLoc); Decl *ParseObjCPropertySynthesize(SourceLocation atLoc); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index adb00dfcb3..4313ec062d 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -5173,6 +5173,17 @@ public: const MultiLevelTemplateArgumentList &TemplateArgs); // Objective-C declarations. + enum ObjCContainerKind { + OCK_None = -1, + OCK_Interface = 0, + OCK_Protocol, + OCK_Category, + OCK_ClassExtension, + OCK_Implementation, + OCK_CategoryImplementation + }; + ObjCContainerKind getObjCContainerKind() const; + Decl *ActOnStartClassInterface(SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, @@ -5275,10 +5286,10 @@ public: void MatchOneProtocolPropertiesInClass(Decl *CDecl, ObjCProtocolDecl *PDecl); - void ActOnAtEnd(Scope *S, SourceRange AtEnd, - Decl **allMethods = 0, unsigned allNum = 0, - Decl **allProperties = 0, unsigned pNum = 0, - DeclGroupPtrTy *allTUVars = 0, unsigned tuvNum = 0); + Decl *ActOnAtEnd(Scope *S, SourceRange AtEnd, + Decl **allMethods = 0, unsigned allNum = 0, + Decl **allProperties = 0, unsigned pNum = 0, + DeclGroupPtrTy *allTUVars = 0, unsigned tuvNum = 0); Decl *ActOnProperty(Scope *S, SourceLocation AtLoc, FieldDeclarator &FD, ObjCDeclSpec &ODS, diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 10c74ffdfd..737f2b858d 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -113,6 +113,23 @@ Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) { ClassNames.size()); } +void Parser::CheckNestedObjCContexts(SourceLocation AtLoc) +{ + Sema::ObjCContainerKind ock = Actions.getObjCContainerKind(); + if (ock == Sema::OCK_None) + return; + + Decl *Decl = 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; +} + /// /// objc-interface: /// objc-class-interface-attributes[opt] objc-class-interface @@ -141,10 +158,11 @@ Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) { /// __attribute__((unavailable)) /// __attribute__((objc_exception)) - used by NSException on 64-bit /// -Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation atLoc, +Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, ParsedAttributes &attrs) { assert(Tok.isObjCAtKeyword(tok::objc_interface) && "ParseObjCAtInterfaceDeclaration(): Expected @interface"); + CheckNestedObjCContexts(AtLoc); ConsumeToken(); // the "interface" identifier // Code completion after '@interface'. @@ -205,7 +223,7 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation atLoc, return 0; Decl *CategoryType = - Actions.ActOnStartCategoryInterface(atLoc, + Actions.ActOnStartCategoryInterface(AtLoc, nameId, nameLoc, categoryId, categoryLoc, ProtocolRefs.data(), @@ -214,7 +232,7 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation atLoc, EndProtoLoc); if (Tok.is(tok::l_brace)) - ParseObjCClassInstanceVariables(CategoryType, tok::objc_private, atLoc); + ParseObjCClassInstanceVariables(CategoryType, tok::objc_private, AtLoc); ParseObjCInterfaceDeclList(tok::objc_not_keyword, CategoryType); return CategoryType; @@ -250,14 +268,14 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation atLoc, return 0; Decl *ClsType = - Actions.ActOnStartClassInterface(atLoc, nameId, nameLoc, + Actions.ActOnStartClassInterface(AtLoc, nameId, nameLoc, superClassId, superClassLoc, ProtocolRefs.data(), ProtocolRefs.size(), ProtocolLocs.data(), EndProtoLoc, attrs.getList()); if (Tok.is(tok::l_brace)) - ParseObjCClassInstanceVariables(ClsType, tok::objc_protected, atLoc); + ParseObjCClassInstanceVariables(ClsType, tok::objc_protected, AtLoc); ParseObjCInterfaceDeclList(tok::objc_interface, ClsType); return ClsType; @@ -425,7 +443,10 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, case tok::objc_implementation: case tok::objc_interface: - Diag(Tok, diag::err_objc_missing_end); + Diag(AtLoc, diag::err_objc_missing_end) + << FixItHint::CreateInsertion(AtLoc, "@end\n"); + Diag(CDecl->getLocStart(), diag::note_objc_container_start) + << (int) Actions.getObjCContainerKind(); ConsumeToken(); break; @@ -465,10 +486,16 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCAtDirective(getCurScope()); return cutOffParsing(); - } else if (Tok.isObjCAtKeyword(tok::objc_end)) + } else if (Tok.isObjCAtKeyword(tok::objc_end)) { ConsumeToken(); // the "end" identifier - else - Diag(Tok, diag::err_objc_missing_end); + } else { + Diag(Tok, diag::err_objc_missing_end) + << FixItHint::CreateInsertion(Tok.getLocation(), "\n@end\n"); + Diag(CDecl->getLocStart(), diag::note_objc_container_start) + << (int) Actions.getObjCContainerKind(); + AtEnd.setBegin(Tok.getLocation()); + AtEnd.setEnd(Tok.getLocation()); + } // Insert collected methods declarations into the @interface object. // This passes in an invalid SourceLocation for AtEndLoc when EOF is hit. @@ -1316,6 +1343,7 @@ Decl *Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc, ParsedAttributes &attrs) { assert(Tok.isObjCAtKeyword(tok::objc_protocol) && "ParseObjCAtProtocolDeclaration(): Expected @protocol"); + CheckNestedObjCContexts(AtLoc); ConsumeToken(); // the "protocol" identifier if (Tok.is(tok::code_completion)) { @@ -1399,10 +1427,10 @@ Decl *Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc, /// /// objc-category-implementation-prologue: /// @implementation identifier ( identifier ) -Decl *Parser::ParseObjCAtImplementationDeclaration( - SourceLocation atLoc) { +Decl *Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) { assert(Tok.isObjCAtKeyword(tok::objc_implementation) && "ParseObjCAtImplementationDeclaration(): Expected @implementation"); + CheckNestedObjCContexts(AtLoc); ConsumeToken(); // the "implementation" identifier // Code completion after '@implementation'. @@ -1446,7 +1474,7 @@ Decl *Parser::ParseObjCAtImplementationDeclaration( } rparenLoc = ConsumeParen(); Decl *ImplCatType = Actions.ActOnStartCategoryImplementation( - atLoc, nameId, nameLoc, categoryId, + AtLoc, nameId, nameLoc, categoryId, categoryLoc); ObjCImpDecl = ImplCatType; @@ -1467,11 +1495,11 @@ Decl *Parser::ParseObjCAtImplementationDeclaration( superClassLoc = ConsumeToken(); // Consume super class name } Decl *ImplClsType = Actions.ActOnStartClassImplementation( - atLoc, nameId, nameLoc, + AtLoc, nameId, nameLoc, superClassId, superClassLoc); if (Tok.is(tok::l_brace)) // we have ivars - ParseObjCClassInstanceVariables(ImplClsType, tok::objc_private, atLoc); + ParseObjCClassInstanceVariables(ImplClsType, tok::objc_private, AtLoc); ObjCImpDecl = ImplClsType; PendingObjCImpDecl.push_back(ObjCImpDecl); @@ -1498,7 +1526,7 @@ Parser::ParseObjCAtEndDeclaration(SourceRange atEnd) { } else // missing @implementation - Diag(atEnd.getBegin(), diag::err_expected_implementation); + Diag(atEnd.getBegin(), diag::err_expected_objc_container); clearLateParsedObjCMethods(); ObjCImpDecl = 0; @@ -1510,8 +1538,15 @@ Parser::DeclGroupPtrTy Parser::FinishPendingObjCActions() { Actions.DiagnoseUseOfUnimplementedSelectors(); if (PendingObjCImpDecl.empty()) return Actions.ConvertDeclToDeclGroup(0); + Decl *ImpDecl = PendingObjCImpDecl.pop_back_val(); - Actions.ActOnAtEnd(getCurScope(), SourceRange()); + 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); } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index afcf3cc63a..b271ae66a1 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -2148,15 +2148,39 @@ void Sema::DiagnoseDuplicateIvars(ObjCInterfaceDecl *ID, } } +Sema::ObjCContainerKind Sema::getObjCContainerKind() const { + switch (CurContext->getDeclKind()) { + case Decl::ObjCInterface: + return Sema::OCK_Interface; + case Decl::ObjCProtocol: + return Sema::OCK_Protocol; + case Decl::ObjCCategory: + if (dyn_cast<ObjCCategoryDecl>(CurContext)->IsClassExtension()) + return Sema::OCK_ClassExtension; + else + return Sema::OCK_Category; + case Decl::ObjCImplementation: + return Sema::OCK_Implementation; + case Decl::ObjCCategoryImpl: + return Sema::OCK_CategoryImplementation; + + default: + return Sema::OCK_None; + } +} + // Note: For class/category implemenations, allMethods/allProperties is // always null. -void Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, - Decl **allMethods, unsigned allNum, - Decl **allProperties, unsigned pNum, - DeclGroupPtrTy *allTUVars, unsigned tuvNum) { +Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, + Decl **allMethods, unsigned allNum, + Decl **allProperties, unsigned pNum, + DeclGroupPtrTy *allTUVars, unsigned tuvNum) { + + if (getObjCContainerKind() == Sema::OCK_None) + return 0; + + assert(AtEnd.isValid() && "Invalid location for '@end'"); - if (!CurContext->isObjCContainer()) - return; ObjCContainerDecl *OCD = dyn_cast<ObjCContainerDecl>(CurContext); Decl *ClassDecl = cast<Decl>(OCD); @@ -2165,15 +2189,6 @@ void Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, || isa<ObjCProtocolDecl>(ClassDecl); bool checkIdenticalMethods = isa<ObjCImplementationDecl>(ClassDecl); - if (!isInterfaceDeclKind && AtEnd.isInvalid()) { - // FIXME: This is wrong. We shouldn't be pretending that there is - // an '@end' in the declaration. - SourceLocation L = OCD->getAtStartLoc(); - AtEnd.setBegin(L); - AtEnd.setEnd(L); - Diag(L, diag::err_missing_atend); - } - // FIXME: Remove these and use the ObjCContainerDecl/DeclContext. llvm::DenseMap<Selector, const ObjCMethodDecl*> InsMap; llvm::DenseMap<Selector, const ObjCMethodDecl*> ClsMap; @@ -2335,6 +2350,8 @@ void Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, (*I)->setTopLevelDeclInObjCContainer(); Consumer.HandleTopLevelDeclInObjCContainer(DG); } + + return ClassDecl; } diff --git a/test/Index/cindex-on-invalid-usrs.m b/test/Index/cindex-on-invalid-usrs.m index 01002bc30f..64147615ca 100644 --- a/test/Index/cindex-on-invalid-usrs.m +++ b/test/Index/cindex-on-invalid-usrs.m @@ -4,4 +4,4 @@ @interface Rdar8452791 () - (void)rdar8452791; // CHECK: error: cannot find interface declaration for 'Rdar8452791' -// CHECK: missing @end +// CHECK: missing '@end' diff --git a/test/Parser/missing-end-2.m b/test/Parser/missing-end-2.m index 63dc965583..e89f28eb24 100644 --- a/test/Parser/missing-end-2.m +++ b/test/Parser/missing-end-2.m @@ -1,19 +1,19 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s // rdar: //7824372 -@interface A +@interface A // expected-note {{class started here}} -(void) im0; -@implementation A // expected-error {{missing @end}} +@implementation A // expected-error {{missing '@end'}} @end -@interface B { +@interface B { // expected-note {{class started here}} } -@implementation B // expected-error {{missing @end}} +@implementation B // expected-error {{missing '@end'}} @end -@interface C +@interface C // expected-note 2 {{class started here}} @property int P; -@implementation C // expected-error 2 {{missing @end}} +@implementation C // expected-error 2 {{missing '@end'}} diff --git a/test/Parser/missing-end-3.m b/test/Parser/missing-end-3.m index 3b226376dc..4875ecdd62 100644 --- a/test/Parser/missing-end-3.m +++ b/test/Parser/missing-end-3.m @@ -1,10 +1,10 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s // rdar://8283484 -@interface blah { +@interface blah { // expected-note {{class started here}} @private } // since I forgot the @end here it should say something -@interface blah // expected-error {{missing @end}} +@interface blah // expected-error {{missing '@end'}} @end // and Unknown type name 'end' here diff --git a/test/Parser/missing-end.m b/test/Parser/missing-end.m index fb264610ae..d66ea6487a 100644 --- a/test/Parser/missing-end.m +++ b/test/Parser/missing-end.m @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -@interface AAA +@interface AAA // expected-note {{class started here}} { } @ x// expected-error{{expected an Objective-C directive after '@'}} -// expected-error{{missing @end}} +// expected-error{{missing '@end'}} diff --git a/test/Parser/objc-missing-impl.m b/test/Parser/objc-missing-impl.m index e9c37ab1b1..791b9f8009 100644 --- a/test/Parser/objc-missing-impl.m +++ b/test/Parser/objc-missing-impl.m @@ -1,2 +1,2 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -@end // expected-error {{@end must appear in an @implementation context}} +@end // expected-error {{'@end' must appear in an Objective-C context}} diff --git a/test/Parser/objc-quirks.m b/test/Parser/objc-quirks.m index 591bca222a..72cb1e1d7c 100644 --- a/test/Parser/objc-quirks.m +++ b/test/Parser/objc-quirks.m @@ -5,8 +5,8 @@ int @"s" = 5; // expected-error {{prefix attribute must be}} // rdar://6480479 -@interface A -}; // expected-error {{missing @end}} \ +@interface A // expected-note {{class started here}} +}; // expected-error {{missing '@end'}} \ // expected-error {{expected external declaration}} \ // expected-warning{{extra ';' outside of a function}} diff --git a/test/SemaObjC/missing-atend-metadata.m b/test/SemaObjC/missing-atend-metadata.m index 9b79c52d96..c929c6d760 100644 --- a/test/SemaObjC/missing-atend-metadata.m +++ b/test/SemaObjC/missing-atend-metadata.m @@ -3,20 +3,20 @@ @interface I0 @end -@implementation I0 // expected-error {{'@end' is missing in implementation context}} +@implementation I0 // expected-note {{implementation started here}} - meth { return 0; } -@interface I1 : I0 +@interface I1 : I0 // expected-error {{missing '@end'}} @end -@implementation I1 // expected-error {{'@end' is missing in implementation context}} +@implementation I1 // expected-note {{implementation started here}} -(void) im0 { self = [super init]; } -@interface I2 : I0 +@interface I2 : I0 // expected-error {{missing '@end'}} - I2meth; @end -@implementation I2 // expected-error {{'@end' is missing in implementation context}} +@implementation I2 // expected-note {{implementation started here}} - I2meth { return 0; } -@implementation I2(CAT) // expected-error {{'@end' is missing in implementation context}} +@implementation I2(CAT) // expected-error 2 {{missing '@end'}} expected-note {{implementation started here}} |