summaryrefslogtreecommitdiffstats
path: root/clang/lib/Parse/ParseObjc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Parse/ParseObjc.cpp')
-rw-r--r--clang/lib/Parse/ParseObjc.cpp1578
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);
+ }
OpenPOWER on IntegriCloud