aboutsummaryrefslogtreecommitdiff
path: root/gcc/c
diff options
context:
space:
mode:
authorIain Sandoe <iain@sandoe.co.uk>2020-10-31 20:53:34 +0000
committerIain Sandoe <iain@sandoe.co.uk>2020-11-06 19:49:23 +0000
commit9a34a5cce6b50fc3527e7c7ab356808ed435883c (patch)
treea1b114d1c1f375371c8eb1cf69025a15d99dfb80 /gcc/c
parent6c282c14d1be0bba2bf5d49acd074b349f28ad17 (diff)
downloadgcc-9a34a5cce6b50fc3527e7c7ab356808ed435883c.zip
gcc-9a34a5cce6b50fc3527e7c7ab356808ed435883c.tar.gz
gcc-9a34a5cce6b50fc3527e7c7ab356808ed435883c.tar.bz2
Objective-C/C++ (parsers) : Update @property attribute parsing.
At present, we are missing parsing and checking for around half of the property attributes in use. The existing ad hoc scheme for the parser's communication with the Objective C validation is not suitable for extending to cover all the missing cases. Additionally: 1/ We were declaring errors in two cases that the reference implementation warns (or is silent). I've elected to warn for both those cases, (Wattributes) it could be that we should implement Wobjc-xxx-property warning masks (TODO). 2/ We were emitting spurious complaints about missing property attributes when these were not being parsed because we gave up on the first syntax error. 3/ The quality of the diagnostic locations was poor (that's true for much of Objective-C, we will have to improve it as we modernise areas). We continue to attempt to keep the code, warning and error output similar (preferably identical output) between the C and C++ front ends. The interface to the Objective-C-specific parts of the parsing is simplified to a vector of parsed (but not fully-checked) property attributes, this will simplify the addition of new attributes. gcc/c-family/ChangeLog: * c-objc.h (enum objc_property_attribute_group): New (enum objc_property_attribute_kind): New. (OBJC_PROPATTR_GROUP_MASK): New. (struct property_attribute_info): Small class encapsulating parser output from property attributes. (objc_prop_attr_kind_for_rid): New (objc_add_property_declaration): Simplify interface. * stub-objc.c (enum rid): Dummy type. (objc_add_property_declaration): Simplify interface. (objc_prop_attr_kind_for_rid): New. gcc/c/ChangeLog: * c-parser.c (c_parser_objc_at_property_declaration): Improve parsing fidelity. Associate better location info with @property attributes. Clean up the interface to objc_add_property_declaration (). gcc/cp/ChangeLog: * parser.c (cp_parser_objc_at_property_declaration): Improve parsing fidelity. Associate better location info with @property attributes. Clean up the interface to objc_add_property_declaration (). gcc/objc/ChangeLog: * objc-act.c (objc_prop_attr_kind_for_rid): New. (objc_add_property_declaration): Adjust to consume the parser output using a vector of parsed attributes. gcc/testsuite/ChangeLog: * obj-c++.dg/property/at-property-1.mm: Adjust expected diagnostics. * obj-c++.dg/property/at-property-29.mm: Likewise. * obj-c++.dg/property/at-property-4.mm: Likewise. * obj-c++.dg/property/property-neg-2.mm: Likewise. * objc.dg/property/at-property-1.m: Likewise. * objc.dg/property/at-property-29.m: Likewise. * objc.dg/property/at-property-4.m: Likewise. * objc.dg/property/at-property-5.m: Likewise. * objc.dg/property/property-neg-2.m: Likewise.
Diffstat (limited to 'gcc/c')
-rw-r--r--gcc/c/c-parser.c280
1 files changed, 159 insertions, 121 deletions
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index dedfb84..da8278a8 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;
}