Patchwork ObjC/ObjC++ - @property parsing

login
register
mail settings
Submitter Nicola Pero
Date Oct. 25, 2010, 7:37 p.m.
Message ID <1288035477.37978763@192.168.2.227>
Download mbox | patch
Permalink /patch/69172/
State New
Headers show

Comments

Nicola Pero - Oct. 25, 2010, 7:37 p.m.
This patch is a first chunk of work on the parsing of @property in Objective-C.  It's a first
patch of a series as I'm updating the @property support in the compiler.  Hopefully I'll manage
to finish the standard Objective-C 2.0 @property/@synthesized/@dynamic features in time 
for 4.6.0 (at least, ignoring the new ABI and garbage collection).  Unfortunately, there's nothing
left to merge from FSF apple/trunk, so I'm on my own, but don't mind.

This patch partially rewrites the parsing of @property in Objective-C/Objective-C++, accomplishing 
the following:

 * it documents the syntax of @property before c_parser_objc_at_property_declaration
and cp_parser_objc_at_property_declaration.

 * it makes parsing of @property in Objective-C and Objective-C++ much more robust; all
the common syntax errors are now processed in a satisfactorily way (6 included testcases
contain a zoology of cases where the previous parser would get really confused because of
a syntax error while the new one manages to produce reasonable error messages and keep going) ;-)

 * it makes the parsing of @property in Objective-C and Objective-C++ almost identical (as
opposed to having two different implementations as we used to have); parsing is done in 
c_parser_objc_at_property_declaration() and cp_parser_objc_at_property_declaration() which are 
now almost identical (modulo replacing c_parser_peek_next_token with cp_lexer_peek_next_token etc).  
This means I have to maintain one parsing code instead of two (eg, it should immediately help 
with the follow-up patches, since work on @property parsing is not finished yet!) ;-)

 * it implements __attribute__s for @property in Objective-C++ (eg __attribute__((deprecated))).  
A fallout of syncing ObjC and ObjC++ is that I noticed these were missing in Objective-C++.  
Testcase at-property-2.mm shows this is now working (they are also working for Objective-C, 
as tested in at-property-2.m).

 * it improves parsing of properties and instance variables in Objective-C++; 'extern', 'static', 'typedef', 
'__thread' etc are invalid for properties and instance variables, but the compiler wasn't checking in Objective-C++.
Testcases (ivars-invalid-type-1.mm, at-property-3.mm) included showing it now works (and corresponding ObjC
testcase shows that ObjC parses them in the same way and emits similar errors [and always has]).

As a note, the patch also merges two @property parsing functions.  This is a bit of a no-op at this stage,
but it allows me to remove the global (to objc-act.c) static property_readonly etc. variables in the next 
patch.

Finally, a couple of ObjC++ testcases have a line or two where the compiler seems to be generating the
right messages, but I couldn't get the testsuite to PASS.  It seems to be a bug in the testsuite (is it
because they are on the last line ?); I don't want to spend time on that right now [as I need to rush to finish 
@property! :-)] so I added FIXMEs there and left these to be fixed in stage 3. :-)

Ok to commit to trunk ?

Thanks

In gcc/:
2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>

        * c-parser.c (c_parser_objc_at_property): Renamed to
        c_parser_objc_at_property_declaration.  Updated calls to
        objc_add_property_variable, now objc_add_property_declaration.
        Code rewritten to be much more robust in recovering from syntax
        errors.  Added comments.
        (c_parser_objc_property_attrlist): Removed.
        (c_parser_external_declaration): Updated calls to
        c_parser_objc_at_property, now
        c_parser_objc_at_property_declaration.
        (c_parser_objc_methodprotolist): Same change.
        
In gcc/c-family/:
2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>

        * c-common.h (objc_add_property_variable): Renamed to
        objc_add_property_declaration.  Added location argument.
        * stub-objc.c (objc_add_property_variable): Same change.
        
In gcc/cp/:
2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>

        * parser.c (cp_parser_objc_property_decl): Renamed to
        cp_parser_objc_struct_declaration.  Return the parsed trees
        instead of calling objc_add_property_variable directly.  Detect
        missing or invalid declspecs.  Implemented attributes.  Do not eat
        the ';' at the end.  Exit loop whenever a non-comma is parsed, not
        just EOF.
        (cp_parser_objc_at_property): Renamed to
        cp_parser_objc_at_property_declaration.  Updated calls to
        objc_add_property_variable, now objc_add_property_declaration, and
        to cp_parser_objc_property_decl, now
        cp_parser_objc_struct_declaration.  Rewritten all code to be more
        robust in dealing with syntax errors, and almost identical to the
        one in c_parser_objc_at_property_declaration.
        (cp_parser_objc_property_attrlist): Removed.
        (cp_parser_objc_method_prototype_list): Updated call to
        cp_parser_objc_at_property.
        (cp_parser_objc_method_definition_list): Same change.
        (cp_parser_objc_class_ivars): Detect a number of invalid
        declarations of instance variables and produce errors when they
        are found.
        
In gcc/objc/:
2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc-act.c (objc_add_property_variable): Renamed to
        objc_add_property_declaration.  Added location argument.  Updated
        warnings and errors to use it.  Use error, not fatal_error, if a
        property declaration is found outside an interface or
        implementation context.
        
In gcc/testsuite/:
2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc.dg/property/at-property-1.m: New.        
        * objc.dg/property/at-property-2.m: New.
        * objc.dg/property/at-property-3.m: New.
        * objc.dg/ivar-invalid-type-1.m: New.
        * obj-c++.dg/property/at-property-1.mm: New.
        * obj-c++.dg/property/at-property-2.mm: New.
        * obj-c++.dg/property/at-property-3.mm: New.
        * obj-c++.dg/ivar-invalid-type-1.mm: New.
        * objc.dg/property/property-neg-6.m: Updated testcase for updates
        in error reporting.

Patch

Index: c-family/ChangeLog
===================================================================
--- c-family/ChangeLog	(revision 165924)
+++ c-family/ChangeLog	(working copy)
@@ -1,3 +1,9 @@ 
+2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* c-common.h (objc_add_property_variable): Renamed to
+	objc_add_property_declaration.  Added location argument.
+	* stub-objc.c (objc_add_property_variable): Same change.
+	
 2010-10-23  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	* c-common.h (objc_maybe_printable_name): New.
Index: c-family/c-common.h
===================================================================
--- c-family/c-common.h	(revision 165924)
+++ c-family/c-common.h	(working copy)
@@ -1043,7 +1043,7 @@  extern void objc_finish_foreach_loop (location_t,
 extern void objc_set_property_attr 
   (location_t, objc_property_attribute_kind, tree);
 extern bool  objc_method_decl (enum tree_code);
-extern void objc_add_property_variable (tree);
+extern void objc_add_property_declaration (location_t, tree);
 extern tree objc_build_getter_call (tree, tree);
 extern tree objc_build_setter_call (tree, tree);
 extern void objc_add_synthesize_declaration (location_t, tree);
Index: c-family/stub-objc.c
===================================================================
--- c-family/stub-objc.c	(revision 165924)
+++ c-family/stub-objc.c	(working copy)
@@ -331,7 +331,7 @@  objc_set_property_attr (location_t ARG_UNUSED (loc
 }
 
 void
-objc_add_property_variable (tree ARG_UNUSED (prop))
+objc_add_property_declaration (location_t ARG_UNUSED (loc), tree ARG_UNUSED (prop))
 {
 }
 
Index: objc/objc-act.c
===================================================================
--- objc/objc-act.c	(revision 165924)
+++ objc/objc-act.c	(working copy)
@@ -871,7 +871,7 @@  objc_set_property_attr (location_t loc, objc_prope
 */
 
 void
-objc_add_property_variable (tree decl)
+objc_add_property_declaration (location_t location, tree decl)
 {
   tree property_decl;
   tree x;
@@ -882,7 +882,7 @@  void
       interface = lookup_interface (CLASS_NAME (objc_implementation_context));
       if (!interface)
 	{
-	  error ("no class property can be implemented without an interface");
+	  error_at (location, "no class property can be implemented without an interface");
 	  return;
 	}
       if (TREE_CODE (objc_implementation_context) == CATEGORY_IMPLEMENTATION_TYPE)
@@ -891,14 +891,14 @@  void
 				     CLASS_SUPER_NAME (objc_implementation_context));	
 	  if (!interface)
 	    {
-	      error ("no category property can be implemented without an interface");
+	      error_at (location, "no category property can be implemented without an interface");
 	      return;
 	    }
         }
     }
   else if (!objc_interface_context)
     {
-      fatal_error ("property declaration not in @interface or @implementation context");
+      error_at (location, "property declaration not in @interface or @implementation context");
       return;
     }
 
@@ -923,13 +923,13 @@  void
       /* Issue error if property and an ivar name match. */
       if (TREE_CODE (objc_interface_context) == CLASS_INTERFACE_TYPE
 	  && is_ivar (CLASS_IVARS (objc_interface_context), DECL_NAME (decl)))
-	error ("property %qD may not have the same name as an ivar in the class", decl);
+	error_at (location, "property %qD may not have the same name as an ivar in the class", decl);
       /* must check for duplicate property declarations. */
       for (x = CLASS_PROPERTY_DECL (objc_interface_context); x; x = TREE_CHAIN (x))
 	{
 	  if (PROPERTY_NAME (x) == DECL_NAME (decl))
 	    {
-	      error ("duplicate property declaration %qD", decl);
+	      error_at (location, "duplicate property declaration %qD", decl);
 	      return;
 	    }
 	}
@@ -945,26 +945,26 @@  void
 	  break;
       if (!x)
 	{
-	  error ("no declaration of property %qD found in the interface", decl);
+	  error_at (location, "no declaration of property %qD found in the interface", decl);
 	  return;
 	}
       /* readonlys must also match. */
       if (PROPERTY_READONLY (x) != PROPERTY_READONLY (property_decl))
 	{
-	  error ("property %qD %<readonly%> attribute conflicts with its" 
-		 " interface version", decl);
+	  error_at (location, "property %qD %<readonly%> attribute conflicts with its" 
+		    " interface version", decl);
 	}
       /* copies must also match. */
       if (PROPERTY_COPIES (x) != PROPERTY_COPIES (property_decl))
 	{
-	  error ("property %qD %<copies%> attribute conflicts with its" 
-		 " interface version", decl);
+	  error_at (location, "property %qD %<copies%> attribute conflicts with its" 
+		    " interface version", decl);
 	}
       /* Cannot have readonly and setter attribute for the same property. */
       if (PROPERTY_READONLY (property_decl) == boolean_true_node &&
 	  PROPERTY_SETTER_NAME (property_decl))
 	{
-	  warning (0, "a %<readonly%> property cannot have a setter (ignored)");
+	  warning_at (location, 0, "a %<readonly%> property cannot have a setter (ignored)");
 	  PROPERTY_SETTER_NAME (property_decl) = NULL_TREE;
 	}
       /* Add the property to the list of properties for current implementation. */
Index: objc/ChangeLog
===================================================================
--- objc/ChangeLog	(revision 165924)
+++ objc/ChangeLog	(working copy)
@@ -1,3 +1,11 @@ 
+2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc-act.c (objc_add_property_variable): Renamed to
+	objc_add_property_declaration.  Added location argument.  Updated
+	warnings and errors to use it.  Use error, not fatal_error, if a
+	property declaration is found outside an interface or
+	implementation context.
+	
 2010-10-24  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	* objc-act.c (objc_build_keyword_decl): Updated comments.  Do not
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 165924)
+++ ChangeLog	(working copy)
@@ -1,3 +1,16 @@ 
+2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* c-parser.c (c_parser_objc_at_property): Renamed to
+	c_parser_objc_at_property_declaration.  Updated calls to
+	objc_add_property_variable, now objc_add_property_declaration.
+	Code rewritten to be much more robust in recovering from syntax
+	errors.  Added comments.
+	(c_parser_objc_property_attrlist): Removed.
+	(c_parser_external_declaration): Updated calls to
+	c_parser_objc_at_property, now
+	c_parser_objc_at_property_declaration.
+	(c_parser_objc_methodprotolist): Same change.
+	
 2010-10-25  Eric Botcazou  <ebotcazou@adacore.com>
 
 	* configure.ac: Use $cpu_type instead of $target to define the nop.
Index: testsuite/ChangeLog
===================================================================
--- testsuite/ChangeLog	(revision 165924)
+++ testsuite/ChangeLog	(working copy)
@@ -1,3 +1,16 @@ 
+2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc.dg/property/at-property-1.m: New.	
+	* objc.dg/property/at-property-2.m: New.
+	* objc.dg/property/at-property-3.m: New.
+	* objc.dg/ivar-invalid-type-1.m: New.
+	* obj-c++.dg/property/at-property-1.mm: New.
+	* obj-c++.dg/property/at-property-2.mm: New.
+	* obj-c++.dg/property/at-property-3.mm: New.	
+	* obj-c++.dg/ivar-invalid-type-1.mm: New.
+	* objc.dg/property/property-neg-6.m: Updated testcase for updates
+	in error reporting.
+	
 2010-10-25  Eric Botcazou  <ebotcazou@adacore.com>
 
 	* gnat.dg/in_out_parameter2.adb: New test.
Index: testsuite/objc.dg/property/at-property-1.m
===================================================================
--- testsuite/objc.dg/property/at-property-1.m	(revision 0)
+++ testsuite/objc.dg/property/at-property-1.m	(revision 0)
@@ -0,0 +1,21 @@ 
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property;                      /* { dg-error "expected" } */
+@property int;                  /* { dg-error "expected identifier" } */
+                                /* { dg-warning "declaration does not declare anything" "" { target *-*-* } 10 } */
+@property int a;
+@property int b, c;
+@property () int d;             /* { dg-error "expected identifier" } */
+@property (readonly) int e;
+@property (readonly,) int f;    /* { dg-error "expected identifier" } */
+@property (xxx) int g;          /* { dg-error "unknown property attribute" } */
+@property (readonly,xxx) int h; /* { dg-error "unknown property attribute" } */
+@property ( int i;              /* { dg-error "unknown property attribute" } */
+/* Because the last syntax error opens a '(' and never closes it, we get to the end of input.  */
+@end                            /* { dg-error "expected ..end. at end of input" } */
Index: testsuite/objc.dg/property/at-property-2.m
===================================================================
--- testsuite/objc.dg/property/at-property-2.m	(revision 0)
+++ testsuite/objc.dg/property/at-property-2.m	(revision 0)
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property id name __attribute__((deprecated));
+@property id table __attribute__((xxx));        /* { dg-warning ".xxx. attribute directive ignored" } */
+@property void function (void);                 /* { dg-error "declared as a function" } */
+@property typedef int j;                        /* { dg-error "expected" } */
+@end
Index: testsuite/objc.dg/property/at-property-3.m
===================================================================
--- testsuite/objc.dg/property/at-property-3.m	(revision 0)
+++ testsuite/objc.dg/property/at-property-3.m	(revision 0)
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property volatile int a;  /* This is allowed */
+@property extern int b;    /* { dg-error "expected" } */
+@property static int c;    /* { dg-error "expected" } */
+@property inline int d;    /* { dg-error "expected" } */
+@property typedef int e;   /* { dg-error "expected" } */
+@property __thread int f;  /* { dg-error "expected" } */
+@end
Index: testsuite/objc.dg/property/property-neg-6.m
===================================================================
--- testsuite/objc.dg/property/property-neg-6.m	(revision 165924)
+++ testsuite/objc.dg/property/property-neg-6.m	(working copy)
@@ -6,3 +6,4 @@ 
   int iVar;
 }
 @property int FooBar /* { dg-error "expected ':', ',', ';', '\}' or '__attribute__' at end of input" } */
+/* { dg-error "expected ..end. at end of input" "" { target *-*-* } 8 } */
Index: testsuite/objc.dg/ivar-invalid-type-1.m
===================================================================
--- testsuite/objc.dg/ivar-invalid-type-1.m	(revision 0)
+++ testsuite/objc.dg/ivar-invalid-type-1.m	(revision 0)
@@ -0,0 +1,19 @@ 
+/* { dg-do compile } */
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@end
+
+@interface MySubClass
+{
+  volatile int a;  /* This is allowed */
+  extern int b;    /* { dg-error "expected" } */
+  static int c;    /* { dg-error "expected" } */
+  inline int d;    /* { dg-error "expected" } */
+  typedef int e;   /* { dg-error "expected" } */
+  __thread int f;  /* { dg-error "expected" } */
+}
+@end
Index: testsuite/obj-c++.dg/property/at-property-1.mm
===================================================================
--- testsuite/obj-c++.dg/property/at-property-1.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/at-property-1.mm	(revision 0)
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property;                      /* { dg-error "expected identifier" } */
+@property int;                  /* { dg-error "expected identifier" } */
+@property int a;
+@property int b, c;
+@property () int d;             /* { dg-error "expected identifier" } */
+@property (readonly) int e;
+@property (readonly,) int f;    /* { dg-error "expected identifier" } */
+@property (xxx) int g;          /* { dg-error "unknown property attribute" } */
+@property (readonly,xxx) int h; /* { dg-error "unknown property attribute" } */
+/* FIXME - there is a problem with the testuite in running the following test.  The compiler
+   generates the messages, but the testsuite still complains.  */
+/*@property ( int i;*/          /* dg-error "unknown property attribute" */
+                                /* dg-error "expected ... "       "" { target *-*-* } 18 */
+                                /* dg-error "expected identfier " "" { target *-*-* } 18 */
+@end
Index: testsuite/obj-c++.dg/property/at-property-2.mm
===================================================================
--- testsuite/obj-c++.dg/property/at-property-2.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/at-property-2.mm	(revision 0)
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property id name __attribute__((deprecated));
+@property id table __attribute__((xxx));        /* { dg-warning ".xxx. attribute directive ignored" } */
+@property void function (void);                 /* { dg-error "can.t make .function. into a method" } */
+@property typedef int j;                        /* { dg-error "invalid type for property" } */
+@end
Index: testsuite/obj-c++.dg/property/at-property-3.mm
===================================================================
--- testsuite/obj-c++.dg/property/at-property-3.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/at-property-3.mm	(revision 0)
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property volatile int a;  /* This is allowed */
+@property extern int b;    /* { dg-error "invalid type" } */
+@property static int c;    /* { dg-error "invalid type" } */
+@property inline int d;    /* { dg-error "declared as an .inline." } */
+@property typedef int e;   /* { dg-error "invalid type" } */
+@property __thread int f;  /* { dg-error "invalid type" } */
+@end
Index: testsuite/obj-c++.dg/ivar-invalid-type-1.mm
===================================================================
--- testsuite/obj-c++.dg/ivar-invalid-type-1.mm	(revision 0)
+++ testsuite/obj-c++.dg/ivar-invalid-type-1.mm	(revision 0)
@@ -0,0 +1,19 @@ 
+/* { dg-do compile } */
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@end
+
+@interface MySubClass
+{
+  volatile int a;  /* This is allowed */
+  extern int b;    /* { dg-error "invalid type" } */
+  static int c;    /* { dg-error "invalid type" } */
+  inline int d;    /* { dg-error "declared as an .inline." } */
+  typedef int e;   /* { dg-error "invalid type" } */
+  __thread int f;  /* dg-error "invalid type" */ /* FIXME: The compiler generates this message, but the testsuite does not match it.  */
+}
+@end
Index: cp/ChangeLog
===================================================================
--- cp/ChangeLog	(revision 165924)
+++ cp/ChangeLog	(working copy)
@@ -1,3 +1,26 @@ 
+2010-10-25  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* parser.c (cp_parser_objc_property_decl): Renamed to
+	cp_parser_objc_struct_declaration.  Return the parsed trees
+	instead of calling objc_add_property_variable directly.  Detect
+	missing or invalid declspecs.  Implemented attributes.  Do not eat
+	the ';' at the end.  Exit loop whenever a non-comma is parsed, not
+	just EOF.
+	(cp_parser_objc_at_property): Renamed to
+	cp_parser_objc_at_property_declaration.  Updated calls to
+	objc_add_property_variable, now objc_add_property_declaration, and
+	to cp_parser_objc_property_decl, now
+	cp_parser_objc_struct_declaration.  Rewritten all code to be more
+	robust in dealing with syntax errors, and almost identical to the
+	one in c_parser_objc_at_property_declaration.
+	(cp_parser_objc_property_attrlist): Removed.
+	(cp_parser_objc_method_prototype_list): Updated call to
+	cp_parser_objc_at_property.
+	(cp_parser_objc_method_definition_list): Same change.
+	(cp_parser_objc_class_ivars): Detect a number of invalid
+	declarations of instance variables and produce errors when they
+	are found.
+	
 2010-10-24  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	Removed Objective-C++ specific replacement of cxx_printable_name.
Index: cp/parser.c
===================================================================
--- cp/parser.c	(revision 165924)
+++ cp/parser.c	(working copy)
@@ -2098,13 +2098,13 @@  static tree cp_parser_objc_statement
   (cp_parser *);
 static bool cp_parser_objc_valid_prefix_attributes
   (cp_parser *, tree *);
-static void cp_parser_objc_at_property 
+static void cp_parser_objc_at_property_declaration 
   (cp_parser *) ;
 static void cp_parser_objc_at_synthesize_declaration 
   (cp_parser *) ;
 static void cp_parser_objc_at_dynamic_declaration
   (cp_parser *) ;
-static void cp_parser_objc_property_decl 
+static tree cp_parser_objc_struct_declaration
   (cp_parser *) ;
 
 /* Utility Routines */
@@ -21752,7 +21752,7 @@  cp_parser_objc_method_prototype_list (cp_parser* p
 	  cp_parser_consume_semicolon_at_end_of_statement (parser);
 	}
       else if (token->keyword == RID_AT_PROPERTY)
-	cp_parser_objc_at_property (parser);
+	cp_parser_objc_at_property_declaration (parser);
       else if (token->keyword == RID_ATTRIBUTE 
       	       && cp_parser_objc_method_maybe_bad_prefix_attributes(parser))
 	warning_at (cp_lexer_peek_token (parser->lexer)->location, 
@@ -21819,8 +21819,10 @@  cp_parser_objc_method_definition_list (cp_parser*
 	      objc_finish_method_definition (meth);
 	    }
 	}
+      /* The following case will be removed once @synthesize is
+	 completely implemented.  */
       else if (token->keyword == RID_AT_PROPERTY)
-	cp_parser_objc_at_property (parser);
+	cp_parser_objc_at_property_declaration (parser);
       else if (token->keyword == RID_AT_SYNTHESIZE)
 	cp_parser_objc_at_synthesize_declaration (parser);
       else if (token->keyword == RID_AT_DYNAMIC)
@@ -21873,6 +21875,28 @@  cp_parser_objc_class_ivars (cp_parser* parser)
 				    CP_PARSER_FLAGS_OPTIONAL,
 				    &declspecs,
 				    &decl_class_or_enum_p);
+
+      /* auto, register, static, extern, mutable.  */
+      if (declspecs.storage_class != sc_none)
+	{
+	  cp_parser_error (parser, "invalid type for instance variable");	  
+	  declspecs.storage_class = sc_none;
+	}
+
+      /* __thread.  */
+      if (declspecs.specs[(int) ds_thread])
+	{
+	  cp_parser_error (parser, "invalid type for instance variable");
+	  declspecs.specs[(int) ds_thread] = 0;
+	}
+      
+      /* typedef.  */
+      if (declspecs.specs[(int) ds_typedef])
+	{
+	  cp_parser_error (parser, "invalid type for instance variable");
+	  declspecs.specs[(int) ds_typedef] = 0;
+	}
+
       prefix_attributes = declspecs.attributes;
       declspecs.attributes = NULL_TREE;
 
@@ -22318,146 +22342,277 @@  cp_parser_objc_valid_prefix_attributes (cp_parser*
   return false;  
 }
 
-/* This routine parses the propery declarations. */
+/* This routine is a minimal replacement for
+   c_parser_struct_declaration () used when parsing the list of
+   types/names or ObjC++ properties.  For example, when parsing the
+   code
 
-static void
-cp_parser_objc_property_decl (cp_parser *parser)
+   @property (readonly) int a, b, c;
+
+   this function is responsible for parsing "int a, int b, int c" and
+   returning the declarations as CHAIN of DECLs.
+
+   TODO: Share this code with cp_parser_objc_class_ivars.  It's very
+   similar parsing.  */
+static tree
+cp_parser_objc_struct_declaration (cp_parser *parser)
 {
-  int declares_class_or_enum;
+  tree decls = NULL_TREE;
   cp_decl_specifier_seq declspecs;
+  int decl_class_or_enum_p;
+  tree prefix_attributes;
 
   cp_parser_decl_specifier_seq (parser,
-                                CP_PARSER_FLAGS_NONE,
-                                &declspecs,
-                                &declares_class_or_enum);
+				CP_PARSER_FLAGS_NONE,
+				&declspecs,
+				&decl_class_or_enum_p);
+
+  if (declspecs.type == error_mark_node)
+    return error_mark_node;
+
+  /* auto, register, static, extern, mutable.  */
+  if (declspecs.storage_class != sc_none)
+    {
+      cp_parser_error (parser, "invalid type for property");
+      declspecs.storage_class = sc_none;
+    }
+  
+  /* __thread.  */
+  if (declspecs.specs[(int) ds_thread])
+    {
+      cp_parser_error (parser, "invalid type for property");
+      declspecs.specs[(int) ds_thread] = 0;
+    }
+  
+  /* typedef.  */
+  if (declspecs.specs[(int) ds_typedef])
+    {
+      cp_parser_error (parser, "invalid type for property");
+      declspecs.specs[(int) ds_typedef] = 0;
+    }
+
+  prefix_attributes = declspecs.attributes;
+  declspecs.attributes = NULL_TREE;
+
   /* Keep going until we hit the `;' at the end of the declaration. */
   while (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
     {
-      tree property;
+      tree attributes, first_attribute, decl;
+      cp_declarator *declarator;
       cp_token *token;
-      cp_declarator *declarator
-	= cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
-				NULL, NULL, false);
-      property = grokdeclarator (declarator, &declspecs, NORMAL,0, NULL);
-      /* Recover from any kind of error in property declaration. */
-      if (property == error_mark_node || property == NULL_TREE)
-	return;
 
-      /* Add to property list. */
-      objc_add_property_variable (copy_node (property));
+      /* Parse the declarator.  */
+      declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+					 NULL, NULL, false);
+
+      /* Look for attributes that apply to the ivar.  */
+      attributes = cp_parser_attributes_opt (parser);
+      /* Remember which attributes are prefix attributes and
+	 which are not.  */
+      first_attribute = attributes;
+      /* Combine the attributes.  */
+      attributes = chainon (prefix_attributes, attributes);
+      
+      decl = grokfield (declarator, &declspecs,
+			NULL_TREE, /*init_const_expr_p=*/false,
+			NULL_TREE, attributes);
+
+      if (decl == error_mark_node || decl == NULL_TREE)
+	return error_mark_node;
+      
+      /* Reset PREFIX_ATTRIBUTES.  */
+      while (attributes && TREE_CHAIN (attributes) != first_attribute)
+	attributes = TREE_CHAIN (attributes);
+      if (attributes)
+	TREE_CHAIN (attributes) = NULL_TREE;
+
+      DECL_CHAIN (decl) = decls;
+      decls = decl;
+
       token = cp_lexer_peek_token (parser->lexer);
       if (token->type == CPP_COMMA)
 	{
 	  cp_lexer_consume_token (parser->lexer);  /* Eat ','.  */
 	  continue;
 	}
-      else if (token->type == CPP_EOF)
+      else
 	break;
     }
-  /* Eat ';' if present, or issue an error.  */
-  cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
+  return decls;
 }
 
-/* ObjC @property. */
-/* Parse a comma-separated list of property attributes.  
-   The lexer does not recognize */
+/* Parse an Objective-C @property declaration.  The syntax is:
 
+   objc-property-declaration:
+     '@property' objc-property-attributes[opt] struct-declaration ;
+
+   objc-property-attributes:
+    '(' objc-property-attribute-list ')'
+
+   objc-property-attribute-list:
+     objc-property-attribute
+     objc-property-attribute-list, objc-property-attribute
+
+   objc-property-attribute
+     'getter' = identifier
+     'setter' = identifier
+     'readonly'
+     'readwrite'
+     'assign'
+     'retain'
+     'copy'
+     'nonatomic'
+
+  For example:
+    @property NSString *name;
+    @property (readonly) id object;
+    @property (retain, nonatomic, getter=getTheName) id name;
+    @property int a, b, c;
+
+   PS: This function is identical to
+   c_parser_objc_at_property_declaration for C.  Keep them in sync.
+
+   WORK IN PROGRESS: At the moment, the list of attributes that are
+   parsed is different from the above list.  It will be updated to use
+   the above list at the same time as @synthesize is implemented.  */
 static void 
-cp_parser_objc_property_attrlist (cp_parser *parser)
+cp_parser_objc_at_property_declaration (cp_parser *parser)
 {
-  cp_token *token;
-  /* Initialize to an empty list.  */
-  objc_set_property_attr (cp_lexer_peek_token (parser->lexer)->location,
-			  OBJC_PATTR_INIT, NULL_TREE);
+  tree properties;
+  location_t loc;
+  loc = cp_lexer_peek_token (parser->lexer)->location;
 
-  /* The list is optional.  */
-  if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN))
-    return;
+  cp_lexer_consume_token (parser->lexer);  /* Eat '@property'.  */
 
-  /* Eat the '('.  */
-  cp_lexer_consume_token (parser->lexer);
+  /* Initialize attributes to an empty list.  */
+  objc_set_property_attr (loc, OBJC_PATTR_INIT, NULL_TREE);
 
-  token = cp_lexer_peek_token (parser->lexer);
-  while (token->type != CPP_CLOSE_PAREN && token->type != CPP_EOF)
+  /* Parse the optional attribute list...  */
+  if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
-      location_t loc = token->location;
-      tree node = cp_parser_identifier (parser);
-      if (node == ridpointers [(int) RID_READONLY])
-	objc_set_property_attr (loc, OBJC_PATTR_READONLY, NULL_TREE);
-      else if (node == ridpointers [(int) RID_GETTER]
-	       || node == ridpointers [(int) RID_SETTER]
-	       || node == ridpointers [(int) RID_IVAR])
+      /* Eat the '('.  */
+      cp_lexer_consume_token (parser->lexer);
+
+      while (true)
 	{
-	  /* Do the getter/setter/ivar attribute. */
-	  token = cp_lexer_consume_token (parser->lexer);
-	  if (token->type == CPP_EQ)
+	  bool syntax_error = false;
+	  cp_token *token = cp_lexer_peek_token (parser->lexer);
+      	  enum rid keyword;
+
+	  if (token->type != CPP_NAME)
 	    {
-	      tree attr_ident = cp_parser_identifier (parser);
+	      cp_parser_error (parser, "expected identifier");
+	      break;
+	    }
+	  keyword = C_RID_CODE (token->u.value);
+	  switch (keyword)
+	    {
+	      tree ident;
 	      objc_property_attribute_kind pkind;
-	      if (node == ridpointers [(int) RID_GETTER])
-		pkind = OBJC_PATTR_GETTER;
-	      else if (node == ridpointers [(int) RID_SETTER])
+	    case RID_READONLY:
+	      cp_lexer_consume_token (parser->lexer);
+	      objc_set_property_attr (loc, OBJC_PATTR_READONLY, NULL_TREE);
+	      break;
+	    case RID_GETTER:
+	    case RID_SETTER:
+	    case RID_IVAR:
+	      cp_lexer_consume_token (parser->lexer);
+	      if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ))
 		{
+		  cp_parser_error (parser,
+				   "getter/setter/ivar attribute must be followed by %<=%>");
+		  syntax_error = true;
+		  break;
+		}
+	      cp_lexer_consume_token (parser->lexer); /* eat the = */
+	      if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME))
+		{
+		  cp_parser_error (parser, "expected identifier");
+		  syntax_error = true;
+		  break;
+		}
+	      ident = cp_lexer_peek_token (parser->lexer)->u.value;
+	      cp_lexer_consume_token (parser->lexer);
+	      if (keyword == RID_SETTER)
+		{
 		  pkind = OBJC_PATTR_SETTER;
-		  /* Consume the ':' which must always follow the setter name. */
-		  if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
-		    cp_lexer_consume_token (parser->lexer); 
-		  else
+		  /* Eat the identifier, and look for the following : */
+		  if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
 		    {
-		      error_at (token->location,
-				"setter name must be followed by %<:%>");
+		      cp_parser_error (parser,
+				      "setter name must be followed by %<:%>");
+		      syntax_error = true;
 		      break;
 		    }
+		  cp_lexer_consume_token (parser->lexer);
 		}
-	      else 
+	      else if (keyword == RID_GETTER)
+		pkind = OBJC_PATTR_GETTER;
+	      else
 		pkind = OBJC_PATTR_IVAR;
-	      objc_set_property_attr (loc, pkind, attr_ident);	  
+	      objc_set_property_attr (loc, pkind, ident);
+	      break;
+	    case RID_COPIES:
+	      cp_lexer_consume_token (parser->lexer);
+	      objc_set_property_attr (loc, OBJC_PATTR_COPIES, NULL_TREE);
+	      break;
+	    default:
+	      if (token->type == CPP_CLOSE_PAREN)
+		cp_parser_error (parser, "expected identifier");
+	      else
+		{
+		  cp_lexer_consume_token (parser->lexer);
+		  cp_parser_error (parser, "unknown property attribute");
+		}
+	      syntax_error = true;
+	      break;
 	    }
+
+	  if (syntax_error)
+	    break;
+	  
+	  if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    cp_lexer_consume_token (parser->lexer);
 	  else
-	    {
-	      error_at (token->location,
-	      	"getter/setter/ivar attribute must be followed by %<=%>");
-	      break;
-	    }
+	    break;
 	}
-      else if (node == ridpointers [(int) RID_COPIES])
-	objc_set_property_attr (loc, OBJC_PATTR_COPIES, NULL_TREE);
-      else
+
+      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
 	{
-	  error_at (token->location,"unknown property attribute");
-	  break;
+	  cp_parser_skip_to_closing_parenthesis (parser,
+						 /*recovering=*/true,
+						 /*or_comma=*/false,
+						 /*consume_paren=*/true);
 	}
-      if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+    }
+
+  /* ... and the property declaration(s).  */
+  properties = cp_parser_objc_struct_declaration (parser);
+
+  if (properties == error_mark_node)
+    {
+      cp_parser_skip_to_end_of_statement (parser);
+      /* If the next token is now a `;', consume it.  */
+      if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
 	cp_lexer_consume_token (parser->lexer);
-      else if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN))
-	warning_at (token->location, 0, 
-		    "property attributes should be separated by a %<,%>");
-      token = cp_lexer_peek_token (parser->lexer);	  
+      return;
     }
 
-  if (token->type != CPP_CLOSE_PAREN)
-    error_at (token->location,
-	      "syntax error in @property's attribute declaration");
+  if (properties == NULL_TREE)
+    cp_parser_error (parser, "expected identifier");
   else
-    /* Consume ')' */
-    cp_lexer_consume_token (parser->lexer);
+    {
+      /* Comma-separated properties are chained together in
+	 reverse order; add them one by one.  */
+      properties = nreverse (properties);
+      
+      for (; properties; properties = TREE_CHAIN (properties))
+	objc_add_property_declaration (loc, copy_node (properties));
+    }
+  
+  cp_parser_consume_semicolon_at_end_of_statement (parser);
 }
 
-/* This function parses a @property declaration inside an objective class
-   or its implementation. */
-
-static void 
-cp_parser_objc_at_property (cp_parser *parser)
-{
-  /* Consume @property */
-  cp_lexer_consume_token (parser->lexer);
-
-  /* Parse optional attributes list...  */
-  cp_parser_objc_property_attrlist (parser);
-  /* ... and the property declaration(s).  */
-  cp_parser_objc_property_decl (parser);
-}
-
 /* Parse an Objective-C++ @synthesize declaration.  The syntax is:
 
    objc-synthesize-declaration:
Index: c-parser.c
===================================================================
--- c-parser.c	(revision 165924)
+++ c-parser.c	(working copy)
@@ -1081,7 +1081,7 @@  static tree c_parser_objc_selector_arg (c_parser *
 static tree c_parser_objc_receiver (c_parser *);
 static tree c_parser_objc_message_args (c_parser *);
 static tree c_parser_objc_keywordexpr (c_parser *);
-static void c_parser_objc_at_property (c_parser *) ;
+static void c_parser_objc_at_property_declaration (c_parser *);
 static void c_parser_objc_at_synthesize_declaration (c_parser *);
 static void c_parser_objc_at_dynamic_declaration (c_parser *);
 static bool c_parser_objc_diagnose_bad_element_prefix
@@ -1185,7 +1185,7 @@  c_parser_external_declaration (c_parser *parser)
 	  break;
 	case RID_AT_PROPERTY:
 	  gcc_assert (c_dialect_objc ());
-	  c_parser_objc_at_property (parser);
+	  c_parser_objc_at_property_declaration (parser);
 	  break;
 	case RID_AT_SYNTHESIZE:
 	  gcc_assert (c_dialect_objc ());
@@ -6997,7 +6997,7 @@  c_parser_objc_methodprotolist (c_parser *parser)
 	  if (c_parser_next_token_is_keyword (parser, RID_AT_END))
 	    return;
 	  else if (c_parser_next_token_is_keyword (parser, RID_AT_PROPERTY))
-	    c_parser_objc_at_property (parser);
+	    c_parser_objc_at_property_declaration (parser);
 	  else if (c_parser_next_token_is_keyword (parser, RID_AT_OPTIONAL))
 	    {
 	      objc_set_method_opt (true);
@@ -7574,132 +7574,178 @@  c_parser_objc_diagnose_bad_element_prefix (c_parse
   return false;
 }
 
-/* ObjC @property. */
+/* Parse an Objective-C @property declaration.  The syntax is:
 
-/* Parse a comma-separated list of property attributes.  */
+   objc-property-declaration:
+     '@property' objc-property-attributes[opt] struct-declaration ;
 
+   objc-property-attributes:
+    '(' objc-property-attribute-list ')'
+
+   objc-property-attribute-list:
+     objc-property-attribute
+     objc-property-attribute-list, objc-property-attribute
+
+   objc-property-attribute
+     'getter' = identifier
+     'setter' = identifier
+     'readonly'
+     'readwrite'
+     'assign'
+     'retain'
+     'copy'
+     'nonatomic'
+
+  For example:
+    @property NSString *name;
+    @property (readonly) id object;
+    @property (retain, nonatomic, getter=getTheName) id name;
+    @property int a, b, c;
+
+  PS: This function is identical to cp_parser_objc_at_propery_declaration
+  for C++.  Keep them in sync.
+
+  WORK IN PROGRESS: At the moment, the list of attributes that are
+  parsed is different from the above list.  It will be updated to use
+  the above list at the same time as @synthesize is implemented.  */
 static void
-c_parser_objc_property_attrlist (c_parser *parser)
+c_parser_objc_at_property_declaration (c_parser *parser)
 {
-  bool err = false;
-  /* Initialize to an empty list.  */
-  objc_set_property_attr (c_parser_peek_token (parser)->location,
-			  OBJC_PATTR_INIT, NULL_TREE);
+  tree properties;
+  location_t loc;
+  loc = c_parser_peek_token (parser)->location;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_PROPERTY));
 
-  if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
-    return;
+  c_parser_consume_token (parser);  /* Eat '@property'.  */
 
-  /* Eat the '(' */
-  c_parser_consume_token (parser);
-  
-  /* Property attribute keywords are valid now.  */
-  parser->objc_property_attr_context = true;
-  while (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN)
-	 && c_parser_next_token_is_not (parser, CPP_EOF)
-	 && !err)
+  /* Initialize attributes to an empty list.  */
+  objc_set_property_attr (loc, OBJC_PATTR_INIT, NULL_TREE);
+
+  /* Parse the optional attribute list...  */
+  if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
-      enum rid keywd;
-      location_t loc;
-      if (c_parser_peek_token (parser)->type != CPP_KEYWORD)
+      /* Eat the '(' */
+      c_parser_consume_token (parser);
+      
+      /* Property attribute keywords are valid now.  */
+      parser->objc_property_attr_context = true;
+
+      while (true)
 	{
-	  c_parser_error (parser, "expected a property attribute");
-	  c_parser_consume_token (parser);
-	  err = true;
-	  break;
-	}
-      keywd = c_parser_peek_token (parser)->keyword;
-      /* Initially, make diagnostics point to the attribute.  */
-      loc = c_parser_peek_token (parser)->location;
-      switch (keywd)
-	{
-	  tree ident;
-	  objc_property_attribute_kind pkind;
-	  case RID_READONLY:
-	    objc_set_property_attr (loc, OBJC_PATTR_READONLY, NULL_TREE);
+	  bool syntax_error = false;
+	  c_token *token = c_parser_peek_token (parser);
+	  enum rid keyword;
+
+	  if (token->type != CPP_KEYWORD)
+	    {
+	      if (token->type == CPP_CLOSE_PAREN)
+		c_parser_error (parser, "expected identifier");
+	      else
+		{
+		  c_parser_consume_token (parser);
+		  c_parser_error (parser, "unknown property attribute");
+		}
+	      break;
+	    }
+	  keyword = token->keyword;
+	  switch (keyword)
+	    {
+	      tree ident;
+	      objc_property_attribute_kind pkind;
+	    case RID_READONLY:
+	      c_parser_consume_token (parser);
+	      objc_set_property_attr (loc, OBJC_PATTR_READONLY, NULL_TREE);
+	      break;
+	    case RID_GETTER:
+	    case RID_SETTER:
+	    case RID_IVAR:
+	      c_parser_consume_token (parser);
+	      if (c_parser_next_token_is_not (parser, CPP_EQ))
+		{
+		  c_parser_error (parser,
+				  "getter/setter/ivar attribute must be followed by %<=%>");
+		  syntax_error = true;
+		  break;
+		}
+	      c_parser_consume_token (parser); /* eat the = */
+	      if (c_parser_next_token_is_not (parser, CPP_NAME))
+		{
+		  c_parser_error (parser, "expected identifier");
+		  syntax_error = true;
+		  break;
+		}
+	      ident = c_parser_peek_token (parser)->value;
+	      c_parser_consume_token (parser);
+	      if (keyword == RID_SETTER)
+		{
+		  pkind = OBJC_PATTR_SETTER;
+		  /* Eat the identifier, and look for the following : */
+		  if (c_parser_next_token_is_not (parser, CPP_COLON))
+		    {
+		      c_parser_error (parser,
+				      "setter name must be followed by %<:%>");
+		      syntax_error = true;
+		      break;
+		    }
+		  c_parser_consume_token (parser);
+		}
+	      else if (keyword == RID_GETTER)
+		pkind = OBJC_PATTR_GETTER;
+	      else
+		pkind = OBJC_PATTR_IVAR;
+	      objc_set_property_attr (loc, pkind, ident);
+	      break;
+	    case RID_COPIES:
+	      c_parser_consume_token (parser);
+	      objc_set_property_attr (loc, OBJC_PATTR_COPIES, NULL_TREE);
+	      break;
+	    default:
+	      if (token->type == CPP_CLOSE_PAREN)
+		c_parser_error (parser, "expected identifier");
+	      else
+		{
+		  c_parser_consume_token (parser);
+		  c_parser_error (parser, "unknown property attribute");
+		}
+	      syntax_error = true;
+	      break;
+	    }
+
+	  if (syntax_error)
 	    break;
-	  case RID_GETTER:
-	  case RID_SETTER:
-	  case RID_IVAR:
+	  
+	  if (c_parser_next_token_is (parser, CPP_COMMA))
 	    c_parser_consume_token (parser);
-	    if (c_parser_next_token_is_not (parser, CPP_EQ))
-	      {
-		c_parser_error (parser, 
-		  "getter/setter/ivar attribute must be followed by %<=%>");
-		err = true;
-		break;
-	      }
-	    c_parser_consume_token (parser); /* eat the = */
-	    if (c_parser_next_token_is_not (parser, CPP_NAME))
-	      {
-		c_parser_error (parser, "expected an identifier");
-		err = true;
-		break;
-	      }
-	    ident = c_parser_peek_token (parser)->value;
-	    if (keywd == RID_SETTER)
-	      {
-		pkind = OBJC_PATTR_SETTER;
-		/* Eat the identifier, and look for the following : */
-		c_parser_consume_token (parser);
-		if (c_parser_next_token_is_not (parser, CPP_COLON))
-		  {
-		    c_parser_error (parser,
-				"setter name must be followed by %<:%>");
-		    err = true;
-		  }
-	      }
-	    else if (keywd == RID_GETTER)
-	      pkind = OBJC_PATTR_GETTER;
-	    else
-	      pkind = OBJC_PATTR_IVAR;
-	    
-	    objc_set_property_attr (loc, pkind, ident);
+	  else
 	    break;
-	  case RID_COPIES:
-	    objc_set_property_attr (loc, OBJC_PATTR_COPIES, NULL_TREE);
-	    break;
-	  default:
-	    c_parser_error (parser, "unknown property attribute");
-	    err = true;
-	    break;
 	}
-      /* Eat the attribute,identifier or colon that's been used.  */
-      c_parser_consume_token (parser);
-      if (err)
-        break;
+      parser->objc_property_attr_context = false;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+    }
+  /* ... and the property declaration(s).  */
+  properties = c_parser_struct_declaration (parser);
 
-      if (c_parser_next_token_is (parser, CPP_COMMA))
-	c_parser_consume_token (parser);
-      else if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN))
-	warning_at (c_parser_peek_token (parser)->location, 0, 
-		    "property attributes should be separated by a %<,%>");
-    }  
-  parser->objc_property_attr_context = false;
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
-}
+  if (properties == error_mark_node)
+    {
+      c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL);
+      parser->error = false;
+      return;
+    }
 
-/* Parse property attributes and then the definition.  */
+  if (properties == NULL_TREE)
+    c_parser_error (parser, "expected identifier");
+  else
+    {
+      /* Comma-separated properties are chained together in
+	 reverse order; add them one by one.  */
+      properties = nreverse (properties);
+      
+      for (; properties; properties = TREE_CHAIN (properties))
+	objc_add_property_declaration (loc, copy_node (properties));
+    }
 
-static void
-c_parser_objc_at_property (c_parser *parser)
-{
-  tree props;
-  /* We should only arrive here with the property keyword.  */
-  c_parser_require_keyword (parser, RID_AT_PROPERTY, "expected %<@property%>");
-
-  /* Process the optional attribute list...  */
-  c_parser_objc_property_attrlist (parser) ;
-  /* ... and the property var decls.  */
-  props = c_parser_struct_declaration (parser);
-
-  /* Comma-separated properties are chained together in
-     reverse order; add them one by one.  */
-  props = nreverse (props);
-
-  for (; props; props = TREE_CHAIN (props))
-    objc_add_property_variable (copy_node (props));
-
   c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
+  parser->error = false;
 }
 
 /* Parse an Objective-C @synthesize declaration.  The syntax is: