diff --git a/gcc/c-family/c-objc.h b/gcc/c-family/c-objc.h
index 4577e4f1c7f570579a0063b6a32decd1f5a1ae0d..a2ca112dcc6cfb731cf61a74edd50f95d564f464 100644
--- a/gcc/c-family/c-objc.h
+++ b/gcc/c-family/c-objc.h
@@ -28,6 +28,67 @@ enum GTY(()) objc_ivar_visibility_kind {
   OBJC_IVAR_VIS_PACKAGE   = 3
 };
 
+/* ObjC property attribute kinds.
+   These have two fields; a unique value (that identifies which attribute)
+   and a group key that indicates membership of an exclusion group.
+   Only one member may be present from an exclusion group in a given attribute
+   list.
+   getters and setters have additional rules, since they are excluded from
+   non-overlapping group sets.  */
+
+enum objc_property_attribute_group
+{
+  OBJC_PROPATTR_GROUP_UNKNOWN = 0,
+  OBJC_PROPATTR_GROUP_GETTER,
+  OBJC_PROPATTR_GROUP_SETTER,
+  OBJC_PROPATTR_GROUP_READWRITE,
+  OBJC_PROPATTR_GROUP_ASSIGN,
+  OBJC_PROPATTR_GROUP_ATOMIC,
+  OBJC_PROPATTR_GROUP_MAX
+};
+
+enum objc_property_attribute_kind
+{
+  OBJC_PROPERTY_ATTR_UNKNOWN =		0|OBJC_PROPATTR_GROUP_UNKNOWN,
+  OBJC_PROPERTY_ATTR_GETTER =		( 1 << 8)|OBJC_PROPATTR_GROUP_GETTER,
+  OBJC_PROPERTY_ATTR_SETTER =		( 2 << 8)|OBJC_PROPATTR_GROUP_SETTER,
+  OBJC_PROPERTY_ATTR_READONLY =		( 3 << 8)|OBJC_PROPATTR_GROUP_READWRITE,
+  OBJC_PROPERTY_ATTR_READWRITE =	( 4 << 8)|OBJC_PROPATTR_GROUP_READWRITE,
+  OBJC_PROPERTY_ATTR_ASSIGN =		( 5 << 8)|OBJC_PROPATTR_GROUP_ASSIGN,
+  OBJC_PROPERTY_ATTR_RETAIN =		( 6 << 8)|OBJC_PROPATTR_GROUP_ASSIGN,
+  OBJC_PROPERTY_ATTR_COPY =		( 7 << 8)|OBJC_PROPATTR_GROUP_ASSIGN,
+  OBJC_PROPERTY_ATTR_ATOMIC =		( 8 << 8)|OBJC_PROPATTR_GROUP_ATOMIC,
+  OBJC_PROPERTY_ATTR_NONATOMIC =	( 9 << 8)|OBJC_PROPATTR_GROUP_ATOMIC,
+  OBJC_PROPERTY_ATTR_MAX =		(255 << 8|OBJC_PROPATTR_GROUP_MAX)
+};
+
+#define OBJC_PROPATTR_GROUP_MASK 0x0f
+
+/* To contain parsed, but unverified, information about a single property
+   attribute.  */
+struct property_attribute_info
+{
+  property_attribute_info () = default;
+  property_attribute_info (tree name, location_t loc,
+			   enum objc_property_attribute_kind k)
+   : name (name), ident (NULL_TREE), prop_loc (loc), prop_kind (k),
+     parse_error (false) {}
+
+  enum objc_property_attribute_group group ()
+    {
+      return (enum objc_property_attribute_group)
+	((unsigned)prop_kind & OBJC_PROPATTR_GROUP_MASK);
+    }
+
+  tree name; /* Name of the attribute.  */
+  tree ident; /* For getter/setter cases, the method/selector name.  */
+  location_t prop_loc; /* Extended location covering the parsed attr.  */
+  enum objc_property_attribute_kind prop_kind : 16;
+  unsigned parse_error : 1; /* The C/C++ parser saw an error in this attr.  */
+};
+
+extern enum objc_property_attribute_kind objc_prop_attr_kind_for_rid (enum rid);
+
 /* Objective-C / Objective-C++ entry points.  */
 
 /* The following ObjC/ObjC++ functions are called by the C and/or C++
@@ -90,8 +151,8 @@ extern tree objc_generate_write_barrier (tree, enum tree_code, tree);
 extern void objc_set_method_opt (bool);
 extern void objc_finish_foreach_loop (location_t, tree, tree, tree, tree, tree);
 extern bool  objc_method_decl (enum tree_code);
-extern void objc_add_property_declaration (location_t, tree, bool, bool, bool, 
-					   bool, bool, bool, tree, tree);
+extern void objc_add_property_declaration (location_t, tree,
+					   vec<property_attribute_info *>&);
 extern tree objc_maybe_build_component_ref (tree, tree);
 extern tree objc_build_class_component_ref (tree, tree);
 extern tree objc_maybe_build_modify_expr (tree, tree);
diff --git a/gcc/c-family/stub-objc.c b/gcc/c-family/stub-objc.c
index d017acfd6fe857ca5c2b491c4eef8672bf2947a3..2f535578789712c987d457cbdb319f8c9d3d8aef 100644
--- a/gcc/c-family/stub-objc.c
+++ b/gcc/c-family/stub-objc.c
@@ -23,6 +23,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tree.h"
+#include "vec.h"
+
+/* Provide a dummy type for the RID enum used as an argument to
+   objc_prop_attr_kind_for_rid () */
+enum rid { DUMMY };
+
 #include "c-objc.h"
 
 tree
@@ -314,14 +320,8 @@ objc_get_class_ivars (tree ARG_UNUSED (name))
 void
 objc_add_property_declaration (location_t ARG_UNUSED (location), 
 			       tree ARG_UNUSED (decl),
-			       bool ARG_UNUSED (parsed_property_readonly),
-			       bool ARG_UNUSED (parsed_property_readwrite),
-			       bool ARG_UNUSED (parsed_property_assign),
-			       bool ARG_UNUSED (parsed_property_retain),
-			       bool ARG_UNUSED (parsed_property_copy),
-			       bool ARG_UNUSED (parsed_property_nonatomic),
-			       tree ARG_UNUSED (parsed_property_getter_ident),
-			       tree ARG_UNUSED (parsed_property_setter_ident))
+			       vec<property_attribute_info *>&
+			       /*prop_attr_list*/)
 {
 }
 
@@ -465,3 +465,8 @@ void
 objc_maybe_warn_exceptions (location_t ARG_UNUSED (loc))
 {
 }
+
+enum objc_property_attribute_kind objc_prop_attr_kind_for_rid (enum rid)
+{
+  return OBJC_PROPERTY_ATTR_UNKNOWN;
+}
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index dedfb8472d0ebbf83f9121d1074d3d66033d56e9..da8278a8257c4ffc8be3d9d2ba300abc87d06f08 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -11958,158 +11958,196 @@ c_parser_objc_diagnose_bad_element_prefix (c_parser *parser,
 static void
 c_parser_objc_at_property_declaration (c_parser *parser)
 {
-  /* The following variables hold the attributes of the properties as
-     parsed.  They are 'false' or 'NULL_TREE' if the attribute was not
-     seen.  When we see an attribute, we set them to 'true' (if they
-     are boolean properties) or to the identifier (if they have an
-     argument, ie, for getter and setter).  Note that here we only
-     parse the list of attributes, check the syntax and accumulate the
-     attributes that we find.  objc_add_property_declaration() will
-     then process the information.  */
-  bool property_assign = false;
-  bool property_copy = false;
-  tree property_getter_ident = NULL_TREE;
-  bool property_nonatomic = false;
-  bool property_readonly = false;
-  bool property_readwrite = false;
-  bool property_retain = false;
-  tree property_setter_ident = NULL_TREE;
-
-  /* 'properties' is the list of properties that we read.  Usually a
-     single one, but maybe more (eg, in "@property int a, b, c;" there
-     are three).  */
-  tree properties;
-  location_t loc;
-
-  loc = c_parser_peek_token (parser)->location;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_PROPERTY));
-
+  location_t loc = c_parser_peek_token (parser)->location;
   c_parser_consume_token (parser);  /* Eat '@property'.  */
 
-  /* Parse the optional attribute list...  */
+  /* Parse the optional attribute list.
+
+     A list of parsed, but not verified, attributes.  */
+  vec<property_attribute_info *> prop_attr_list = vNULL;
+
+  bool syntax_error = false;
   if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
       matching_parens parens;
 
+      location_t attr_start = c_parser_peek_token (parser)->location;
       /* Eat the '(' */
       parens.consume_open (parser);
 
       /* Property attribute keywords are valid now.  */
       parser->objc_property_attr_context = true;
 
-      while (true)
+      /* Allow @property (), with a warning.  */
+      location_t attr_end = c_parser_peek_token (parser)->location;
+
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
 	{
-	  bool syntax_error = false;
-	  c_token *token = c_parser_peek_token (parser);
-	  enum rid keyword;
+	  location_t attr_comb = make_location (attr_end, attr_start, attr_end);
+	  warning_at (attr_comb, OPT_Wattributes,
+		      "empty property attribute list");
+	}
+      else
+	while (true)
+	  {
+	    c_token *token = c_parser_peek_token (parser);
+	    attr_start = token->location;
+	    attr_end = get_finish (token->location);
+	    location_t attr_comb = make_location (attr_start, attr_start,
+						  attr_end);
 
-	  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;
-	  c_parser_consume_token (parser);
-	  switch (keyword)
-	    {
-	    case RID_ASSIGN:    property_assign = true;    break;
-	    case RID_COPY:      property_copy = true;      break;
-	    case RID_NONATOMIC: property_nonatomic = true; break;
-	    case RID_READONLY:  property_readonly = true;  break;
-	    case RID_READWRITE: property_readwrite = true; break;
-	    case RID_RETAIN:    property_retain = true;    break;
-
-	    case RID_GETTER:
-	    case RID_SETTER:
-	      if (c_parser_next_token_is_not (parser, CPP_EQ))
-		{
-		  if (keyword == RID_GETTER)
-		    c_parser_error (parser,
-				    "missing %<=%> (after %<getter%> attribute)");
-		  else
-		    c_parser_error (parser,
-				    "missing %<=%> (after %<setter%> attribute)");
-		  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;
+	    if (token->type == CPP_CLOSE_PAREN || token->type == CPP_COMMA)
+	      {
+		warning_at (attr_comb, OPT_Wattributes,
+			    "missing property attribute");
+		if (token->type == CPP_CLOSE_PAREN)
 		  break;
-		}
-	      if (keyword == RID_SETTER)
-		{
-		  if (property_setter_ident != NULL_TREE)
-		    c_parser_error (parser, "the %<setter%> attribute may only be specified once");
-		  else
-		    property_setter_ident = c_parser_peek_token (parser)->value;
-		  c_parser_consume_token (parser);
-		  if (c_parser_next_token_is_not (parser, CPP_COLON))
-		    c_parser_error (parser, "setter name must terminate with %<:%>");
-		  else
-		    c_parser_consume_token (parser);
-		}
-	      else
-		{
-		  if (property_getter_ident != NULL_TREE)
-		    c_parser_error (parser, "the %<getter%> attribute may only be specified once");
-		  else
-		    property_getter_ident = c_parser_peek_token (parser)->value;
-		  c_parser_consume_token (parser);
-		}
-	      break;
-	    default:
-	      c_parser_error (parser, "unknown property attribute");
-	      syntax_error = true;
-	      break;
+		c_parser_consume_token (parser);
+		continue;
+	      }
+
+	    tree attr_name = NULL_TREE;
+	    enum rid keyword = RID_MAX; /* Not a valid property attribute.  */
+	    bool add_at = false;
+	    if (token->type == CPP_KEYWORD)
+	      {
+		keyword = token->keyword;
+		if (OBJC_IS_AT_KEYWORD (keyword))
+		  {
+		    /* For '@' keywords the token value has the keyword,
+		       prepend the '@' for diagnostics.  */
+		    attr_name = token->value;
+		    add_at = true;
+		  }
+		else
+		  attr_name = ridpointers[(int)keyword];
+	      }
+	    else if (token->type == CPP_NAME)
+	      attr_name = token->value;
+	    c_parser_consume_token (parser);
+
+	    enum objc_property_attribute_kind prop_kind
+	      = objc_prop_attr_kind_for_rid (keyword);
+	    property_attribute_info *prop
+	      = new property_attribute_info (attr_name, attr_comb, prop_kind);
+	    prop_attr_list.safe_push (prop);
+
+	    tree meth_name;
+	    switch (prop->prop_kind)
+	      {
+	      default: break;
+	      case OBJC_PROPERTY_ATTR_UNKNOWN:
+		if (attr_name)
+		  error_at (attr_comb, "unknown property attribute %<%s%s%>",
+			    add_at ? "@" : "", IDENTIFIER_POINTER (attr_name));
+		else
+		  error_at (attr_comb, "unknown property attribute");
+		prop->parse_error = syntax_error = true;
+		break;
+
+	      case OBJC_PROPERTY_ATTR_GETTER:
+	      case OBJC_PROPERTY_ATTR_SETTER:
+		if (c_parser_next_token_is_not (parser, CPP_EQ))
+		  {
+		    attr_comb = make_location (attr_end, attr_start, attr_end);
+		    error_at (attr_comb, "expected %<=%> after Objective-C %qE",
+			      attr_name);
+		    prop->parse_error = syntax_error = true;
+		    break;
+		  }
+		token = c_parser_peek_token (parser);
+		attr_end = token->location;
+		c_parser_consume_token (parser); /* eat the = */
+		if (c_parser_next_token_is_not (parser, CPP_NAME))
+		  {
+		    attr_comb = make_location (attr_end, attr_start, attr_end);
+		    error_at (attr_comb, "expected %qE selector name",
+			      attr_name);
+		    prop->parse_error = syntax_error = true;
+		    break;
+		  }
+		/* Get the end of the method name, and consume the name.  */
+		token = c_parser_peek_token (parser);
+		attr_end = get_finish (token->location);
+		meth_name = token->value;
+		c_parser_consume_token (parser);
+		if (prop->prop_kind == OBJC_PROPERTY_ATTR_SETTER)
+		  {
+		    if (c_parser_next_token_is_not (parser, CPP_COLON))
+		      {
+			attr_comb = make_location (attr_end, attr_start,
+						   attr_end);
+			error_at (attr_comb, "setter method names must"
+				  " terminate with %<:%>");
+			prop->parse_error = syntax_error = true;
+		      }
+		    else
+		      {
+			attr_end = get_finish (c_parser_peek_token
+					       (parser)->location);
+			c_parser_consume_token (parser);
+		      }
+		    attr_comb = make_location (attr_start, attr_start,
+					       attr_end);
+		  }
+		else
+		  attr_comb = make_location (attr_start, attr_start,
+					       attr_end);
+		prop->ident = meth_name;
+		/* Updated location including all that was successfully
+		   parsed.  */
+		prop->prop_loc = attr_comb;
+		break;
 	    }
 
-	  if (syntax_error)
-	    break;
-	  
+	  /* If we see a comma here, then keep going - even if we already
+	     saw a syntax error.  For simple mistakes e.g. (asign, getter=x)
+	     this makes a more useful output and avoid spurious warnings about
+	     missing attributes that are, in fact, specified after the one with
+	     the syntax error.  */
 	  if (c_parser_next_token_is (parser, CPP_COMMA))
 	    c_parser_consume_token (parser);
 	  else
 	    break;
 	}
       parser->objc_property_attr_context = false;
-      parens.skip_until_found_close (parser);
-    }
-  /* ... and the property declaration(s).  */
-  properties = c_parser_struct_declaration (parser);
 
-  if (properties == error_mark_node)
-    {
-      c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL);
-      parser->error = false;
-      return;
+      if (syntax_error && c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN))
+	/* We don't really want to chew the whole of the file looking for a
+	   matching closing parenthesis, so we will try to read the decl and
+	   let the error handling for that close out the statement.  */
+	;
+      else
+	syntax_error = false, parens.skip_until_found_close (parser);
     }
 
-  if (properties == NULL_TREE)
-    c_parser_error (parser, "expected identifier");
+  /* 'properties' is the list of properties that we read.  Usually a
+     single one, but maybe more (eg, in "@property int a, b, c;" there
+     are three).  */
+  tree properties = c_parser_struct_declaration (parser);
+
+  if (properties == error_mark_node)
+    c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL);
   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),
-				       property_readonly, property_readwrite,
-				       property_assign, property_retain,
-				       property_copy, property_nonatomic,
-				       property_getter_ident, property_setter_ident);
+      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),
+					    prop_attr_list);
+	}
+      c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
     }
 
-  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
+  while (!prop_attr_list.is_empty())
+    delete prop_attr_list.pop ();
+  prop_attr_list.release ();
   parser->error = false;
 }
 
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 6e7b982f073d1ae55636c6ac4be140b81e367ed4..c4c672efa09584395ee39415f8992f5e86b80d5c 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -34001,30 +34001,11 @@ cp_parser_objc_struct_declaration (cp_parser *parser)
 static void
 cp_parser_objc_at_property_declaration (cp_parser *parser)
 {
-  /* The following variables hold the attributes of the properties as
-     parsed.  They are 'false' or 'NULL_TREE' if the attribute was not
-     seen.  When we see an attribute, we set them to 'true' (if they
-     are boolean properties) or to the identifier (if they have an
-     argument, ie, for getter and setter).  Note that here we only
-     parse the list of attributes, check the syntax and accumulate the
-     attributes that we find.  objc_add_property_declaration() will
-     then process the information.  */
-  bool property_assign = false;
-  bool property_copy = false;
-  tree property_getter_ident = NULL_TREE;
-  bool property_nonatomic = false;
-  bool property_readonly = false;
-  bool property_readwrite = false;
-  bool property_retain = false;
-  tree property_setter_ident = NULL_TREE;
+  /* Parse the optional attribute list.
 
-  /* 'properties' is the list of properties that we read.  Usually a
-     single one, but maybe more (eg, in "@property int a, b, c;" there
-     are three).  */
-  tree properties;
-  location_t loc;
-
-  loc = cp_lexer_peek_token (parser->lexer)->location;
+     A list of parsed, but not verified, attributes.  */
+  vec<property_attribute_info *> prop_attr_list = vNULL;
+  location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@property'.  */
 
@@ -34033,127 +34014,172 @@ cp_parser_objc_at_property_declaration (cp_parser *parser)
     {
       /* Eat the '('.  */
       matching_parens parens;
+      location_t attr_start = cp_lexer_peek_token (parser->lexer)->location;
       parens.consume_open (parser);
       bool syntax_error = false;
 
-      while (true)
+      /* Allow empty @property attribute lists, but with a warning.  */
+      location_t attr_end = cp_lexer_peek_token (parser->lexer)->location;
+      location_t attr_comb;
+      if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
 	{
-	  cp_token *token = cp_lexer_peek_token (parser->lexer);
-      	  enum rid keyword;
+	  attr_comb = make_location (attr_end, attr_start, attr_end);
+	  warning_at (attr_comb, OPT_Wattributes,
+		      "empty property attribute list");
+	}
+      else
+	while (true)
+	  {
+	    cp_token *token = cp_lexer_peek_token (parser->lexer);
+	    attr_start = token->location;
+	    attr_end = get_finish (token->location);
+	    attr_comb = make_location (attr_start, attr_start, attr_end);
 
-	  if (token->type != CPP_NAME)
-	    {
-	      cp_parser_error (parser, "expected identifier");
-	      syntax_error = true;
-	      break;
-	    }
-	  keyword = C_RID_CODE (token->u.value);
-	  cp_lexer_consume_token (parser->lexer);
-	  switch (keyword)
-	    {
-	    case RID_ASSIGN:    property_assign = true;    break;
-	    case RID_COPY:      property_copy = true;      break;
-	    case RID_NONATOMIC: property_nonatomic = true; break;
-	    case RID_READONLY:  property_readonly = true;  break;
-	    case RID_READWRITE: property_readwrite = true; break;
-	    case RID_RETAIN:    property_retain = true;    break;
-
-	    case RID_GETTER:
-	    case RID_SETTER:
-	      if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ))
-		{
-		  if (keyword == RID_GETTER)
-		    cp_parser_error (parser,
-				     "missing %<=%> (after %<getter%> attribute)");
-		  else
-		    cp_parser_error (parser,
-				     "missing %<=%> (after %<setter%> attribute)");
-		  syntax_error = true;
-		  break;
-		}
-	      cp_lexer_consume_token (parser->lexer); /* eat the = */
-	      if (!cp_parser_objc_selector_p (cp_lexer_peek_token (parser->lexer)->type))
-		{
-		  cp_parser_error (parser, "expected identifier");
-		  syntax_error = true;
+	    if (token->type == CPP_CLOSE_PAREN || token->type == CPP_COMMA)
+	      {
+		warning_at (attr_comb, OPT_Wattributes,
+			    "missing property attribute");
+		if (token->type == CPP_CLOSE_PAREN)
 		  break;
-		}
-	      if (keyword == RID_SETTER)
-		{
-		  if (property_setter_ident != NULL_TREE)
-		    {
-		      cp_parser_error (parser, "the %<setter%> attribute may only be specified once");
-		      cp_lexer_consume_token (parser->lexer);
-		    }
-		  else
-		    property_setter_ident = cp_parser_objc_selector (parser);
-		  if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
-		    cp_parser_error (parser, "setter name must terminate with %<:%>");
-		  else
-		    cp_lexer_consume_token (parser->lexer);
-		}
-	      else
-		{
-		  if (property_getter_ident != NULL_TREE)
-		    {
-		      cp_parser_error (parser, "the %<getter%> attribute may only be specified once");
-		      cp_lexer_consume_token (parser->lexer);
-		    }
-		  else
-		    property_getter_ident = cp_parser_objc_selector (parser);
-		}
-	      break;
-	    default:
-	      cp_parser_error (parser, "unknown property attribute");
-	      syntax_error = true;
-	      break;
-	    }
+		cp_lexer_consume_token (parser->lexer);
+		continue;
+	      }
 
-	  if (syntax_error)
-	    break;
+	    tree attr_name = NULL_TREE;
+	    if (identifier_p (token->u.value))
+	      attr_name = token->u.value;
 
-	  if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    enum rid keyword;
+	    if (token->type == CPP_NAME)
+	      keyword = C_RID_CODE (token->u.value);
+	    else
+	      keyword = RID_MAX; /* By definition, an unknown property.  */
 	    cp_lexer_consume_token (parser->lexer);
-	  else
-	    break;
-	}
+
+	    enum objc_property_attribute_kind prop_kind
+	      = objc_prop_attr_kind_for_rid (keyword);
+	    property_attribute_info *prop
+	      = new property_attribute_info (attr_name, attr_comb, prop_kind);
+	    prop_attr_list.safe_push (prop);
+
+	    tree meth_name;
+	    switch (prop->prop_kind)
+	      {
+	      default: break;
+	      case OBJC_PROPERTY_ATTR_UNKNOWN:
+		if (attr_name)
+		  error_at (attr_start, "unknown property attribute %qE",
+			    attr_name);
+		else
+		  error_at (attr_start, "unknown property attribute");
+		prop->parse_error = syntax_error = true;
+		break;
+
+	      case OBJC_PROPERTY_ATTR_GETTER:
+	      case OBJC_PROPERTY_ATTR_SETTER:
+		if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ))
+		  {
+		    attr_comb = make_location (attr_end, attr_start, attr_end);
+		    error_at (attr_comb, "expected %<=%> after Objective-C %qE",
+			      attr_name);
+		    prop->parse_error = syntax_error = true;
+		    break;
+		  }
+
+		token = cp_lexer_peek_token (parser->lexer);
+		attr_end = token->location;
+		cp_lexer_consume_token (parser->lexer); /* eat the = */
+
+		if (!cp_parser_objc_selector_p
+		     (cp_lexer_peek_token (parser->lexer)->type))
+		  {
+		    attr_comb = make_location (attr_end, attr_start, attr_end);
+		    error_at (attr_comb, "expected %qE selector name",
+			      attr_name);
+		    prop->parse_error = syntax_error = true;
+		    break;
+		  }
+
+		/* Get the end of the method name, and consume the name.  */
+		token = cp_lexer_peek_token (parser->lexer);
+		attr_end = get_finish (token->location);
+		/* Because method names may contain C++ keywords, we have a
+		   routine to fetch them (this also consumes the token).  */
+		meth_name = cp_parser_objc_selector (parser);
+
+		if (prop->prop_kind == OBJC_PROPERTY_ATTR_SETTER)
+		  {
+		    if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
+		      {
+			attr_comb = make_location (attr_end, attr_start,
+						   attr_end);
+			error_at (attr_comb, "setter method names must"
+				  " terminate with %<:%>");
+			prop->parse_error = syntax_error = true;
+		      }
+		    else
+		      {
+			attr_end = get_finish (cp_lexer_peek_token
+					       (parser->lexer)->location);
+			cp_lexer_consume_token (parser->lexer);
+		      }
+		    attr_comb = make_location (attr_start, attr_start,
+					       attr_end);
+		  }
+		else
+		  attr_comb = make_location (attr_start, attr_start,
+					     attr_end);
+		prop->ident = meth_name;
+		/* Updated location including all that was successfully
+		   parsed.  */
+		prop->prop_loc = attr_comb;
+		break;
+	      }
+
+	    /* If we see a comma here, then keep going - even if we already
+	       saw a syntax error.  For simple mistakes e.g. (asign, getter=x)
+	       this makes a more useful output and avoid spurious warnings
+	       about missing attributes that are, in fact, specified after the
+	       one with the syntax error.  */
+	    if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	      cp_lexer_consume_token (parser->lexer);
+	    else
+	      break;
+	  }
 
       if (syntax_error || !parens.require_close (parser))
-	cp_parser_skip_to_closing_parenthesis (parser,
-						 /*recovering=*/true,
-						 /*or_comma=*/false,
-						 /*consume_paren=*/true);
+	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
+					       /*or_comma=*/false,
+					       /*consume_paren=*/true);
     }
 
-  /* ... and the property declaration(s).  */
-  properties = cp_parser_objc_struct_declaration (parser);
+  /* 'properties' is the list of properties that we read.  Usually a
+     single one, but maybe more (eg, in "@property int a, b, c;" there
+     are three).
+     TODO: Update this parsing so that it accepts (erroneous) bitfields so
+     that we can issue a meaningful and consistent (between C/C++) error
+     message from objc_add_property_declaration ().  */
+  tree 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);
-      return;
-    }
-
-  if (properties == NULL_TREE)
+    cp_parser_skip_to_end_of_statement (parser);
+  else if (properties == NULL_TREE)
     cp_parser_error (parser, "expected identifier");
   else
     {
-      /* Comma-separated properties are chained together in
-	 reverse order; add them one by one.  */
+      /* 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),
-				       property_readonly, property_readwrite,
-				       property_assign, property_retain,
-				       property_copy, property_nonatomic,
-				       property_getter_ident, property_setter_ident);
+				       prop_attr_list);
     }
 
   cp_parser_consume_semicolon_at_end_of_statement (parser);
+
+  while (!prop_attr_list.is_empty())
+    delete prop_attr_list.pop ();
+  prop_attr_list.release ();
 }
 
 /* Parse an Objective-C++ @synthesize declaration.  The syntax is:
diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c
index c0d07ae9182ad8fa903155af43841971a69aec75..26cdeddfc5af63bf961618b6a5abcd0ae036c0a9 100644
--- a/gcc/objc/objc-act.c
+++ b/gcc/objc/objc-act.c
@@ -804,119 +804,74 @@ lookup_property (tree interface_type, tree property)
   return inter;
 }
 
+/* This routine returns a PROPERTY_KIND for the front end RID code supplied.  */
+
+enum objc_property_attribute_kind
+objc_prop_attr_kind_for_rid (enum rid prop_rid)
+{
+  switch (prop_rid)
+    {
+      default:			return OBJC_PROPERTY_ATTR_UNKNOWN;
+      case RID_GETTER:		return OBJC_PROPERTY_ATTR_GETTER;
+      case RID_SETTER:		return OBJC_PROPERTY_ATTR_SETTER;
+
+      case RID_READONLY:	return OBJC_PROPERTY_ATTR_READONLY;
+      case RID_READWRITE:	return OBJC_PROPERTY_ATTR_READWRITE;
+
+      case RID_ASSIGN:		return OBJC_PROPERTY_ATTR_ASSIGN;
+      case RID_RETAIN:		return OBJC_PROPERTY_ATTR_RETAIN;
+      case RID_COPY:		return OBJC_PROPERTY_ATTR_COPY;
+
+      case RID_NONATOMIC:	return OBJC_PROPERTY_ATTR_NONATOMIC;
+
+    }
+}
+
 /* This routine is called by the parser when a
    @property... declaration is found.  'decl' is the declaration of
    the property (type/identifier), and the other arguments represent
    property attributes that may have been specified in the Objective-C
    declaration.  'parsed_property_readonly' is 'true' if the attribute
    'readonly' was specified, and 'false' if not; similarly for the
-   other bool parameters.  'parsed_property_getter_ident' is NULL_TREE
+   other bool parameters.  'property_getter_ident' is NULL_TREE
    if the attribute 'getter' was not specified, and is the identifier
    corresponding to the specified getter if it was; similarly for
-   'parsed_property_setter_ident'.  */
+   'property_setter_ident'.  */
 void
 objc_add_property_declaration (location_t location, tree decl,
-			       bool parsed_property_readonly, bool parsed_property_readwrite,
-			       bool parsed_property_assign, bool parsed_property_retain,
-			       bool parsed_property_copy, bool parsed_property_nonatomic,
-			       tree parsed_property_getter_ident, tree parsed_property_setter_ident)
+			       vec<property_attribute_info *>& prop_attr_list)
 {
-  tree property_decl;
-  tree x;
-  /* 'property_readonly' and 'property_assign_semantics' are the final
-     attributes of the property after all parsed attributes have been
-     considered (eg, if we parsed no 'readonly' and no 'readwrite', ie
-     parsed_property_readonly = false and parsed_property_readwrite =
-     false, then property_readonly will be false because the default
-     is readwrite).  */
-  bool property_readonly = false;
-  objc_property_assign_semantics property_assign_semantics = OBJC_PROPERTY_ASSIGN;
-  bool property_extension_in_class_extension = false;
-
   if (flag_objc1_only)
-    error_at (input_location, "%<@property%> is not available in Objective-C 1.0");
-
-  if (parsed_property_readonly && parsed_property_readwrite)
-    {
-      error_at (location, "%<readonly%> attribute conflicts with %<readwrite%> attribute");
-      /* In case of conflicting attributes (here and below), after
-	 producing an error, we pick one of the attributes and keep
-	 going.  */
-      property_readonly = false;
-    }
-  else
-    {
-      if (parsed_property_readonly)
-	property_readonly = true;
-
-      if (parsed_property_readwrite)
-	property_readonly = false;
-    }
-
-  if (parsed_property_readonly && parsed_property_setter_ident)
-    {
-      error_at (location, "%<readonly%> attribute conflicts with %<setter%> attribute");
-      property_readonly = false;
-    }
-
-  if (parsed_property_assign && parsed_property_retain)
-    {
-      error_at (location, "%<assign%> attribute conflicts with %<retain%> attribute");
-      property_assign_semantics = OBJC_PROPERTY_RETAIN;
-    }
-  else if (parsed_property_assign && parsed_property_copy)
-    {
-      error_at (location, "%<assign%> attribute conflicts with %<copy%> attribute");
-      property_assign_semantics = OBJC_PROPERTY_COPY;
-    }
-  else if (parsed_property_retain && parsed_property_copy)
-    {
-      error_at (location, "%<retain%> attribute conflicts with %<copy%> attribute");
-      property_assign_semantics = OBJC_PROPERTY_COPY;
-    }
-  else
-    {
-      if (parsed_property_assign)
-	property_assign_semantics = OBJC_PROPERTY_ASSIGN;
-
-      if (parsed_property_retain)
-	property_assign_semantics = OBJC_PROPERTY_RETAIN;
-
-      if (parsed_property_copy)
-	property_assign_semantics = OBJC_PROPERTY_COPY;
-    }
+    /* FIXME: we probably ought to bail out at this point.  */
+    error_at (location, "%<@property%> is not available in Objective-C 1.0");
 
+  /* We must be in an interface, category, or protocol.  */
   if (!objc_interface_context)
     {
-      error_at (location, "property declaration not in @interface or @protocol context");
+      error_at (location, "property declaration not in %<@interface%>,"
+			  " %<@protocol%> or %<category%> context");
       return;
     }
 
-  /* At this point we know that we are either in an interface, a
-     category, or a protocol.  */
+  /* Do some spot-checks for the most obvious invalid cases.  */
+
+  gcc_checking_assert (decl && TREE_CODE (decl) == FIELD_DECL);
 
-  /* We expect a FIELD_DECL from the parser.  Make sure we didn't get
-     something else, as that would confuse the checks below.  */
-  if (TREE_CODE (decl) != FIELD_DECL)
+  if (decl && !DECL_NAME (decl))
     {
-      error_at (location, "invalid property declaration");
+      error_at (location, "properties must be named");
       return;
     }
 
-  /* Do some spot-checks for the most obvious invalid types.  */
-
+  location_t decl_loc = DECL_SOURCE_LOCATION (decl);
+  decl_loc = make_location (decl_loc, location, decl_loc);
   if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
     {
-      error_at (location, "property cannot be an array");
+      error_at (decl_loc, "property cannot be an array");
       return;
     }
 
-  /* The C++/ObjC++ parser seems to reject the ':' for a bitfield when
-     parsing, while the C/ObjC parser accepts it and gives us a
-     FIELD_DECL with a DECL_INITIAL set.  So we use the DECL_INITIAL
-     to check for a bitfield when doing ObjC.  */
-#ifndef OBJCPLUS
-  if (DECL_INITIAL (decl))
+  if (DECL_C_BIT_FIELD (decl))
     {
       /* A @property is not an actual variable, but it is a way to
 	 describe a pair of accessor methods, so its type (which is
@@ -925,10 +880,110 @@ objc_add_property_declaration (location_t location, tree decl,
 	 and arguments of functions cannot be bitfields).  The
 	 underlying instance variable could be a bitfield, but that is
 	 a different matter.  */
-      error_at (location, "property cannot be a bit-field");
+      error_at (decl_loc, "property cannot be a bit-field");
       return;
     }
-#endif
+
+  /* The final results of parsing the (growing number) of property
+     attributes.  */
+  property_attribute_info *attrs[OBJC_PROPATTR_GROUP_MAX] = { nullptr };
+
+  tree property_getter_ident = NULL_TREE;
+  tree property_setter_ident = NULL_TREE;
+  for (unsigned pn = 0; pn < prop_attr_list.length (); ++pn)
+    {
+      if (prop_attr_list[pn]->parse_error)
+	continue; /* Ignore attributes known to be wrongly parsed.  */
+
+      switch (int g = (int) prop_attr_list[pn]->group())
+	{
+	case OBJC_PROPATTR_GROUP_UNKNOWN:
+	  continue;
+	case OBJC_PROPATTR_GROUP_SETTER:
+	case OBJC_PROPATTR_GROUP_GETTER:
+	  if (attrs[g])
+	    {
+	      warning_at (prop_attr_list[pn]->prop_loc, OPT_Wattributes,
+			  "multiple property %qE methods specified, the latest"
+			  " one will be used", attrs[g]->name);
+	      inform (attrs[g]->prop_loc, "previous specification");
+	    }
+	  attrs[g] = prop_attr_list[pn];
+	  if (g == OBJC_PROPATTR_GROUP_SETTER)
+	    property_setter_ident = attrs[g]->ident;
+	  else
+	    property_getter_ident = attrs[g]->ident;
+	  continue;
+	default:
+	  {
+	    if (!attrs[g])
+	      ;
+	    else if (attrs[g]->prop_kind != prop_attr_list[pn]->prop_kind)
+	      {
+		error_at (prop_attr_list[pn]->prop_loc,
+			  "%qE attribute conflicts with %qE attribute",
+			  prop_attr_list[pn]->name, attrs[g]->name);
+		inform (attrs[g]->prop_loc, "%qE specified here",
+			attrs[g]->name );
+	      }
+	    else
+	      {
+		warning_at (prop_attr_list[pn]->prop_loc, OPT_Wattributes,
+			    "duplicate %qE attribute", attrs[g]->name);
+		inform (attrs[g]->prop_loc, "first specified here");
+	      }
+	    attrs[g] = prop_attr_list[pn];
+	  }
+	  continue;
+	}
+    }
+
+  /* The defaults for atomicity (atomic) and write-ability (readwrite) apply
+     even if the user provides no specified attributes.  */
+  bool property_nonatomic = false;
+  bool property_readonly = false;
+
+  /* Set the values from any specified by the user; these are easy, only two
+     states.  */
+  if (attrs[OBJC_PROPATTR_GROUP_ATOMIC])
+    property_nonatomic = attrs[OBJC_PROPATTR_GROUP_ATOMIC]->prop_kind
+			 == OBJC_PROPERTY_ATTR_NONATOMIC;
+
+  if (attrs[OBJC_PROPATTR_GROUP_READWRITE])
+    property_readonly = attrs[OBJC_PROPATTR_GROUP_READWRITE]->prop_kind
+			 == OBJC_PROPERTY_ATTR_READONLY;
+
+  /* One can't set a readonly value; we issue an error, but force the property
+     to readwrite as well.  */
+  if (property_readonly && property_setter_ident)
+    {
+      error_at (attrs[OBJC_PROPATTR_GROUP_READWRITE]->prop_loc, "%<readonly%>"
+		" attribute conflicts with %<setter%> attribute");
+      gcc_checking_assert (attrs[OBJC_PROPATTR_GROUP_SETTER]);
+      inform (attrs[OBJC_PROPATTR_GROUP_SETTER]->prop_loc, "%<setter%>"
+	      " specified here");
+      property_readonly = false;
+    }
+
+  /* Assign semantics is a tri-state property, and also needs some further
+     checking against the object type.  */
+  objc_property_assign_semantics property_assign_semantics
+    = OBJC_PROPERTY_ASSIGN;
+
+  if (attrs[OBJC_PROPATTR_GROUP_ASSIGN])
+    {
+      if (attrs[OBJC_PROPATTR_GROUP_ASSIGN]->prop_kind
+	  == OBJC_PROPERTY_ATTR_ASSIGN)
+	property_assign_semantics = OBJC_PROPERTY_ASSIGN;
+      else if (attrs[OBJC_PROPATTR_GROUP_ASSIGN]->prop_kind
+	       == OBJC_PROPERTY_ATTR_RETAIN)
+	property_assign_semantics = OBJC_PROPERTY_RETAIN;
+      else if (attrs[OBJC_PROPATTR_GROUP_ASSIGN]->prop_kind
+	       == OBJC_PROPERTY_ATTR_COPY)
+	property_assign_semantics = OBJC_PROPERTY_COPY;
+      else
+	gcc_unreachable ();
+    }
 
   /* TODO: Check that the property type is an Objective-C object or a
      "POD".  */
@@ -950,69 +1005,77 @@ objc_add_property_declaration (location_t location, tree decl,
 	 for non-{Objective-C objects}, and to 'retain' for
 	 Objective-C objects.  But that would break compatibility with
 	 other compilers.  */
-      if (!parsed_property_assign && !parsed_property_retain && !parsed_property_copy)
+      if (!attrs[OBJC_PROPATTR_GROUP_ASSIGN])
 	{
 	  /* Use 'false' so we do not warn for Class objects.  */
 	  if (objc_type_valid_for_messaging (TREE_TYPE (decl), false))
 	    {
-	      warning_at (location,
-			  0,
-			  "object property %qD has no %<assign%>, %<retain%> or %<copy%> attribute; assuming %<assign%>",
-			  decl);
-	      inform (location,
-		      "%<assign%> can be unsafe for Objective-C objects; please state explicitly if you need it");
+	      warning_at (decl_loc, 0, "object property %qD has no %<assign%>,"
+			  " %<retain%> or %<copy%> attribute; assuming"
+			  " %<assign%>", decl);
+	      inform (decl_loc, "%<assign%> can be unsafe for Objective-C"
+		      " objects; please state explicitly if you need it");
 	    }
 	}
     }
 
-  if (property_assign_semantics == OBJC_PROPERTY_RETAIN
-      && !objc_type_valid_for_messaging (TREE_TYPE (decl), true))
-    error_at (location, "%<retain%> attribute is only valid for Objective-C objects");
+  /* Some attributes make no sense unless applied to an Objective-C object.  */
+  bool prop_objc_object_p
+    = objc_type_valid_for_messaging (TREE_TYPE (decl), true);
+  if (!prop_objc_object_p)
+    {
+      tree p_name = NULL_TREE;
+      if (property_assign_semantics == OBJC_PROPERTY_RETAIN
+	  || property_assign_semantics == OBJC_PROPERTY_COPY)
+	p_name = attrs[OBJC_PROPATTR_GROUP_ASSIGN]->name;
 
-  if (property_assign_semantics == OBJC_PROPERTY_COPY
-      && !objc_type_valid_for_messaging (TREE_TYPE (decl), true))
-    error_at (location, "%<copy%> attribute is only valid for Objective-C objects");
+      if (p_name)
+	error_at (decl_loc, "%qE attribute is only valid for Objective-C"
+		  " objects", p_name);
+    }
 
   /* Now determine the final property getter and setter names.  They
      will be stored in the PROPERTY_DECL, from which they'll always be
      extracted and used.  */
 
   /* Adjust, or fill in, setter and getter names.  We overwrite the
-     parsed_property_setter_ident and parsed_property_getter_ident
+     property_setter_ident and property_getter_ident
      with the final setter and getter identifiers that will be
      used.  */
-  if (parsed_property_setter_ident)
+  if (property_setter_ident)
     {
       /* The setter should be terminated by ':', but the parser only
 	 gives us an identifier without ':'.  So, we need to add ':'
 	 at the end.  */
-      const char *parsed_setter = IDENTIFIER_POINTER (parsed_property_setter_ident);
+      const char *parsed_setter = IDENTIFIER_POINTER (property_setter_ident);
       size_t length = strlen (parsed_setter);
       char *final_setter = (char *)alloca (length + 2);
 
       sprintf (final_setter, "%s:", parsed_setter);
-      parsed_property_setter_ident = get_identifier (final_setter);
+      property_setter_ident = get_identifier (final_setter);
     }
   else
     {
       if (!property_readonly)
-	parsed_property_setter_ident = get_identifier (objc_build_property_setter_name
+	property_setter_ident = get_identifier (objc_build_property_setter_name
 						       (DECL_NAME (decl)));
     }
 
-  if (!parsed_property_getter_ident)
-    parsed_property_getter_ident = DECL_NAME (decl);
+  if (!property_getter_ident)
+    property_getter_ident = DECL_NAME (decl);
 
   /* Check for duplicate property declarations.  We first check the
      immediate context for a property with the same name.  Any such
      declarations are an error, unless this is a class extension and
      we are extending a property from readonly to readwrite.  */
+  bool property_extension_in_class_extension = false;
+  tree x = NULL_TREE;
   for (x = CLASS_PROPERTY_DECL (objc_interface_context); x; x = TREE_CHAIN (x))
     {
       if (PROPERTY_NAME (x) == DECL_NAME (decl))
 	{
 	  if (objc_in_class_extension
-	      && property_readonly == 0
+	      && !property_readonly
 	      && PROPERTY_READONLY (x) == 1)
 	    {
 	      /* This is a class extension, and we are extending an
@@ -1087,7 +1150,7 @@ objc_add_property_declaration (location_t location, tree decl,
 	 types, or it is compatible.  */
       location_t original_location = DECL_SOURCE_LOCATION (x);
 
-      if (PROPERTY_NONATOMIC (x) != parsed_property_nonatomic)
+      if (PROPERTY_NONATOMIC (x) != property_nonatomic)
 	{
 	  warning_at (location, 0,
 		      "%<nonatomic%> attribute of property %qD conflicts with "
@@ -1098,7 +1161,7 @@ objc_add_property_declaration (location_t location, tree decl,
 	  return;
 	}
 
-      if (PROPERTY_GETTER_NAME (x) != parsed_property_getter_ident)
+      if (PROPERTY_GETTER_NAME (x) != property_getter_ident)
 	{
 	  warning_at (location, 0,
 		      "%<getter%> attribute of property %qD conflicts with "
@@ -1112,7 +1175,7 @@ objc_add_property_declaration (location_t location, tree decl,
       /* We can only compare the setter names if both the old and new property have a setter.  */
       if (!property_readonly  &&  !PROPERTY_READONLY(x))
 	{
-	  if (PROPERTY_SETTER_NAME (x) != parsed_property_setter_ident)
+	  if (PROPERTY_SETTER_NAME (x) != property_setter_ident)
 	    {
 	      warning_at (location, 0,
 			  "%<setter%> attribute of property %qD conflicts with "
@@ -1190,13 +1253,13 @@ objc_add_property_declaration (location_t location, tree decl,
       if (property_extension_in_class_extension)
 	{
 	  PROPERTY_READONLY (x) = 0;
-	  PROPERTY_SETTER_NAME (x) = parsed_property_setter_ident;
+	  PROPERTY_SETTER_NAME (x) = property_setter_ident;
 	  return;
 	}
     }
 
   /* Create a PROPERTY_DECL node.  */
-  property_decl = make_node (PROPERTY_DECL);
+  tree property_decl = make_node (PROPERTY_DECL);
 
   /* Copy the basic information from the original decl.  */
   TREE_TYPE (property_decl) = TREE_TYPE (decl);
@@ -1205,10 +1268,10 @@ objc_add_property_declaration (location_t location, tree decl,
 
   /* Add property-specific information.  */
   PROPERTY_NAME (property_decl) = DECL_NAME (decl);
-  PROPERTY_GETTER_NAME (property_decl) = parsed_property_getter_ident;
-  PROPERTY_SETTER_NAME (property_decl) = parsed_property_setter_ident;
+  PROPERTY_GETTER_NAME (property_decl) = property_getter_ident;
+  PROPERTY_SETTER_NAME (property_decl) = property_setter_ident;
   PROPERTY_READONLY (property_decl) = property_readonly;
-  PROPERTY_NONATOMIC (property_decl) = parsed_property_nonatomic;
+  PROPERTY_NONATOMIC (property_decl) = property_nonatomic;
   PROPERTY_ASSIGN_SEMANTICS (property_decl) = property_assign_semantics;
   PROPERTY_IVAR_NAME (property_decl) = NULL_TREE;
   PROPERTY_DYNAMIC (property_decl) = 0;
diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-1.mm b/gcc/testsuite/obj-c++.dg/property/at-property-1.mm
index 6a90471d89cf344d03e237f3998d4bce104eb73e..332582384a383b495ced7bf221df08105e04276f 100644
--- a/gcc/testsuite/obj-c++.dg/property/at-property-1.mm
+++ b/gcc/testsuite/obj-c++.dg/property/at-property-1.mm
@@ -6,14 +6,18 @@
 {
   Class isa;
 }
-@property;                      /* { dg-error "expected identifier" } */
+@property;                      /* { dg-error "expected" } */
 @property int;                  /* { dg-error "expected identifier" } */
+
 @property int a;
 @property int b, c;
-@property () int d;             /* { dg-error "expected identifier" } */
+@property () int d;             /* { dg-warning "empty property attribute list" } */
 @property (readonly) int e;
-@property (readonly,) int f;    /* { dg-error "expected identifier" } */
+@property (readonly,) int f;    /* { dg-warning "missing property attribute" } */
 @property (xxx) int g;          /* { dg-error "unknown property attribute" } */
 @property (readonly,xxx) int h; /* { dg-error "unknown property attribute" } */
-@property ( int i;              /* { dg-error "expected identifier" } */
+@property ( int i;              /* { dg-error "unknown property attribute" } */
+				/* { dg-error "expected" "" { target *-*-* } .-1 } */
+@property (assign,,nonatomic) int j; /* { dg-warning "missing property attribute" } */
+@property (assign nonatomic) int k; /* { dg-error {expected } } */
 @end
diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-29.mm b/gcc/testsuite/obj-c++.dg/property/at-property-29.mm
index 0f31617f841d7f2dc43ad09d01ed93e58238be4b..64dfe83cd5957097ca407ed5bddd48a229239fd8 100644
--- a/gcc/testsuite/obj-c++.dg/property/at-property-29.mm
+++ b/gcc/testsuite/obj-c++.dg/property/at-property-29.mm
@@ -8,7 +8,9 @@
   Class isa;
 }
 /* Test missing '=' in setter/getter attributes.  */
-@property (getter)  int property_a; /* { dg-error "missing .=. .after .getter. attribute." } */
-@property (setter) int property_b;  /* { dg-error "missing .=. .after .setter. attribute." } */
-@property (assign, getter) int property_c; /* { dg-error "missing .=. .after .getter. attribute." } */
+@property (getter)  int property_a; /* { dg-error {expected '=' after Objective-C 'getter'} } */
+@property (setter) int property_b;  /* { dg-error {expected '=' after Objective-C 'setter'} } */
+@property (assign, getter) int property_c; /* { dg-error {expected '=' after Objective-C 'getter'} } */
+@property (retain, getter=) id x; /* { dg-error {expected 'getter' selector name} } */
+@property (retain, setter=) id y; /* { dg-error {expected 'setter' selector name} } */
 @end
diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-4.mm b/gcc/testsuite/obj-c++.dg/property/at-property-4.mm
index 941aab8e33cec418456c544242c0c738efdeade9..4083947de71d3cab5e7d43a3fd54e0aaf5b9e612 100644
--- a/gcc/testsuite/obj-c++.dg/property/at-property-4.mm
+++ b/gcc/testsuite/obj-c++.dg/property/at-property-4.mm
@@ -27,14 +27,14 @@
 
 /* Now test various problems.  */
 
-@property (readonly, readwrite) int a;    /* { dg-error ".readonly. attribute conflicts with .readwrite. attribute" } */
+@property (readonly, readwrite) int a;    /* { dg-error ".readwrite. attribute conflicts with .readonly. attribute" } */
 @property (readonly, setter=mySetterB:) int b; /* { dg-error ".readonly. attribute conflicts with .setter. attribute" } */
 
-@property (assign, retain) id c;          /* { dg-error ".assign. attribute conflicts with .retain. attribute" } */
-@property (assign, copy) id d;            /* { dg-error ".assign. attribute conflicts with .copy. attribute" } */
+@property (assign, retain) id c;          /* { dg-error ".retain. attribute conflicts with .assign. attribute" } */
+@property (assign, copy) id d;            /* { dg-error ".copy. attribute conflicts with .assign. attribute" } */
 @property (copy, retain) id e;            /* { dg-error ".retain. attribute conflicts with .copy. attribute" } */
 
-@property (setter=mySetter:,setter=mySetter2:)  int f; /* { dg-error ".setter. attribute may only be specified once" } */
-@property (getter=myGetter, getter=myGetter2 )  int g; /* { dg-error ".getter. attribute may only be specified once" } */
+@property (setter=mySetter:,setter=mySetter2:)  int f; /* { dg-warning {multiple property 'setter' methods specified, the latest one will be used} } */
+@property (getter=myGetter, getter=myGetter2 )  int g; /* { dg-warning {multiple property 'getter' methods specified, the latest one will be used} } */
 
 @end
diff --git a/gcc/testsuite/obj-c++.dg/property/property-neg-2.mm b/gcc/testsuite/obj-c++.dg/property/property-neg-2.mm
index f730fe846442ffd8238ad391a64511ed3082743a..794f2bd0e965f789092fecd958bebc1608c714d2 100644
--- a/gcc/testsuite/obj-c++.dg/property/property-neg-2.mm
+++ b/gcc/testsuite/obj-c++.dg/property/property-neg-2.mm
@@ -4,5 +4,5 @@
 @end
 
 @implementation Bar
-@property int FooBar; /* { dg-error "property declaration not in @interface or @protocol context" } */
+@property int FooBar; /* { dg-error {property declaration not in '@interface', '@protocol' or 'category' context} } */
 @end
diff --git a/gcc/testsuite/objc.dg/property/at-property-1.m b/gcc/testsuite/objc.dg/property/at-property-1.m
index fa12fa282c63ad66c8a2429ca70163c4723ff401..6dba8f4b84a2f9d141f04a5678f53d5e7ecafa9d 100644
--- a/gcc/testsuite/objc.dg/property/at-property-1.m
+++ b/gcc/testsuite/objc.dg/property/at-property-1.m
@@ -11,11 +11,15 @@
                                 /* { dg-warning "declaration does not declare anything" "" { target *-*-* } .-1 } */
 @property int a;
 @property int b, c;
-@property () int d;             /* { dg-error "expected identifier" } */
+@property () int d;             /* { dg-warning "empty property attribute list" } */
 @property (readonly) int e;
-@property (readonly,) int f;    /* { dg-error "expected identifier" } */
+@property (readonly,) int f;    /* { dg-warning "missing property attribute" } */
 @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" } */
+				/* { dg-error "expected" "" { target *-*-* } .-1 } */
+@property (assign,,nonatomic) int j; /* { dg-warning "missing property attribute" } */
+@property (assign nonatomic) int k; /* { dg-error {expected } } */
+@property (assign) int l[4]; /* { dg-error {property cannot be an array} } */
+@property (assign) int : 5; /* { dg-error {properties must be named} } */
+@end
diff --git a/gcc/testsuite/objc.dg/property/at-property-29.m b/gcc/testsuite/objc.dg/property/at-property-29.m
index 0f31617f841d7f2dc43ad09d01ed93e58238be4b..0b34e1cf67199b516dfbf6a1cfa0dac9ca779b90 100644
--- a/gcc/testsuite/objc.dg/property/at-property-29.m
+++ b/gcc/testsuite/objc.dg/property/at-property-29.m
@@ -8,7 +8,8 @@
   Class isa;
 }
 /* Test missing '=' in setter/getter attributes.  */
-@property (getter)  int property_a; /* { dg-error "missing .=. .after .getter. attribute." } */
-@property (setter) int property_b;  /* { dg-error "missing .=. .after .setter. attribute." } */
-@property (assign, getter) int property_c; /* { dg-error "missing .=. .after .getter. attribute." } */
+@property (getter)  int property_a; /* { dg-error {expected '=' after Objective-C 'getter'} } */
+@property (setter) int property_b;  /* { dg-error {expected '=' after Objective-C 'setter'} } */
+@property (assign, getter) int property_c; /* { dg-error {expected '=' after Objective-C 'getter'} } */
+@property (retain, getter=) id x; /* { dg-error {expected 'getter' selector name} } */
 @end
diff --git a/gcc/testsuite/objc.dg/property/at-property-4.m b/gcc/testsuite/objc.dg/property/at-property-4.m
index 941aab8e33cec418456c544242c0c738efdeade9..4083947de71d3cab5e7d43a3fd54e0aaf5b9e612 100644
--- a/gcc/testsuite/objc.dg/property/at-property-4.m
+++ b/gcc/testsuite/objc.dg/property/at-property-4.m
@@ -27,14 +27,14 @@
 
 /* Now test various problems.  */
 
-@property (readonly, readwrite) int a;    /* { dg-error ".readonly. attribute conflicts with .readwrite. attribute" } */
+@property (readonly, readwrite) int a;    /* { dg-error ".readwrite. attribute conflicts with .readonly. attribute" } */
 @property (readonly, setter=mySetterB:) int b; /* { dg-error ".readonly. attribute conflicts with .setter. attribute" } */
 
-@property (assign, retain) id c;          /* { dg-error ".assign. attribute conflicts with .retain. attribute" } */
-@property (assign, copy) id d;            /* { dg-error ".assign. attribute conflicts with .copy. attribute" } */
+@property (assign, retain) id c;          /* { dg-error ".retain. attribute conflicts with .assign. attribute" } */
+@property (assign, copy) id d;            /* { dg-error ".copy. attribute conflicts with .assign. attribute" } */
 @property (copy, retain) id e;            /* { dg-error ".retain. attribute conflicts with .copy. attribute" } */
 
-@property (setter=mySetter:,setter=mySetter2:)  int f; /* { dg-error ".setter. attribute may only be specified once" } */
-@property (getter=myGetter, getter=myGetter2 )  int g; /* { dg-error ".getter. attribute may only be specified once" } */
+@property (setter=mySetter:,setter=mySetter2:)  int f; /* { dg-warning {multiple property 'setter' methods specified, the latest one will be used} } */
+@property (getter=myGetter, getter=myGetter2 )  int g; /* { dg-warning {multiple property 'getter' methods specified, the latest one will be used} } */
 
 @end
diff --git a/gcc/testsuite/objc.dg/property/at-property-5.m b/gcc/testsuite/objc.dg/property/at-property-5.m
index 1267df318ede19a663024e4f562a31fb3ac0b645..820f5b3101cb9e881af8f2ecb3486a56ec22a83a 100644
--- a/gcc/testsuite/objc.dg/property/at-property-5.m
+++ b/gcc/testsuite/objc.dg/property/at-property-5.m
@@ -31,4 +31,4 @@
    			          /* { dg-message "originally specified here" "" { target *-*-* } property_e_first } */
 @end
 
-@property id test; /* { dg-error "property declaration not in .interface or .protocol context" } */
+@property id test; /* { dg-error {property declaration not in '@interface', '@protocol' or 'category' context} } */
diff --git a/gcc/testsuite/objc.dg/property/property-neg-2.m b/gcc/testsuite/objc.dg/property/property-neg-2.m
index f730fe846442ffd8238ad391a64511ed3082743a..794f2bd0e965f789092fecd958bebc1608c714d2 100644
--- a/gcc/testsuite/objc.dg/property/property-neg-2.m
+++ b/gcc/testsuite/objc.dg/property/property-neg-2.m
@@ -4,5 +4,5 @@
 @end
 
 @implementation Bar
-@property int FooBar; /* { dg-error "property declaration not in @interface or @protocol context" } */
+@property int FooBar; /* { dg-error {property declaration not in '@interface', '@protocol' or 'category' context} } */
 @end