diff options
Diffstat (limited to 'clang/lib/Parse/ParseObjc.cpp')
-rw-r--r-- | clang/lib/Parse/ParseObjc.cpp | 1578 |
1 files changed, 1578 insertions, 0 deletions
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp new file mode 100644 index 00000000000..77d2adbd320 --- /dev/null +++ b/clang/lib/Parse/ParseObjc.cpp @@ -0,0 +1,1578 @@ +//===--- ParseObjC.cpp - Objective C Parsing ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Objective-C portions of the Parser interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Parse/Parser.h" +#include "clang/Parse/DeclSpec.h" +#include "clang/Parse/Scope.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/SmallVector.h" +using namespace clang; + + +/// ParseExternalDeclaration: +/// external-declaration: [C99 6.9] +/// [OBJC] objc-class-definition +/// [OBJC] objc-class-declaration +/// [OBJC] objc-alias-declaration +/// [OBJC] objc-protocol-definition +/// [OBJC] objc-method-definition +/// [OBJC] '@' 'end' +Parser::DeclTy *Parser::ParseObjCAtDirectives() { + SourceLocation AtLoc = ConsumeToken(); // the "@" + + switch (Tok.getObjCKeywordID()) { + case tok::objc_class: + return ParseObjCAtClassDeclaration(AtLoc); + case tok::objc_interface: + return ParseObjCAtInterfaceDeclaration(AtLoc); + case tok::objc_protocol: + return ParseObjCAtProtocolDeclaration(AtLoc); + case tok::objc_implementation: + return ParseObjCAtImplementationDeclaration(AtLoc); + case tok::objc_end: + return ParseObjCAtEndDeclaration(AtLoc); + case tok::objc_compatibility_alias: + return ParseObjCAtAliasDeclaration(AtLoc); + case tok::objc_synthesize: + return ParseObjCPropertySynthesize(AtLoc); + case tok::objc_dynamic: + return ParseObjCPropertyDynamic(AtLoc); + default: + Diag(AtLoc, diag::err_unexpected_at); + SkipUntil(tok::semi); + return 0; + } +} + +/// +/// objc-class-declaration: +/// '@' 'class' identifier-list ';' +/// +Parser::DeclTy *Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) { + ConsumeToken(); // the identifier "class" + llvm::SmallVector<IdentifierInfo *, 8> ClassNames; + + while (1) { + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::semi); + return 0; + } + ClassNames.push_back(Tok.getIdentifierInfo()); + ConsumeToken(); + + if (Tok.isNot(tok::comma)) + break; + + ConsumeToken(); + } + + // Consume the ';'. + if (ExpectAndConsume(tok::semi, diag::err_expected_semi_after, "@class")) + return 0; + + return Actions.ActOnForwardClassDeclaration(atLoc, + &ClassNames[0], ClassNames.size()); +} + +/// +/// objc-interface: +/// objc-class-interface-attributes[opt] objc-class-interface +/// objc-category-interface +/// +/// objc-class-interface: +/// '@' 'interface' identifier objc-superclass[opt] +/// objc-protocol-refs[opt] +/// objc-class-instance-variables[opt] +/// objc-interface-decl-list +/// @end +/// +/// objc-category-interface: +/// '@' 'interface' identifier '(' identifier[opt] ')' +/// objc-protocol-refs[opt] +/// objc-interface-decl-list +/// @end +/// +/// objc-superclass: +/// ':' identifier +/// +/// objc-class-interface-attributes: +/// __attribute__((visibility("default"))) +/// __attribute__((visibility("hidden"))) +/// __attribute__((deprecated)) +/// __attribute__((unavailable)) +/// __attribute__((objc_exception)) - used by NSException on 64-bit +/// +Parser::DeclTy *Parser::ParseObjCAtInterfaceDeclaration( + SourceLocation atLoc, AttributeList *attrList) { + assert(Tok.isObjCAtKeyword(tok::objc_interface) && + "ParseObjCAtInterfaceDeclaration(): Expected @interface"); + ConsumeToken(); // the "interface" identifier + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing class or category name. + return 0; + } + // We have a class or category name - consume it. + IdentifierInfo *nameId = Tok.getIdentifierInfo(); + SourceLocation nameLoc = ConsumeToken(); + + if (Tok.is(tok::l_paren)) { // we have a category. + SourceLocation lparenLoc = ConsumeParen(); + SourceLocation categoryLoc, rparenLoc; + IdentifierInfo *categoryId = 0; + llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs; + + // For ObjC2, the category name is optional (not an error). + if (Tok.is(tok::identifier)) { + categoryId = Tok.getIdentifierInfo(); + categoryLoc = ConsumeToken(); + } else if (!getLang().ObjC2) { + Diag(Tok, diag::err_expected_ident); // missing category name. + return 0; + } + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected_rparen); + SkipUntil(tok::r_paren, false); // don't stop at ';' + return 0; + } + rparenLoc = ConsumeParen(); + SourceLocation endProtoLoc; + // Next, we need to check for any protocol references. + if (Tok.is(tok::less)) { + if (ParseObjCProtocolReferences(ProtocolRefs, endProtoLoc)) + return 0; + } + if (attrList) // categories don't support attributes. + Diag(Tok, diag::err_objc_no_attributes_on_category); + + DeclTy *CategoryType = Actions.ActOnStartCategoryInterface(atLoc, + nameId, nameLoc, categoryId, categoryLoc, + &ProtocolRefs[0], ProtocolRefs.size(), + endProtoLoc); + + ParseObjCInterfaceDeclList(CategoryType, tok::objc_not_keyword); + + // The @ sign was already consumed by ParseObjCInterfaceDeclList(). + if (Tok.isObjCAtKeyword(tok::objc_end)) { + ConsumeToken(); // the "end" identifier + return CategoryType; + } + Diag(Tok, diag::err_objc_missing_end); + return 0; + } + // Parse a class interface. + IdentifierInfo *superClassId = 0; + SourceLocation superClassLoc; + + if (Tok.is(tok::colon)) { // a super class is specified. + ConsumeToken(); + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing super class name. + return 0; + } + superClassId = Tok.getIdentifierInfo(); + superClassLoc = ConsumeToken(); + } + // Next, we need to check for any protocol references. + llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs; + SourceLocation endProtoLoc; + if (Tok.is(tok::less)) { + if (ParseObjCProtocolReferences(ProtocolRefs, endProtoLoc)) + return 0; + } + DeclTy *ClsType = Actions.ActOnStartClassInterface( + atLoc, nameId, nameLoc, + superClassId, superClassLoc, &ProtocolRefs[0], + ProtocolRefs.size(), endProtoLoc, attrList); + + if (Tok.is(tok::l_brace)) + ParseObjCClassInstanceVariables(ClsType, atLoc); + + ParseObjCInterfaceDeclList(ClsType, tok::objc_interface); + + // The @ sign was already consumed by ParseObjCInterfaceDeclList(). + if (Tok.isObjCAtKeyword(tok::objc_end)) { + ConsumeToken(); // the "end" identifier + return ClsType; + } + Diag(Tok, diag::err_objc_missing_end); + return 0; +} + +/// objc-interface-decl-list: +/// empty +/// objc-interface-decl-list objc-property-decl [OBJC2] +/// objc-interface-decl-list objc-method-requirement [OBJC2] +/// objc-interface-decl-list objc-method-proto ';' +/// objc-interface-decl-list declaration +/// objc-interface-decl-list ';' +/// +/// objc-method-requirement: [OBJC2] +/// @required +/// @optional +/// +void Parser::ParseObjCInterfaceDeclList(DeclTy *interfaceDecl, + tok::ObjCKeywordKind contextKey) { + llvm::SmallVector<DeclTy*, 32> allMethods; + llvm::SmallVector<DeclTy*, 16> allProperties; + tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword; + SourceLocation AtEndLoc; + + while (1) { + if (Tok.is(tok::at)) { + SourceLocation AtLoc = ConsumeToken(); // the "@" + tok::ObjCKeywordKind ocKind = Tok.getObjCKeywordID(); + + if (ocKind == tok::objc_end) { // terminate list + AtEndLoc = AtLoc; + break; + } else if (ocKind == tok::objc_required) { // protocols only + ConsumeToken(); + MethodImplKind = ocKind; + if (contextKey != tok::objc_protocol) + Diag(AtLoc, diag::err_objc_protocol_required); + } else if (ocKind == tok::objc_optional) { // protocols only + ConsumeToken(); + MethodImplKind = ocKind; + if (contextKey != tok::objc_protocol) + Diag(AtLoc, diag::err_objc_protocol_optional); + } else if (ocKind == tok::objc_property) { + allProperties.push_back(ParseObjCPropertyDecl(interfaceDecl, AtLoc)); + continue; + } else { + Diag(Tok, diag::err_objc_illegal_interface_qual); + ConsumeToken(); + } + } + if (Tok.is(tok::minus) || Tok.is(tok::plus)) { + DeclTy *methodPrototype = + ParseObjCMethodPrototype(interfaceDecl, MethodImplKind); + allMethods.push_back(methodPrototype); + // Consume the ';' here, since ParseObjCMethodPrototype() is re-used for + // method definitions. + ExpectAndConsume(tok::semi, diag::err_expected_semi_after,"method proto"); + continue; + } + else if (Tok.is(tok::at)) + continue; + + if (Tok.is(tok::semi)) + ConsumeToken(); + else if (Tok.is(tok::eof)) + break; + else { + // FIXME: as the name implies, this rule allows function definitions. + // We could pass a flag or check for functions during semantic analysis. + ParseDeclarationOrFunctionDefinition(); + } + } + /// Insert collected methods declarations into the @interface object. + Actions.ActOnAtEnd(AtEndLoc, interfaceDecl, &allMethods[0], allMethods.size(), + &allProperties[0], allProperties.size()); +} + +/// Parse property attribute declarations. +/// +/// property-attr-decl: '(' property-attrlist ')' +/// property-attrlist: +/// property-attribute +/// property-attrlist ',' property-attribute +/// property-attribute: +/// getter '=' identifier +/// setter '=' identifier ':' +/// readonly +/// readwrite +/// assign +/// retain +/// copy +/// nonatomic +/// +void Parser::ParseObjCPropertyAttribute (ObjCDeclSpec &DS) { + SourceLocation loc = ConsumeParen(); // consume '(' + while (isObjCPropertyAttribute()) { + const IdentifierInfo *II = Tok.getIdentifierInfo(); + // getter/setter require extra treatment. + if (II == ObjCPropertyAttrs[objc_getter] || + II == ObjCPropertyAttrs[objc_setter]) { + // skip getter/setter part. + SourceLocation loc = ConsumeToken(); + if (Tok.is(tok::equal)) { + loc = ConsumeToken(); + if (Tok.is(tok::identifier)) { + if (II == ObjCPropertyAttrs[objc_setter]) { + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_setter); + DS.setSetterName(Tok.getIdentifierInfo()); + loc = ConsumeToken(); // consume method name + if (Tok.isNot(tok::colon)) { + Diag(loc, diag::err_expected_colon); + SkipUntil(tok::r_paren,true,true); + break; + } + } + else { + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_getter); + DS.setGetterName(Tok.getIdentifierInfo()); + } + } + else { + Diag(loc, diag::err_expected_ident); + SkipUntil(tok::r_paren,true,true); + break; + } + } + else { + Diag(loc, diag::err_objc_expected_equal); + SkipUntil(tok::r_paren,true,true); + break; + } + } + + else if (II == ObjCPropertyAttrs[objc_readonly]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readonly); + else if (II == ObjCPropertyAttrs[objc_assign]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_assign); + else if (II == ObjCPropertyAttrs[objc_readwrite]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readwrite); + else if (II == ObjCPropertyAttrs[objc_retain]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_retain); + else if (II == ObjCPropertyAttrs[objc_copy]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_copy); + else if (II == ObjCPropertyAttrs[objc_nonatomic]) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nonatomic); + + ConsumeToken(); // consume last attribute token + if (Tok.is(tok::comma)) { + loc = ConsumeToken(); + continue; + } + if (Tok.is(tok::r_paren)) + break; + Diag(loc, diag::err_expected_rparen); + SkipUntil(tok::semi); + return; + } + if (Tok.is(tok::r_paren)) + ConsumeParen(); + else { + Diag(loc, diag::err_objc_expected_property_attr); + SkipUntil(tok::r_paren); // recover from error inside attribute list + } +} + +/// Main routine to parse property declaration. +/// +/// @property property-attr-decl[opt] property-component-decl ';' +/// +Parser::DeclTy *Parser::ParseObjCPropertyDecl(DeclTy *interfaceDecl, + SourceLocation AtLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_property) && + "ParseObjCPropertyDecl(): Expected @property"); + ObjCDeclSpec DS; + ConsumeToken(); // the "property" identifier + // Parse property attribute list, if any. + if (Tok.is(tok::l_paren)) { + // property has attribute list. + ParseObjCPropertyAttribute(DS); + } + // Parse declaration portion of @property. + llvm::SmallVector<DeclTy*, 8> PropertyDecls; + ParseStructDeclaration(interfaceDecl, PropertyDecls); + if (Tok.is(tok::semi)) + ConsumeToken(); + else { + Diag(Tok, diag::err_expected_semi_decl_list); + SkipUntil(tok::r_brace, true, true); + } + return Actions.ActOnAddObjCProperties(AtLoc, + &PropertyDecls[0], PropertyDecls.size(), DS); +} + +/// objc-method-proto: +/// objc-instance-method objc-method-decl objc-method-attributes[opt] +/// objc-class-method objc-method-decl objc-method-attributes[opt] +/// +/// objc-instance-method: '-' +/// objc-class-method: '+' +/// +/// objc-method-attributes: [OBJC2] +/// __attribute__((deprecated)) +/// +Parser::DeclTy *Parser::ParseObjCMethodPrototype(DeclTy *IDecl, + tok::ObjCKeywordKind MethodImplKind) { + assert((Tok.is(tok::minus) || Tok.is(tok::plus)) && "expected +/-"); + + tok::TokenKind methodType = Tok.getKind(); + SourceLocation mLoc = ConsumeToken(); + + DeclTy *MDecl = ParseObjCMethodDecl(mLoc, methodType, IDecl, MethodImplKind); + // Since this rule is used for both method declarations and definitions, + // the caller is (optionally) responsible for consuming the ';'. + return MDecl; +} + +/// objc-selector: +/// identifier +/// one of +/// enum struct union if else while do for switch case default +/// break continue return goto asm sizeof typeof __alignof +/// unsigned long const short volatile signed restrict _Complex +/// in out inout bycopy byref oneway int char float double void _Bool +/// +IdentifierInfo *Parser::ParseObjCSelector(SourceLocation &SelectorLoc) { + switch (Tok.getKind()) { + default: + return 0; + case tok::identifier: + case tok::kw_typeof: + case tok::kw___alignof: + case tok::kw_auto: + case tok::kw_break: + case tok::kw_case: + case tok::kw_char: + case tok::kw_const: + case tok::kw_continue: + case tok::kw_default: + case tok::kw_do: + case tok::kw_double: + case tok::kw_else: + case tok::kw_enum: + case tok::kw_extern: + case tok::kw_float: + case tok::kw_for: + case tok::kw_goto: + case tok::kw_if: + case tok::kw_inline: + case tok::kw_int: + case tok::kw_long: + case tok::kw_register: + case tok::kw_restrict: + case tok::kw_return: + case tok::kw_short: + case tok::kw_signed: + case tok::kw_sizeof: + case tok::kw_static: + case tok::kw_struct: + case tok::kw_switch: + case tok::kw_typedef: + case tok::kw_union: + case tok::kw_unsigned: + case tok::kw_void: + case tok::kw_volatile: + case tok::kw_while: + case tok::kw_bool: + case tok::kw__Bool: + case tok::kw__Complex: + IdentifierInfo *II = Tok.getIdentifierInfo(); + SelectorLoc = ConsumeToken(); + return II; + } +} + +/// property-attrlist: one of +/// readonly getter setter assign retain copy nonatomic +/// +bool Parser::isObjCPropertyAttribute() { + if (Tok.is(tok::identifier)) { + const IdentifierInfo *II = Tok.getIdentifierInfo(); + for (unsigned i = 0; i < objc_NumAttrs; ++i) + if (II == ObjCPropertyAttrs[i]) return true; + } + return false; +} + +/// objc-for-collection-in: 'in' +/// +bool Parser::isTokIdentifier_in() const { + // FIXME: May have to do additional look-ahead to only allow for + // valid tokens following an 'in'; such as an identifier, unary operators, + // '[' etc. + return (getLang().ObjC2 && Tok.is(tok::identifier) && + Tok.getIdentifierInfo() == ObjCForCollectionInKW); +} + +/// ParseObjCTypeQualifierList - This routine parses the objective-c's type +/// qualifier list and builds their bitmask representation in the input +/// argument. +/// +/// objc-type-qualifiers: +/// objc-type-qualifier +/// objc-type-qualifiers objc-type-qualifier +/// +void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS) { + while (1) { + if (Tok.isNot(tok::identifier)) + return; + + const IdentifierInfo *II = Tok.getIdentifierInfo(); + for (unsigned i = 0; i != objc_NumQuals; ++i) { + if (II != ObjCTypeQuals[i]) + continue; + + ObjCDeclSpec::ObjCDeclQualifier Qual; + switch (i) { + default: assert(0 && "Unknown decl qualifier"); + case objc_in: Qual = ObjCDeclSpec::DQ_In; break; + case objc_out: Qual = ObjCDeclSpec::DQ_Out; break; + case objc_inout: Qual = ObjCDeclSpec::DQ_Inout; break; + case objc_oneway: Qual = ObjCDeclSpec::DQ_Oneway; break; + case objc_bycopy: Qual = ObjCDeclSpec::DQ_Bycopy; break; + case objc_byref: Qual = ObjCDeclSpec::DQ_Byref; break; + } + DS.setObjCDeclQualifier(Qual); + ConsumeToken(); + II = 0; + break; + } + + // If this wasn't a recognized qualifier, bail out. + if (II) return; + } +} + +/// objc-type-name: +/// '(' objc-type-qualifiers[opt] type-name ')' +/// '(' objc-type-qualifiers[opt] ')' +/// +Parser::TypeTy *Parser::ParseObjCTypeName(ObjCDeclSpec &DS) { + assert(Tok.is(tok::l_paren) && "expected ("); + + SourceLocation LParenLoc = ConsumeParen(), RParenLoc; + TypeTy *Ty = 0; + + // Parse type qualifiers, in, inout, etc. + ParseObjCTypeQualifierList(DS); + + if (isTypeSpecifierQualifier()) { + Ty = ParseTypeName(); + // FIXME: back when Sema support is in place... + // assert(Ty && "Parser::ParseObjCTypeName(): missing type"); + } + if (Tok.isNot(tok::r_paren)) { + MatchRHSPunctuation(tok::r_paren, LParenLoc); + return 0; // FIXME: decide how we want to handle this error... + } + RParenLoc = ConsumeParen(); + return Ty; +} + +/// objc-method-decl: +/// objc-selector +/// objc-keyword-selector objc-parmlist[opt] +/// objc-type-name objc-selector +/// objc-type-name objc-keyword-selector objc-parmlist[opt] +/// +/// objc-keyword-selector: +/// objc-keyword-decl +/// objc-keyword-selector objc-keyword-decl +/// +/// objc-keyword-decl: +/// objc-selector ':' objc-type-name objc-keyword-attributes[opt] identifier +/// objc-selector ':' objc-keyword-attributes[opt] identifier +/// ':' objc-type-name objc-keyword-attributes[opt] identifier +/// ':' objc-keyword-attributes[opt] identifier +/// +/// objc-parmlist: +/// objc-parms objc-ellipsis[opt] +/// +/// objc-parms: +/// objc-parms , parameter-declaration +/// +/// objc-ellipsis: +/// , ... +/// +/// objc-keyword-attributes: [OBJC2] +/// __attribute__((unused)) +/// +Parser::DeclTy *Parser::ParseObjCMethodDecl(SourceLocation mLoc, + tok::TokenKind mType, + DeclTy *IDecl, + tok::ObjCKeywordKind MethodImplKind) +{ + // Parse the return type. + TypeTy *ReturnType = 0; + ObjCDeclSpec DSRet; + if (Tok.is(tok::l_paren)) + ReturnType = ParseObjCTypeName(DSRet); + SourceLocation selLoc; + IdentifierInfo *SelIdent = ParseObjCSelector(selLoc); + if (Tok.isNot(tok::colon)) { + if (!SelIdent) { + Diag(Tok, diag::err_expected_ident); // missing selector name. + // FIXME: this creates a unary selector with a null identifier, is this + // ok?? Maybe we should skip to the next semicolon or something. + } + + // If attributes exist after the method, parse them. + AttributeList *MethodAttrs = 0; + if (getLang().ObjC2 && Tok.is(tok::kw___attribute)) + MethodAttrs = ParseAttributes(); + + Selector Sel = PP.getSelectorTable().getNullarySelector(SelIdent); + return Actions.ActOnMethodDeclaration(mLoc, Tok.getLocation(), + mType, IDecl, DSRet, ReturnType, Sel, + 0, 0, 0, MethodAttrs, MethodImplKind); + } + + llvm::SmallVector<IdentifierInfo *, 12> KeyIdents; + llvm::SmallVector<Action::TypeTy *, 12> KeyTypes; + llvm::SmallVector<ObjCDeclSpec, 12> ArgTypeQuals; + llvm::SmallVector<IdentifierInfo *, 12> ArgNames; + + Action::TypeTy *TypeInfo; + while (1) { + KeyIdents.push_back(SelIdent); + + // Each iteration parses a single keyword argument. + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_colon); + break; + } + ConsumeToken(); // Eat the ':'. + ObjCDeclSpec DSType; + if (Tok.is(tok::l_paren)) { // Parse the argument type. + TypeInfo = ParseObjCTypeName(DSType); + } + else + TypeInfo = 0; + KeyTypes.push_back(TypeInfo); + ArgTypeQuals.push_back(DSType); + + // If attributes exist before the argument name, parse them. + if (getLang().ObjC2 && Tok.is(tok::kw___attribute)) + ParseAttributes(); // FIXME: pass attributes through. + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing argument name. + break; + } + ArgNames.push_back(Tok.getIdentifierInfo()); + ConsumeToken(); // Eat the identifier. + + // Check for another keyword selector. + SourceLocation Loc; + SelIdent = ParseObjCSelector(Loc); + if (!SelIdent && Tok.isNot(tok::colon)) + break; + // We have a selector or a colon, continue parsing. + } + + bool isVariadic = false; + + // Parse the (optional) parameter list. + while (Tok.is(tok::comma)) { + ConsumeToken(); + if (Tok.is(tok::ellipsis)) { + isVariadic = true; + ConsumeToken(); + break; + } + // FIXME: implement this... + // Parse the c-style argument declaration-specifier. + DeclSpec DS; + ParseDeclarationSpecifiers(DS); + // Parse the declarator. + Declarator ParmDecl(DS, Declarator::PrototypeContext); + ParseDeclarator(ParmDecl); + } + + // FIXME: Add support for optional parmameter list... + // If attributes exist after the method, parse them. + AttributeList *MethodAttrs = 0; + if (getLang().ObjC2 && Tok.is(tok::kw___attribute)) + MethodAttrs = ParseAttributes(); + + Selector Sel = PP.getSelectorTable().getSelector(KeyIdents.size(), + &KeyIdents[0]); + return Actions.ActOnMethodDeclaration(mLoc, Tok.getLocation(), + mType, IDecl, DSRet, ReturnType, Sel, + &ArgTypeQuals[0], &KeyTypes[0], + &ArgNames[0], MethodAttrs, + MethodImplKind, isVariadic); +} + +/// CmpProtocolVals - Comparison predicate for sorting protocols. +static bool CmpProtocolVals(const IdentifierInfo* const& lhs, + const IdentifierInfo* const& rhs) { + return strcmp(lhs->getName(), rhs->getName()) < 0; +} + +/// objc-protocol-refs: +/// '<' identifier-list '>' +/// +bool Parser::ParseObjCProtocolReferences( + llvm::SmallVectorImpl<IdentifierInfo*> &ProtocolRefs, SourceLocation &endLoc) +{ + assert(Tok.is(tok::less) && "expected <"); + + ConsumeToken(); // the "<" + + while (1) { + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::greater); + return true; + } + ProtocolRefs.push_back(Tok.getIdentifierInfo()); + ConsumeToken(); + + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); + } + + // Sort protocols, keyed by name. + // Later on, we remove duplicates. + std::stable_sort(ProtocolRefs.begin(), ProtocolRefs.end(), CmpProtocolVals); + + // Make protocol names unique. + ProtocolRefs.erase(std::unique(ProtocolRefs.begin(), ProtocolRefs.end()), + ProtocolRefs.end()); + // Consume the '>'. + if (Tok.is(tok::greater)) { + endLoc = ConsumeAnyToken(); + return false; + } + Diag(Tok, diag::err_expected_greater); + return true; +} + +/// objc-class-instance-variables: +/// '{' objc-instance-variable-decl-list[opt] '}' +/// +/// objc-instance-variable-decl-list: +/// objc-visibility-spec +/// objc-instance-variable-decl ';' +/// ';' +/// objc-instance-variable-decl-list objc-visibility-spec +/// objc-instance-variable-decl-list objc-instance-variable-decl ';' +/// objc-instance-variable-decl-list ';' +/// +/// objc-visibility-spec: +/// @private +/// @protected +/// @public +/// @package [OBJC2] +/// +/// objc-instance-variable-decl: +/// struct-declaration +/// +void Parser::ParseObjCClassInstanceVariables(DeclTy *interfaceDecl, + SourceLocation atLoc) { + assert(Tok.is(tok::l_brace) && "expected {"); + llvm::SmallVector<DeclTy*, 16> IvarDecls; + llvm::SmallVector<DeclTy*, 32> AllIvarDecls; + llvm::SmallVector<tok::ObjCKeywordKind, 32> AllVisibilities; + + SourceLocation LBraceLoc = ConsumeBrace(); // the "{" + + tok::ObjCKeywordKind visibility = tok::objc_private; + // While we still have something to read, read the instance variables. + while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { + // Each iteration of this loop reads one objc-instance-variable-decl. + + // Check for extraneous top-level semicolon. + if (Tok.is(tok::semi)) { + Diag(Tok, diag::ext_extra_struct_semi); + ConsumeToken(); + continue; + } + // Set the default visibility to private. + if (Tok.is(tok::at)) { // parse objc-visibility-spec + ConsumeToken(); // eat the @ sign + switch (Tok.getObjCKeywordID()) { + case tok::objc_private: + case tok::objc_public: + case tok::objc_protected: + case tok::objc_package: + visibility = Tok.getObjCKeywordID(); + ConsumeToken(); + continue; + default: + Diag(Tok, diag::err_objc_illegal_visibility_spec); + ConsumeToken(); + continue; + } + } + ParseStructDeclaration(interfaceDecl, IvarDecls); + for (unsigned i = 0; i < IvarDecls.size(); i++) { + AllIvarDecls.push_back(IvarDecls[i]); + AllVisibilities.push_back(visibility); + } + IvarDecls.clear(); + + if (Tok.is(tok::semi)) { + ConsumeToken(); + } else if (Tok.is(tok::r_brace)) { + Diag(Tok.getLocation(), diag::ext_expected_semi_decl_list); + break; + } else { + Diag(Tok, diag::err_expected_semi_decl_list); + // Skip to end of block or statement + SkipUntil(tok::r_brace, true, true); + } + } + SourceLocation RBraceLoc = MatchRHSPunctuation(tok::r_brace, LBraceLoc); + // Call ActOnFields() even if we don't have any decls. This is useful + // for code rewriting tools that need to be aware of the empty list. + Actions.ActOnFields(CurScope, atLoc, interfaceDecl, + &AllIvarDecls[0], AllIvarDecls.size(), + LBraceLoc, RBraceLoc, &AllVisibilities[0]); + return; +} + +/// objc-protocol-declaration: +/// objc-protocol-definition +/// objc-protocol-forward-reference +/// +/// objc-protocol-definition: +/// @protocol identifier +/// objc-protocol-refs[opt] +/// objc-interface-decl-list +/// @end +/// +/// objc-protocol-forward-reference: +/// @protocol identifier-list ';' +/// +/// "@protocol identifier ;" should be resolved as "@protocol +/// identifier-list ;": objc-interface-decl-list may not start with a +/// semicolon in the first alternative if objc-protocol-refs are omitted. + +Parser::DeclTy *Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_protocol) && + "ParseObjCAtProtocolDeclaration(): Expected @protocol"); + ConsumeToken(); // the "protocol" identifier + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing protocol name. + return 0; + } + // Save the protocol name, then consume it. + IdentifierInfo *protocolName = Tok.getIdentifierInfo(); + SourceLocation nameLoc = ConsumeToken(); + + llvm::SmallVector<IdentifierInfo *, 8> ProtocolRefs; + if (Tok.is(tok::semi)) { // forward declaration of one protocol. + ConsumeToken(); + ProtocolRefs.push_back(protocolName); + } + if (Tok.is(tok::comma)) { // list of forward declarations. + // Parse the list of forward declarations. + ProtocolRefs.push_back(protocolName); + + while (1) { + ConsumeToken(); // the ',' + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::semi); + return 0; + } + ProtocolRefs.push_back(Tok.getIdentifierInfo()); + ConsumeToken(); // the identifier + + if (Tok.isNot(tok::comma)) + break; + } + // Consume the ';'. + if (ExpectAndConsume(tok::semi, diag::err_expected_semi_after, "@protocol")) + return 0; + } + if (ProtocolRefs.size() > 0) + return Actions.ActOnForwardProtocolDeclaration(AtLoc, + &ProtocolRefs[0], + ProtocolRefs.size()); + // Last, and definitely not least, parse a protocol declaration. + SourceLocation endProtoLoc; + if (Tok.is(tok::less)) { + if (ParseObjCProtocolReferences(ProtocolRefs, endProtoLoc)) + return 0; + } + + DeclTy *ProtoType = Actions.ActOnStartProtocolInterface(AtLoc, + protocolName, nameLoc, + &ProtocolRefs[0], + ProtocolRefs.size(), endProtoLoc); + ParseObjCInterfaceDeclList(ProtoType, tok::objc_protocol); + + // The @ sign was already consumed by ParseObjCInterfaceDeclList(). + if (Tok.isObjCAtKeyword(tok::objc_end)) { + ConsumeToken(); // the "end" identifier + return ProtoType; + } + Diag(Tok, diag::err_objc_missing_end); + return 0; +} + +/// objc-implementation: +/// objc-class-implementation-prologue +/// objc-category-implementation-prologue +/// +/// objc-class-implementation-prologue: +/// @implementation identifier objc-superclass[opt] +/// objc-class-instance-variables[opt] +/// +/// objc-category-implementation-prologue: +/// @implementation identifier ( identifier ) + +Parser::DeclTy *Parser::ParseObjCAtImplementationDeclaration( + SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_implementation) && + "ParseObjCAtImplementationDeclaration(): Expected @implementation"); + ConsumeToken(); // the "implementation" identifier + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); // missing class or category name. + return 0; + } + // We have a class or category name - consume it. + IdentifierInfo *nameId = Tok.getIdentifierInfo(); + SourceLocation nameLoc = ConsumeToken(); // consume class or category name + + if (Tok.is(tok::l_paren)) { + // we have a category implementation. + SourceLocation lparenLoc = ConsumeParen(); + SourceLocation categoryLoc, rparenLoc; + IdentifierInfo *categoryId = 0; + + if (Tok.is(tok::identifier)) { + categoryId = Tok.getIdentifierInfo(); + categoryLoc = ConsumeToken(); + } else { + Diag(Tok, diag::err_expected_ident); // missing category name. + return 0; + } + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected_rparen); + SkipUntil(tok::r_paren, false); // don't stop at ';' + return 0; + } + rparenLoc = ConsumeParen(); + DeclTy *ImplCatType = Actions.ActOnStartCategoryImplementation( + atLoc, nameId, nameLoc, categoryId, + categoryLoc); + ObjCImpDecl = ImplCatType; + 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; + } + superClassId = Tok.getIdentifierInfo(); + superClassLoc = ConsumeToken(); // Consume super class name + } + DeclTy *ImplClsType = Actions.ActOnStartClassImplementation( + atLoc, nameId, nameLoc, + superClassId, superClassLoc); + + if (Tok.is(tok::l_brace)) // we have ivars + ParseObjCClassInstanceVariables(ImplClsType/*FIXME*/, atLoc); + ObjCImpDecl = ImplClsType; + + return 0; +} + +Parser::DeclTy *Parser::ParseObjCAtEndDeclaration(SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_end) && + "ParseObjCAtEndDeclaration(): Expected @end"); + ConsumeToken(); // the "end" identifier + if (ObjCImpDecl) + Actions.ActOnAtEnd(atLoc, ObjCImpDecl); + else + Diag(atLoc, diag::warn_expected_implementation); // missing @implementation + return ObjCImpDecl; +} + +/// compatibility-alias-decl: +/// @compatibility_alias alias-name class-name ';' +/// +Parser::DeclTy *Parser::ParseObjCAtAliasDeclaration(SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_compatibility_alias) && + "ParseObjCAtAliasDeclaration(): Expected @compatibility_alias"); + ConsumeToken(); // consume compatibility_alias + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return 0; + } + IdentifierInfo *aliasId = Tok.getIdentifierInfo(); + SourceLocation aliasLoc = ConsumeToken(); // consume alias-name + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return 0; + } + IdentifierInfo *classId = Tok.getIdentifierInfo(); + SourceLocation classLoc = ConsumeToken(); // consume class-name; + if (Tok.isNot(tok::semi)) { + Diag(Tok, diag::err_expected_semi_after, "@compatibility_alias"); + return 0; + } + DeclTy *ClsType = Actions.ActOnCompatiblityAlias(atLoc, + aliasId, aliasLoc, + classId, classLoc); + return ClsType; +} + +/// property-synthesis: +/// @synthesize property-ivar-list ';' +/// +/// property-ivar-list: +/// property-ivar +/// property-ivar-list ',' property-ivar +/// +/// property-ivar: +/// identifier +/// identifier '=' identifier +/// +Parser::DeclTy *Parser::ParseObjCPropertySynthesize(SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_synthesize) && + "ParseObjCPropertyDynamic(): Expected '@synthesize'"); + SourceLocation loc = ConsumeToken(); // consume dynamic + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return 0; + } + while (Tok.is(tok::identifier)) { + ConsumeToken(); // consume property name + if (Tok.is(tok::equal)) { + // property '=' ivar-name + ConsumeToken(); // consume '=' + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + break; + } + ConsumeToken(); // consume ivar-name + } + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // consume ',' + } + if (Tok.isNot(tok::semi)) + Diag(Tok, diag::err_expected_semi_after, "@synthesize"); + return 0; +} + +/// property-dynamic: +/// @dynamic property-list +/// +/// property-list: +/// identifier +/// property-list ',' identifier +/// +Parser::DeclTy *Parser::ParseObjCPropertyDynamic(SourceLocation atLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_dynamic) && + "ParseObjCPropertyDynamic(): Expected '@dynamic'"); + SourceLocation loc = ConsumeToken(); // consume dynamic + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return 0; + } + while (Tok.is(tok::identifier)) { + ConsumeToken(); // consume property name + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // consume ',' + } + if (Tok.isNot(tok::semi)) + Diag(Tok, diag::err_expected_semi_after, "@dynamic"); + return 0; +} + +/// objc-throw-statement: +/// throw expression[opt]; +/// +Parser::StmtResult Parser::ParseObjCThrowStmt(SourceLocation atLoc) { + ExprResult Res; + ConsumeToken(); // consume throw + if (Tok.isNot(tok::semi)) { + Res = ParseExpression(); + if (Res.isInvalid) { + SkipUntil(tok::semi); + return true; + } + } + ConsumeToken(); // consume ';' + return Actions.ActOnObjCAtThrowStmt(atLoc, Res.Val); +} + +/// objc-synchronized-statement: +/// @synchronized '(' expression ')' compound-statement +/// +Parser::StmtResult Parser::ParseObjCSynchronizedStmt(SourceLocation atLoc) { + ConsumeToken(); // consume synchronized + if (Tok.isNot(tok::l_paren)) { + Diag (Tok, diag::err_expected_lparen_after, "@synchronized"); + return true; + } + ConsumeParen(); // '(' + ExprResult Res = ParseExpression(); + if (Res.isInvalid) { + SkipUntil(tok::semi); + return true; + } + if (Tok.isNot(tok::r_paren)) { + Diag (Tok, diag::err_expected_lbrace); + return true; + } + ConsumeParen(); // ')' + if (Tok.isNot(tok::l_brace)) { + Diag (Tok, diag::err_expected_lbrace); + return true; + } + StmtResult SynchBody = ParseCompoundStatementBody(); + if (SynchBody.isInvalid) + SynchBody = Actions.ActOnNullStmt(Tok.getLocation()); + return Actions.ActOnObjCAtSynchronizedStmt(atLoc, Res.Val, SynchBody.Val); +} + +/// objc-try-catch-statement: +/// @try compound-statement objc-catch-list[opt] +/// @try compound-statement objc-catch-list[opt] @finally compound-statement +/// +/// objc-catch-list: +/// @catch ( parameter-declaration ) compound-statement +/// objc-catch-list @catch ( catch-parameter-declaration ) compound-statement +/// catch-parameter-declaration: +/// parameter-declaration +/// '...' [OBJC2] +/// +Parser::StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { + bool catch_or_finally_seen = false; + + ConsumeToken(); // consume try + if (Tok.isNot(tok::l_brace)) { + Diag (Tok, diag::err_expected_lbrace); + return true; + } + StmtResult CatchStmts; + StmtResult FinallyStmt; + StmtResult TryBody = ParseCompoundStatementBody(); + if (TryBody.isInvalid) + TryBody = Actions.ActOnNullStmt(Tok.getLocation()); + + while (Tok.is(tok::at)) { + // At this point, we need to lookahead to determine if this @ is the start + // of an @catch or @finally. We don't want to consume the @ token if this + // is an @try or @encode or something else. + Token AfterAt = GetLookAheadToken(1); + if (!AfterAt.isObjCAtKeyword(tok::objc_catch) && + !AfterAt.isObjCAtKeyword(tok::objc_finally)) + break; + + SourceLocation AtCatchFinallyLoc = ConsumeToken(); + if (Tok.isObjCAtKeyword(tok::objc_catch)) { + StmtTy *FirstPart = 0; + ConsumeToken(); // consume catch + if (Tok.is(tok::l_paren)) { + ConsumeParen(); + EnterScope(Scope::DeclScope); + if (Tok.isNot(tok::ellipsis)) { + DeclSpec DS; + ParseDeclarationSpecifiers(DS); + // FIXME: Is BlockContext right? + Declarator DeclaratorInfo(DS, Declarator::BlockContext); + ParseDeclarator(DeclaratorInfo); + DeclTy *aBlockVarDecl = Actions.ActOnDeclarator(CurScope, + DeclaratorInfo, 0); + StmtResult stmtResult = + Actions.ActOnDeclStmt(aBlockVarDecl, DS.getSourceRange().getBegin(), + DeclaratorInfo.getSourceRange().getEnd()); + FirstPart = stmtResult.isInvalid ? 0 : stmtResult.Val; + } else + ConsumeToken(); // consume '...' + SourceLocation RParenLoc = ConsumeParen(); + + StmtResult CatchBody(true); + if (Tok.is(tok::l_brace)) + CatchBody = ParseCompoundStatementBody(); + else + Diag(Tok, diag::err_expected_lbrace); + if (CatchBody.isInvalid) + CatchBody = Actions.ActOnNullStmt(Tok.getLocation()); + CatchStmts = Actions.ActOnObjCAtCatchStmt(AtCatchFinallyLoc, RParenLoc, + FirstPart, CatchBody.Val, CatchStmts.Val); + ExitScope(); + } else { + Diag(AtCatchFinallyLoc, diag::err_expected_lparen_after, + "@catch clause"); + return true; + } + catch_or_finally_seen = true; + } else { + assert(Tok.isObjCAtKeyword(tok::objc_finally) && "Lookahead confused?"); + ConsumeToken(); // consume finally + + StmtResult FinallyBody(true); + if (Tok.is(tok::l_brace)) + FinallyBody = ParseCompoundStatementBody(); + else + Diag(Tok, diag::err_expected_lbrace); + if (FinallyBody.isInvalid) + FinallyBody = Actions.ActOnNullStmt(Tok.getLocation()); + FinallyStmt = Actions.ActOnObjCAtFinallyStmt(AtCatchFinallyLoc, + FinallyBody.Val); + catch_or_finally_seen = true; + break; + } + } + if (!catch_or_finally_seen) { + Diag(atLoc, diag::err_missing_catch_finally); + return true; + } + return Actions.ActOnObjCAtTryStmt(atLoc, TryBody.Val, CatchStmts.Val, + FinallyStmt.Val); +} + +/// objc-method-def: objc-method-proto ';'[opt] '{' body '}' +/// +Parser::DeclTy *Parser::ParseObjCMethodDefinition() { + DeclTy *MDecl = ParseObjCMethodPrototype(ObjCImpDecl); + // parse optional ';' + if (Tok.is(tok::semi)) + ConsumeToken(); + + // We should have an opening brace now. + if (Tok.isNot(tok::l_brace)) { + Diag(Tok, diag::err_expected_method_body); + + // Skip over garbage, until we get to '{'. Don't eat the '{'. + SkipUntil(tok::l_brace, true, true); + + // If we didn't find the '{', bail out. + if (Tok.isNot(tok::l_brace)) + return 0; + } + SourceLocation BraceLoc = Tok.getLocation(); + + // Enter a scope for the method body. + EnterScope(Scope::FnScope|Scope::DeclScope); + + // Tell the actions module that we have entered a method definition with the + // specified Declarator for the method. + Actions.ObjCActOnStartOfMethodDef(CurScope, MDecl); + + StmtResult FnBody = ParseCompoundStatementBody(); + + // If the function body could not be parsed, make a bogus compoundstmt. + if (FnBody.isInvalid) + FnBody = Actions.ActOnCompoundStmt(BraceLoc, BraceLoc, 0, 0, false); + + // Leave the function body scope. + ExitScope(); + + // TODO: Pass argument information. + Actions.ActOnFinishFunctionBody(MDecl, FnBody.Val); + return MDecl; +} + +Parser::StmtResult Parser::ParseObjCAtStatement(SourceLocation AtLoc) { + if (Tok.isObjCAtKeyword(tok::objc_try)) { + return ParseObjCTryStmt(AtLoc); + } else if (Tok.isObjCAtKeyword(tok::objc_throw)) + return ParseObjCThrowStmt(AtLoc); + else if (Tok.isObjCAtKeyword(tok::objc_synchronized)) + return ParseObjCSynchronizedStmt(AtLoc); + ExprResult Res = ParseExpressionWithLeadingAt(AtLoc); + if (Res.isInvalid) { + // If the expression is invalid, skip ahead to the next semicolon. Not + // doing this opens us up to the possibility of infinite loops if + // ParseExpression does not consume any tokens. + SkipUntil(tok::semi); + return true; + } + // Otherwise, eat the semicolon. + ExpectAndConsume(tok::semi, diag::err_expected_semi_after_expr); + return Actions.ActOnExprStmt(Res.Val); +} + +Parser::ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { + + switch (Tok.getKind()) { + case tok::string_literal: // primary-expression: string-literal + case tok::wide_string_literal: + return ParsePostfixExpressionSuffix(ParseObjCStringLiteral(AtLoc)); + default: + break; + } + + switch (Tok.getIdentifierInfo()->getObjCKeywordID()) { + case tok::objc_encode: + return ParsePostfixExpressionSuffix(ParseObjCEncodeExpression(AtLoc)); + case tok::objc_protocol: + return ParsePostfixExpressionSuffix(ParseObjCProtocolExpression(AtLoc)); + case tok::objc_selector: + return ParsePostfixExpressionSuffix(ParseObjCSelectorExpression(AtLoc)); + default: + Diag(AtLoc, diag::err_unexpected_at); + SkipUntil(tok::semi); + return true; + } +} + +/// objc-message-expr: +/// '[' objc-receiver objc-message-args ']' +/// +/// objc-receiver: +/// expression +/// class-name +/// type-name +Parser::ExprResult Parser::ParseObjCMessageExpression() { + assert(Tok.is(tok::l_square) && "'[' expected"); + SourceLocation LBracLoc = ConsumeBracket(); // consume '[' + + // Parse receiver + if (isTokObjCMessageIdentifierReceiver()) { + IdentifierInfo *ReceiverName = Tok.getIdentifierInfo(); + ConsumeToken(); + return ParseObjCMessageExpressionBody(LBracLoc, ReceiverName, 0); + } + + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid) { + Diag(Tok, diag::err_invalid_receiver_to_message); + SkipUntil(tok::r_square); + return Res; + } + return ParseObjCMessageExpressionBody(LBracLoc, 0, Res.Val); +} + +/// ParseObjCMessageExpressionBody - Having parsed "'[' objc-receiver", parse +/// the rest of a message expression. +/// +/// objc-message-args: +/// objc-selector +/// objc-keywordarg-list +/// +/// objc-keywordarg-list: +/// objc-keywordarg +/// objc-keywordarg-list objc-keywordarg +/// +/// objc-keywordarg: +/// selector-name[opt] ':' objc-keywordexpr +/// +/// objc-keywordexpr: +/// nonempty-expr-list +/// +/// nonempty-expr-list: +/// assignment-expression +/// nonempty-expr-list , assignment-expression +/// +Parser::ExprResult +Parser::ParseObjCMessageExpressionBody(SourceLocation LBracLoc, + IdentifierInfo *ReceiverName, + ExprTy *ReceiverExpr) { + // Parse objc-selector + SourceLocation Loc; + IdentifierInfo *selIdent = ParseObjCSelector(Loc); + + llvm::SmallVector<IdentifierInfo *, 12> KeyIdents; + llvm::SmallVector<Action::ExprTy *, 12> KeyExprs; + + if (Tok.is(tok::colon)) { + while (1) { + // Each iteration parses a single keyword argument. + KeyIdents.push_back(selIdent); + + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_colon); + SkipUntil(tok::semi); + return true; + } + ConsumeToken(); // Eat the ':'. + /// Parse the expression after ':' + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid) { + SkipUntil(tok::identifier); + return Res; + } + // We have a valid expression. + KeyExprs.push_back(Res.Val); + + // Check for another keyword selector. + selIdent = ParseObjCSelector(Loc); + if (!selIdent && Tok.isNot(tok::colon)) + break; + // We have a selector or a colon, continue parsing. + } + // Parse the, optional, argument list, comma separated. + while (Tok.is(tok::comma)) { + ConsumeToken(); // Eat the ','. + /// Parse the expression after ',' + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid) { + SkipUntil(tok::identifier); + return Res; + } + // We have a valid expression. + KeyExprs.push_back(Res.Val); + } + } else if (!selIdent) { + Diag(Tok, diag::err_expected_ident); // missing selector name. + SkipUntil(tok::semi); + return true; + } + + if (Tok.isNot(tok::r_square)) { + Diag(Tok, diag::err_expected_rsquare); + SkipUntil(tok::semi); + return true; + } + SourceLocation RBracLoc = ConsumeBracket(); // consume ']' + + unsigned nKeys = KeyIdents.size(); + if (nKeys == 0) + KeyIdents.push_back(selIdent); + Selector Sel = PP.getSelectorTable().getSelector(nKeys, &KeyIdents[0]); + + // We've just parsed a keyword message. + if (ReceiverName) + return Actions.ActOnClassMessage(CurScope, + ReceiverName, Sel, LBracLoc, RBracLoc, + &KeyExprs[0], KeyExprs.size()); + return Actions.ActOnInstanceMessage(ReceiverExpr, Sel, LBracLoc, RBracLoc, + &KeyExprs[0], KeyExprs.size()); +} + +Parser::ExprResult Parser::ParseObjCStringLiteral(SourceLocation AtLoc) { + ExprResult Res = ParseStringLiteralExpression(); + if (Res.isInvalid) return Res; + + // @"foo" @"bar" is a valid concatenated string. Eat any subsequent string + // expressions. At this point, we know that the only valid thing that starts + // with '@' is an @"". + llvm::SmallVector<SourceLocation, 4> AtLocs; + llvm::SmallVector<ExprTy*, 4> AtStrings; + AtLocs.push_back(AtLoc); + AtStrings.push_back(Res.Val); + + while (Tok.is(tok::at)) { + AtLocs.push_back(ConsumeToken()); // eat the @. + + ExprResult Res(true); // Invalid unless there is a string literal. + if (isTokenStringLiteral()) + Res = ParseStringLiteralExpression(); + else + Diag(Tok, diag::err_objc_concat_string); + + if (Res.isInvalid) { + while (!AtStrings.empty()) { + Actions.DeleteExpr(AtStrings.back()); + AtStrings.pop_back(); + } + return Res; + } + + AtStrings.push_back(Res.Val); + } + + return Actions.ParseObjCStringLiteral(&AtLocs[0], &AtStrings[0], + AtStrings.size()); +} + +/// objc-encode-expression: +/// @encode ( type-name ) +Parser::ExprResult Parser::ParseObjCEncodeExpression(SourceLocation AtLoc) { + assert(Tok.isObjCAtKeyword(tok::objc_encode) && "Not an @encode expression!"); + + SourceLocation EncLoc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "@encode"); + return true; + } + + SourceLocation LParenLoc = ConsumeParen(); + + TypeTy *Ty = ParseTypeName(); + + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + + return Actions.ParseObjCEncodeExpression(AtLoc, EncLoc, LParenLoc, Ty, + RParenLoc); +} + +/// objc-protocol-expression +/// @protocol ( protocol-name ) + +Parser::ExprResult Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) +{ + SourceLocation ProtoLoc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "@protocol"); + return true; + } + + SourceLocation LParenLoc = ConsumeParen(); + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return true; + } + IdentifierInfo *protocolId = Tok.getIdentifierInfo(); + ConsumeToken(); + + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + + return Actions.ParseObjCProtocolExpression(protocolId, AtLoc, ProtoLoc, + LParenLoc, RParenLoc); +} + +/// objc-selector-expression +/// @selector '(' objc-keyword-selector ')' +Parser::ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) +{ + SourceLocation SelectorLoc = ConsumeToken(); + + if (Tok.isNot(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen_after, "@selector"); + return 0; + } + + llvm::SmallVector<IdentifierInfo *, 12> KeyIdents; + SourceLocation LParenLoc = ConsumeParen(); + SourceLocation sLoc; + IdentifierInfo *SelIdent = ParseObjCSelector(sLoc); + if (!SelIdent && Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_ident); // missing selector name. + return 0; + } + KeyIdents.push_back(SelIdent); + unsigned nColons = 0; + if (Tok.isNot(tok::r_paren)) { + while (1) { + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected_colon); + break; + } + nColons++; + ConsumeToken(); // Eat the ':'. + if (Tok.is(tok::r_paren)) + break; + // Check for another keyword selector. + SourceLocation Loc; + SelIdent = ParseObjCSelector(Loc); + KeyIdents.push_back(SelIdent); + if (!SelIdent && Tok.isNot(tok::colon)) + break; + } + } + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + Selector Sel = PP.getSelectorTable().getSelector(nColons, &KeyIdents[0]); + return Actions.ParseObjCSelectorExpression(Sel, AtLoc, SelectorLoc, LParenLoc, + RParenLoc); + } |