diff mbox

[2/3] Matching tokens: C parts (v2)

Message ID 1501618904-5593-3-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm Aug. 1, 2017, 8:21 p.m. UTC
Changed in v2:

* Renamed template argument to traits_t; eliminated subclasses, just
  using traits struct.
* Moved enum constants into struct bodies (string constants can't be
  without constexpr, which isn't available in C++98).
* Fixed typo.

OK for trunk?

gcc/c/ChangeLog:
	* c-parser.c (c_parser_error): Rename to...
	(c_parser_error_richloc): ...this, making static, and adding
	"richloc" parameter, passing it to the c_parse_error call,
	rather than calling c_parser_set_source_position_from_token.
	(c_parser_error): Reintroduce, reimplementing in terms of the
	above, converting return type from void to bool.
	(class token_pair): New class.
	(struct matching_paren_traits): New struct.
	(matching_parens): New typedef.
	(struct matching_brace_traits): New struct.
	(matching_braces): New typedef.
	(get_matching_symbol): New function.
	(c_parser_require): Add param MATCHING_LOCATION, using it to
	highlight matching "opening" tokens for missing "closing" tokens.
	(c_parser_skip_until_found): Likewise.
	(c_parser_static_assert_declaration_no_semi): Convert explicit
	parsing of CPP_OPEN_PAREN and CPP_CLOSE_PAREN to use of
	class matching_parens, so that the pertinent open parenthesis is
	highlighted when there are problems locating the close
	parenthesis.
	(c_parser_struct_or_union_specifier): Likewise.
	(c_parser_typeof_specifier): Likewise.
	(c_parser_alignas_specifier): Likewise.
	(c_parser_simple_asm_expr): Likewise.
	(c_parser_braced_init): Likewise, for matching_braces.
	(c_parser_paren_condition): Likewise, for matching_parens.
	(c_parser_switch_statement): Likewise.
	(c_parser_for_statement): Likewise.
	(c_parser_asm_statement): Likewise.
	(c_parser_asm_operands): Likewise.
	(c_parser_cast_expression): Likewise.
	(c_parser_sizeof_expression): Likewise.
	(c_parser_alignof_expression): Likewise.
	(c_parser_generic_selection): Likewise.
	(c_parser_postfix_expression): Likewise for cases RID_VA_ARG,
	RID_OFFSETOF, RID_TYPES_COMPATIBLE_P, RID_AT_SELECTOR,
	RID_AT_PROTOCOL, RID_AT_ENCODE, reindenting as necessary.
	In case CPP_OPEN_PAREN, pass loc_open_paren to the
	c_parser_skip_until_found call.
	(c_parser_objc_class_definition): Use class matching_parens as
	above.
	(c_parser_objc_method_decl): Likewise.
	(c_parser_objc_try_catch_finally_statement): Likewise.
	(c_parser_objc_synchronized_statement): Likewise.
	(c_parser_objc_at_property_declaration): Likewise.
	(c_parser_oacc_wait_list): Likewise.
	(c_parser_omp_var_list_parens): Likewise.
	(c_parser_omp_clause_collapse): Likewise.
	(c_parser_omp_clause_default): Likewise.
	(c_parser_omp_clause_if): Likewise.
	(c_parser_omp_clause_num_threads): Likewise.
	(c_parser_omp_clause_num_tasks): Likewise.
	(c_parser_omp_clause_grainsize): Likewise.
	(c_parser_omp_clause_priority): Likewise.
	(c_parser_omp_clause_hint): Likewise.
	(c_parser_omp_clause_defaultmap): Likewise.
	(c_parser_oacc_single_int_clause): Likewise.
	(c_parser_omp_clause_ordered): Likewise.
	(c_parser_omp_clause_reduction): Likewise.
	(c_parser_omp_clause_schedule): Likewise.
	(c_parser_omp_clause_num_teams): Likewise.
	(c_parser_omp_clause_thread_limit): Likewise.
	(c_parser_omp_clause_aligned): Likewise.
	(c_parser_omp_clause_linear): Likewise.
	(c_parser_omp_clause_safelen): Likewise.
	(c_parser_omp_clause_simdlen): Likewise.
	(c_parser_omp_clause_depend): Likewise.
	(c_parser_omp_clause_map): Likewise.
	(c_parser_omp_clause_device): Likewise.
	(c_parser_omp_clause_dist_schedule): Likewise.
	(c_parser_omp_clause_proc_bind): Likewise.
	(c_parser_omp_clause_uniform): Likewise.
	(c_parser_omp_for_loop): Likewise.
	(c_parser_cilk_clause_vectorlength): Likewise.
	(c_parser_cilk_clause_linear): Likewise.
	(c_parser_transaction_expression): Likewise.
	* c-parser.h (c_parser_require): Add param matching_location with
	default UNKNOWN_LOCATION.
	(c_parser_error): Convert return type from void to bool.
	(c_parser_skip_until_found): Add param matching_location with
	default UNKNOWN_LOCATION.

gcc/testsuite/ChangeLog:
	* gcc.dg/unclosed-init.c: New test case.
---
 gcc/c/c-parser.c                     | 644 +++++++++++++++++++++++------------
 gcc/c/c-parser.h                     |   8 +-
 gcc/testsuite/gcc.dg/unclosed-init.c |   3 +
 3 files changed, 438 insertions(+), 217 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/unclosed-init.c

Comments

Jeff Law Aug. 3, 2017, 5:34 p.m. UTC | #1
On 08/01/2017 02:21 PM, David Malcolm wrote:
> Changed in v2:
> 
> * Renamed template argument to traits_t; eliminated subclasses, just
>   using traits struct.
> * Moved enum constants into struct bodies (string constants can't be
>   without constexpr, which isn't available in C++98).
> * Fixed typo.
> 
> OK for trunk?
> 
> gcc/c/ChangeLog:
> 	* c-parser.c (c_parser_error): Rename to...
> 	(c_parser_error_richloc): ...this, making static, and adding
> 	"richloc" parameter, passing it to the c_parse_error call,
> 	rather than calling c_parser_set_source_position_from_token.
> 	(c_parser_error): Reintroduce, reimplementing in terms of the
> 	above, converting return type from void to bool.
> 	(class token_pair): New class.
> 	(struct matching_paren_traits): New struct.
> 	(matching_parens): New typedef.
> 	(struct matching_brace_traits): New struct.
> 	(matching_braces): New typedef.
> 	(get_matching_symbol): New function.
> 	(c_parser_require): Add param MATCHING_LOCATION, using it to
> 	highlight matching "opening" tokens for missing "closing" tokens.
> 	(c_parser_skip_until_found): Likewise.
> 	(c_parser_static_assert_declaration_no_semi): Convert explicit
> 	parsing of CPP_OPEN_PAREN and CPP_CLOSE_PAREN to use of
> 	class matching_parens, so that the pertinent open parenthesis is
> 	highlighted when there are problems locating the close
> 	parenthesis.
> 	(c_parser_struct_or_union_specifier): Likewise.
> 	(c_parser_typeof_specifier): Likewise.
> 	(c_parser_alignas_specifier): Likewise.
> 	(c_parser_simple_asm_expr): Likewise.
> 	(c_parser_braced_init): Likewise, for matching_braces.
> 	(c_parser_paren_condition): Likewise, for matching_parens.
> 	(c_parser_switch_statement): Likewise.
> 	(c_parser_for_statement): Likewise.
> 	(c_parser_asm_statement): Likewise.
> 	(c_parser_asm_operands): Likewise.
> 	(c_parser_cast_expression): Likewise.
> 	(c_parser_sizeof_expression): Likewise.
> 	(c_parser_alignof_expression): Likewise.
> 	(c_parser_generic_selection): Likewise.
> 	(c_parser_postfix_expression): Likewise for cases RID_VA_ARG,
> 	RID_OFFSETOF, RID_TYPES_COMPATIBLE_P, RID_AT_SELECTOR,
> 	RID_AT_PROTOCOL, RID_AT_ENCODE, reindenting as necessary.
> 	In case CPP_OPEN_PAREN, pass loc_open_paren to the
> 	c_parser_skip_until_found call.
> 	(c_parser_objc_class_definition): Use class matching_parens as
> 	above.
> 	(c_parser_objc_method_decl): Likewise.
> 	(c_parser_objc_try_catch_finally_statement): Likewise.
> 	(c_parser_objc_synchronized_statement): Likewise.
> 	(c_parser_objc_at_property_declaration): Likewise.
> 	(c_parser_oacc_wait_list): Likewise.
> 	(c_parser_omp_var_list_parens): Likewise.
> 	(c_parser_omp_clause_collapse): Likewise.
> 	(c_parser_omp_clause_default): Likewise.
> 	(c_parser_omp_clause_if): Likewise.
> 	(c_parser_omp_clause_num_threads): Likewise.
> 	(c_parser_omp_clause_num_tasks): Likewise.
> 	(c_parser_omp_clause_grainsize): Likewise.
> 	(c_parser_omp_clause_priority): Likewise.
> 	(c_parser_omp_clause_hint): Likewise.
> 	(c_parser_omp_clause_defaultmap): Likewise.
> 	(c_parser_oacc_single_int_clause): Likewise.
> 	(c_parser_omp_clause_ordered): Likewise.
> 	(c_parser_omp_clause_reduction): Likewise.
> 	(c_parser_omp_clause_schedule): Likewise.
> 	(c_parser_omp_clause_num_teams): Likewise.
> 	(c_parser_omp_clause_thread_limit): Likewise.
> 	(c_parser_omp_clause_aligned): Likewise.
> 	(c_parser_omp_clause_linear): Likewise.
> 	(c_parser_omp_clause_safelen): Likewise.
> 	(c_parser_omp_clause_simdlen): Likewise.
> 	(c_parser_omp_clause_depend): Likewise.
> 	(c_parser_omp_clause_map): Likewise.
> 	(c_parser_omp_clause_device): Likewise.
> 	(c_parser_omp_clause_dist_schedule): Likewise.
> 	(c_parser_omp_clause_proc_bind): Likewise.
> 	(c_parser_omp_clause_uniform): Likewise.
> 	(c_parser_omp_for_loop): Likewise.
> 	(c_parser_cilk_clause_vectorlength): Likewise.
> 	(c_parser_cilk_clause_linear): Likewise.
> 	(c_parser_transaction_expression): Likewise.
> 	* c-parser.h (c_parser_require): Add param matching_location with
> 	default UNKNOWN_LOCATION.
> 	(c_parser_error): Convert return type from void to bool.
> 	(c_parser_skip_until_found): Add param matching_location with
> 	default UNKNOWN_LOCATION.
> 
> gcc/testsuite/ChangeLog:
> 	* gcc.dg/unclosed-init.c: New test case.
Phew.  I only spot-checked most of the changes around the new API for
requiring the open/close paren/brace/bracket or consuming
parens/braces/brackets.  They were very mechanical :-)

Jeff
David Malcolm Aug. 4, 2017, 2:32 p.m. UTC | #2
On Thu, 2017-08-03 at 11:34 -0600, Jeff Law wrote:
> On 08/01/2017 02:21 PM, David Malcolm wrote:
> > Changed in v2:
> > 
> > * Renamed template argument to traits_t; eliminated subclasses,
> > just
> >   using traits struct.
> > * Moved enum constants into struct bodies (string constants can't
> > be
> >   without constexpr, which isn't available in C++98).
> > * Fixed typo.
> > 
> > OK for trunk?
> > 
> > gcc/c/ChangeLog:
> > 	* c-parser.c (c_parser_error): Rename to...
> > 	(c_parser_error_richloc): ...this, making static, and adding
> > 	"richloc" parameter, passing it to the c_parse_error call,
> > 	rather than calling c_parser_set_source_position_from_token.
> > 	(c_parser_error): Reintroduce, reimplementing in terms of the
> > 	above, converting return type from void to bool.
> > 	(class token_pair): New class.
> > 	(struct matching_paren_traits): New struct.
> > 	(matching_parens): New typedef.
> > 	(struct matching_brace_traits): New struct.
> > 	(matching_braces): New typedef.
> > 	(get_matching_symbol): New function.
> > 	(c_parser_require): Add param MATCHING_LOCATION, using it to
> > 	highlight matching "opening" tokens for missing "closing"
> > tokens.
> > 	(c_parser_skip_until_found): Likewise.
> > 	(c_parser_static_assert_declaration_no_semi): Convert explicit
> > 	parsing of CPP_OPEN_PAREN and CPP_CLOSE_PAREN to use of
> > 	class matching_parens, so that the pertinent open parenthesis
> > is
> > 	highlighted when there are problems locating the close
> > 	parenthesis.
> > 	(c_parser_struct_or_union_specifier): Likewise.
> > 	(c_parser_typeof_specifier): Likewise.
> > 	(c_parser_alignas_specifier): Likewise.
> > 	(c_parser_simple_asm_expr): Likewise.
> > 	(c_parser_braced_init): Likewise, for matching_braces.
> > 	(c_parser_paren_condition): Likewise, for matching_parens.
> > 	(c_parser_switch_statement): Likewise.
> > 	(c_parser_for_statement): Likewise.
> > 	(c_parser_asm_statement): Likewise.
> > 	(c_parser_asm_operands): Likewise.
> > 	(c_parser_cast_expression): Likewise.
> > 	(c_parser_sizeof_expression): Likewise.
> > 	(c_parser_alignof_expression): Likewise.
> > 	(c_parser_generic_selection): Likewise.
> > 	(c_parser_postfix_expression): Likewise for cases RID_VA_ARG,
> > 	RID_OFFSETOF, RID_TYPES_COMPATIBLE_P, RID_AT_SELECTOR,
> > 	RID_AT_PROTOCOL, RID_AT_ENCODE, reindenting as necessary.
> > 	In case CPP_OPEN_PAREN, pass loc_open_paren to the
> > 	c_parser_skip_until_found call.
> > 	(c_parser_objc_class_definition): Use class matching_parens as
> > 	above.
> > 	(c_parser_objc_method_decl): Likewise.
> > 	(c_parser_objc_try_catch_finally_statement): Likewise.
> > 	(c_parser_objc_synchronized_statement): Likewise.
> > 	(c_parser_objc_at_property_declaration): Likewise.
> > 	(c_parser_oacc_wait_list): Likewise.
> > 	(c_parser_omp_var_list_parens): Likewise.
> > 	(c_parser_omp_clause_collapse): Likewise.
> > 	(c_parser_omp_clause_default): Likewise.
> > 	(c_parser_omp_clause_if): Likewise.
> > 	(c_parser_omp_clause_num_threads): Likewise.
> > 	(c_parser_omp_clause_num_tasks): Likewise.
> > 	(c_parser_omp_clause_grainsize): Likewise.
> > 	(c_parser_omp_clause_priority): Likewise.
> > 	(c_parser_omp_clause_hint): Likewise.
> > 	(c_parser_omp_clause_defaultmap): Likewise.
> > 	(c_parser_oacc_single_int_clause): Likewise.
> > 	(c_parser_omp_clause_ordered): Likewise.
> > 	(c_parser_omp_clause_reduction): Likewise.
> > 	(c_parser_omp_clause_schedule): Likewise.
> > 	(c_parser_omp_clause_num_teams): Likewise.
> > 	(c_parser_omp_clause_thread_limit): Likewise.
> > 	(c_parser_omp_clause_aligned): Likewise.
> > 	(c_parser_omp_clause_linear): Likewise.
> > 	(c_parser_omp_clause_safelen): Likewise.
> > 	(c_parser_omp_clause_simdlen): Likewise.
> > 	(c_parser_omp_clause_depend): Likewise.
> > 	(c_parser_omp_clause_map): Likewise.
> > 	(c_parser_omp_clause_device): Likewise.
> > 	(c_parser_omp_clause_dist_schedule): Likewise.
> > 	(c_parser_omp_clause_proc_bind): Likewise.
> > 	(c_parser_omp_clause_uniform): Likewise.
> > 	(c_parser_omp_for_loop): Likewise.
> > 	(c_parser_cilk_clause_vectorlength): Likewise.
> > 	(c_parser_cilk_clause_linear): Likewise.
> > 	(c_parser_transaction_expression): Likewise.
> > 	* c-parser.h (c_parser_require): Add param matching_location
> > with
> > 	default UNKNOWN_LOCATION.
> > 	(c_parser_error): Convert return type from void to bool.
> > 	(c_parser_skip_until_found): Add param matching_location with
> > 	default UNKNOWN_LOCATION.
> > 
> > gcc/testsuite/ChangeLog:
> > 	* gcc.dg/unclosed-init.c: New test case.
> 
> Phew.  I only spot-checked most of the changes around the new API for
> requiring the open/close paren/brace/bracket or consuming
> parens/braces/brackets.  They were very mechanical :-)

Thanks for looking at this.  Do you have an opinion on Trevor's idea
the the "parser" argument should be moved into the token_pair class (to
avoid manually passing it in everywhere), or should it be kept outside
and passed in as needed? 

I was worried about increasing register pressure in the parsers, since
it's not clear to me that the optimizer can always prove that a field
"token_pair::m_parser" in a local token_pair is equal to the parser
local (see:
https://gcc.gnu.org/ml/gcc-patches/2017-08/msg00114.html )

Dave
Jeff Law Aug. 4, 2017, 6:09 p.m. UTC | #3
On 08/04/2017 08:32 AM, David Malcolm wrote:
> On Thu, 2017-08-03 at 11:34 -0600, Jeff Law wrote:
>> On 08/01/2017 02:21 PM, David Malcolm wrote:
>>> Changed in v2:
>>>
>>> * Renamed template argument to traits_t; eliminated subclasses,
>>> just
>>>   using traits struct.
>>> * Moved enum constants into struct bodies (string constants can't
>>> be
>>>   without constexpr, which isn't available in C++98).
>>> * Fixed typo.
>>>
>>> OK for trunk?
>>>
>>> gcc/c/ChangeLog:
>>> 	* c-parser.c (c_parser_error): Rename to...
>>> 	(c_parser_error_richloc): ...this, making static, and adding
>>> 	"richloc" parameter, passing it to the c_parse_error call,
>>> 	rather than calling c_parser_set_source_position_from_token.
>>> 	(c_parser_error): Reintroduce, reimplementing in terms of the
>>> 	above, converting return type from void to bool.
>>> 	(class token_pair): New class.
>>> 	(struct matching_paren_traits): New struct.
>>> 	(matching_parens): New typedef.
>>> 	(struct matching_brace_traits): New struct.
>>> 	(matching_braces): New typedef.
>>> 	(get_matching_symbol): New function.
>>> 	(c_parser_require): Add param MATCHING_LOCATION, using it to
>>> 	highlight matching "opening" tokens for missing "closing"
>>> tokens.
>>> 	(c_parser_skip_until_found): Likewise.
>>> 	(c_parser_static_assert_declaration_no_semi): Convert explicit
>>> 	parsing of CPP_OPEN_PAREN and CPP_CLOSE_PAREN to use of
>>> 	class matching_parens, so that the pertinent open parenthesis
>>> is
>>> 	highlighted when there are problems locating the close
>>> 	parenthesis.
>>> 	(c_parser_struct_or_union_specifier): Likewise.
>>> 	(c_parser_typeof_specifier): Likewise.
>>> 	(c_parser_alignas_specifier): Likewise.
>>> 	(c_parser_simple_asm_expr): Likewise.
>>> 	(c_parser_braced_init): Likewise, for matching_braces.
>>> 	(c_parser_paren_condition): Likewise, for matching_parens.
>>> 	(c_parser_switch_statement): Likewise.
>>> 	(c_parser_for_statement): Likewise.
>>> 	(c_parser_asm_statement): Likewise.
>>> 	(c_parser_asm_operands): Likewise.
>>> 	(c_parser_cast_expression): Likewise.
>>> 	(c_parser_sizeof_expression): Likewise.
>>> 	(c_parser_alignof_expression): Likewise.
>>> 	(c_parser_generic_selection): Likewise.
>>> 	(c_parser_postfix_expression): Likewise for cases RID_VA_ARG,
>>> 	RID_OFFSETOF, RID_TYPES_COMPATIBLE_P, RID_AT_SELECTOR,
>>> 	RID_AT_PROTOCOL, RID_AT_ENCODE, reindenting as necessary.
>>> 	In case CPP_OPEN_PAREN, pass loc_open_paren to the
>>> 	c_parser_skip_until_found call.
>>> 	(c_parser_objc_class_definition): Use class matching_parens as
>>> 	above.
>>> 	(c_parser_objc_method_decl): Likewise.
>>> 	(c_parser_objc_try_catch_finally_statement): Likewise.
>>> 	(c_parser_objc_synchronized_statement): Likewise.
>>> 	(c_parser_objc_at_property_declaration): Likewise.
>>> 	(c_parser_oacc_wait_list): Likewise.
>>> 	(c_parser_omp_var_list_parens): Likewise.
>>> 	(c_parser_omp_clause_collapse): Likewise.
>>> 	(c_parser_omp_clause_default): Likewise.
>>> 	(c_parser_omp_clause_if): Likewise.
>>> 	(c_parser_omp_clause_num_threads): Likewise.
>>> 	(c_parser_omp_clause_num_tasks): Likewise.
>>> 	(c_parser_omp_clause_grainsize): Likewise.
>>> 	(c_parser_omp_clause_priority): Likewise.
>>> 	(c_parser_omp_clause_hint): Likewise.
>>> 	(c_parser_omp_clause_defaultmap): Likewise.
>>> 	(c_parser_oacc_single_int_clause): Likewise.
>>> 	(c_parser_omp_clause_ordered): Likewise.
>>> 	(c_parser_omp_clause_reduction): Likewise.
>>> 	(c_parser_omp_clause_schedule): Likewise.
>>> 	(c_parser_omp_clause_num_teams): Likewise.
>>> 	(c_parser_omp_clause_thread_limit): Likewise.
>>> 	(c_parser_omp_clause_aligned): Likewise.
>>> 	(c_parser_omp_clause_linear): Likewise.
>>> 	(c_parser_omp_clause_safelen): Likewise.
>>> 	(c_parser_omp_clause_simdlen): Likewise.
>>> 	(c_parser_omp_clause_depend): Likewise.
>>> 	(c_parser_omp_clause_map): Likewise.
>>> 	(c_parser_omp_clause_device): Likewise.
>>> 	(c_parser_omp_clause_dist_schedule): Likewise.
>>> 	(c_parser_omp_clause_proc_bind): Likewise.
>>> 	(c_parser_omp_clause_uniform): Likewise.
>>> 	(c_parser_omp_for_loop): Likewise.
>>> 	(c_parser_cilk_clause_vectorlength): Likewise.
>>> 	(c_parser_cilk_clause_linear): Likewise.
>>> 	(c_parser_transaction_expression): Likewise.
>>> 	* c-parser.h (c_parser_require): Add param matching_location
>>> with
>>> 	default UNKNOWN_LOCATION.
>>> 	(c_parser_error): Convert return type from void to bool.
>>> 	(c_parser_skip_until_found): Add param matching_location with
>>> 	default UNKNOWN_LOCATION.
>>>
>>> gcc/testsuite/ChangeLog:
>>> 	* gcc.dg/unclosed-init.c: New test case.
>>
>> Phew.  I only spot-checked most of the changes around the new API for
>> requiring the open/close paren/brace/bracket or consuming
>> parens/braces/brackets.  They were very mechanical :-)
> 
> Thanks for looking at this.  Do you have an opinion on Trevor's idea
> the the "parser" argument should be moved into the token_pair class (to
> avoid manually passing it in everywhere), or should it be kept outside
> and passed in as needed? 
No opinion.  I'd put it where ever it makes the  most logical sense in
terms of code readability -- until such a point as it's shown to be a
bottleneck.

> 
> I was worried about increasing register pressure in the parsers, since
> it's not clear to me that the optimizer can always prove that a field
> "token_pair::m_parser" in a local token_pair is equal to the parser
> local (see:
> https://gcc.gnu.org/ml/gcc-patches/2017-08/msg00114.html )
The ability to prove they're the same would depend on aliasing
relationships and whether or not the objects escape as well. Odds are it
won't be able to determine that in general.

jeff
David Malcolm Aug. 8, 2017, 8:37 p.m. UTC | #4
On Fri, 2017-08-04 at 12:09 -0600, Jeff Law wrote:
> On 08/04/2017 08:32 AM, David Malcolm wrote:
> > On Thu, 2017-08-03 at 11:34 -0600, Jeff Law wrote:
> > > On 08/01/2017 02:21 PM, David Malcolm wrote:
> > > > Changed in v2:
> > > > 
> > > > * Renamed template argument to traits_t; eliminated subclasses,
> > > > just
> > > >   using traits struct.
> > > > * Moved enum constants into struct bodies (string constants
> > > > can't
> > > > be
> > > >   without constexpr, which isn't available in C++98).
> > > > * Fixed typo.
> > > > 
> > > > OK for trunk?
> > > > 
> > > > gcc/c/ChangeLog:
> > > > 	* c-parser.c (c_parser_error): Rename to...
> > > > 	(c_parser_error_richloc): ...this, making static, and
> > > > adding
> > > > 	"richloc" parameter, passing it to the c_parse_error
> > > > call,
> > > > 	rather than calling
> > > > c_parser_set_source_position_from_token.
> > > > 	(c_parser_error): Reintroduce, reimplementing in terms
> > > > of the
> > > > 	above, converting return type from void to bool.
> > > > 	(class token_pair): New class.
> > > > 	(struct matching_paren_traits): New struct.
> > > > 	(matching_parens): New typedef.
> > > > 	(struct matching_brace_traits): New struct.
> > > > 	(matching_braces): New typedef.
> > > > 	(get_matching_symbol): New function.
> > > > 	(c_parser_require): Add param MATCHING_LOCATION, using
> > > > it to
> > > > 	highlight matching "opening" tokens for missing
> > > > "closing"
> > > > tokens.
> > > > 	(c_parser_skip_until_found): Likewise.
> > > > 	(c_parser_static_assert_declaration_no_semi): Convert
> > > > explicit
> > > > 	parsing of CPP_OPEN_PAREN and CPP_CLOSE_PAREN to use of
> > > > 	class matching_parens, so that the pertinent open
> > > > parenthesis
> > > > is
> > > > 	highlighted when there are problems locating the close
> > > > 	parenthesis.
> > > > 	(c_parser_struct_or_union_specifier): Likewise.
> > > > 	(c_parser_typeof_specifier): Likewise.
> > > > 	(c_parser_alignas_specifier): Likewise.
> > > > 	(c_parser_simple_asm_expr): Likewise.
> > > > 	(c_parser_braced_init): Likewise, for matching_braces.
> > > > 	(c_parser_paren_condition): Likewise, for
> > > > matching_parens.
> > > > 	(c_parser_switch_statement): Likewise.
> > > > 	(c_parser_for_statement): Likewise.
> > > > 	(c_parser_asm_statement): Likewise.
> > > > 	(c_parser_asm_operands): Likewise.
> > > > 	(c_parser_cast_expression): Likewise.
> > > > 	(c_parser_sizeof_expression): Likewise.
> > > > 	(c_parser_alignof_expression): Likewise.
> > > > 	(c_parser_generic_selection): Likewise.
> > > > 	(c_parser_postfix_expression): Likewise for cases
> > > > RID_VA_ARG,
> > > > 	RID_OFFSETOF, RID_TYPES_COMPATIBLE_P, RID_AT_SELECTOR,
> > > > 	RID_AT_PROTOCOL, RID_AT_ENCODE, reindenting as
> > > > necessary.
> > > > 	In case CPP_OPEN_PAREN, pass loc_open_paren to the
> > > > 	c_parser_skip_until_found call.
> > > > 	(c_parser_objc_class_definition): Use class
> > > > matching_parens as
> > > > 	above.
> > > > 	(c_parser_objc_method_decl): Likewise.
> > > > 	(c_parser_objc_try_catch_finally_statement): Likewise.
> > > > 	(c_parser_objc_synchronized_statement): Likewise.
> > > > 	(c_parser_objc_at_property_declaration): Likewise.
> > > > 	(c_parser_oacc_wait_list): Likewise.
> > > > 	(c_parser_omp_var_list_parens): Likewise.
> > > > 	(c_parser_omp_clause_collapse): Likewise.
> > > > 	(c_parser_omp_clause_default): Likewise.
> > > > 	(c_parser_omp_clause_if): Likewise.
> > > > 	(c_parser_omp_clause_num_threads): Likewise.
> > > > 	(c_parser_omp_clause_num_tasks): Likewise.
> > > > 	(c_parser_omp_clause_grainsize): Likewise.
> > > > 	(c_parser_omp_clause_priority): Likewise.
> > > > 	(c_parser_omp_clause_hint): Likewise.
> > > > 	(c_parser_omp_clause_defaultmap): Likewise.
> > > > 	(c_parser_oacc_single_int_clause): Likewise.
> > > > 	(c_parser_omp_clause_ordered): Likewise.
> > > > 	(c_parser_omp_clause_reduction): Likewise.
> > > > 	(c_parser_omp_clause_schedule): Likewise.
> > > > 	(c_parser_omp_clause_num_teams): Likewise.
> > > > 	(c_parser_omp_clause_thread_limit): Likewise.
> > > > 	(c_parser_omp_clause_aligned): Likewise.
> > > > 	(c_parser_omp_clause_linear): Likewise.
> > > > 	(c_parser_omp_clause_safelen): Likewise.
> > > > 	(c_parser_omp_clause_simdlen): Likewise.
> > > > 	(c_parser_omp_clause_depend): Likewise.
> > > > 	(c_parser_omp_clause_map): Likewise.
> > > > 	(c_parser_omp_clause_device): Likewise.
> > > > 	(c_parser_omp_clause_dist_schedule): Likewise.
> > > > 	(c_parser_omp_clause_proc_bind): Likewise.
> > > > 	(c_parser_omp_clause_uniform): Likewise.
> > > > 	(c_parser_omp_for_loop): Likewise.
> > > > 	(c_parser_cilk_clause_vectorlength): Likewise.
> > > > 	(c_parser_cilk_clause_linear): Likewise.
> > > > 	(c_parser_transaction_expression): Likewise.
> > > > 	* c-parser.h (c_parser_require): Add param
> > > > matching_location
> > > > with
> > > > 	default UNKNOWN_LOCATION.
> > > > 	(c_parser_error): Convert return type from void to
> > > > bool.
> > > > 	(c_parser_skip_until_found): Add param
> > > > matching_location with
> > > > 	default UNKNOWN_LOCATION.
> > > > 
> > > > gcc/testsuite/ChangeLog:
> > > > 	* gcc.dg/unclosed-init.c: New test case.
> > > 
> > > Phew.  I only spot-checked most of the changes around the new API
> > > for
> > > requiring the open/close paren/brace/bracket or consuming
> > > parens/braces/brackets.  They were very mechanical :-)
> > 
> > Thanks for looking at this.  Do you have an opinion on Trevor's
> > idea
> > the the "parser" argument should be moved into the token_pair class
> > (to
> > avoid manually passing it in everywhere), or should it be kept
> > outside
> > and passed in as needed? 
> 
> No opinion.  I'd put it where ever it makes the  most logical sense
> in
> terms of code readability -- until such a point as it's shown to be a
> bottleneck.

Jason said (for the C++ part of the patch):
> About passing parser in or not, I'm happy with the current approach; 
> adding things to the stack isn't free in a highly recursive program 
> like GCC.
(in https://gcc.gnu.org/ml/gcc-patches/2017-08/msg00535.html )

Jeff: is the C patch OK for trunk (assuming the rest of the kit is
approved), or were you just reporting that you spot-checked the
mechanical changes and that you hadn't seen any problems with those?

[...]

Thanks
Dave
Marek Polacek Aug. 9, 2017, 6:46 a.m. UTC | #5
On Tue, Aug 08, 2017 at 04:37:28PM -0400, David Malcolm wrote:
> Jason said (for the C++ part of the patch):
> > About passing parser in or not, I'm happy with the current approach; 
> > adding things to the stack isn't free in a highly recursive program 
> > like GCC.
> (in https://gcc.gnu.org/ml/gcc-patches/2017-08/msg00535.html )
> 
> Jeff: is the C patch OK for trunk (assuming the rest of the kit is
> approved), or were you just reporting that you spot-checked the
> mechanical changes and that you hadn't seen any problems with those?

The C part is ok.

	Marek
diff mbox

Patch

diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 16cd357..3803154 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -850,21 +850,26 @@  c_parser_peek_conflict_marker (c_parser *parser, enum cpp_ttype tok1_kind,
    MESSAGE (specified by the caller) is usually of the form "expected
    OTHER-TOKEN".
 
+   Use RICHLOC as the location of the diagnostic.
+
    Do not issue a diagnostic if still recovering from an error.
 
+   Return true iff an error was actually emitted.
+
    ??? This is taken from the C++ parser, but building up messages in
    this way is not i18n-friendly and some other approach should be
    used.  */
 
-void
-c_parser_error (c_parser *parser, const char *gmsgid)
+static bool
+c_parser_error_richloc (c_parser *parser, const char *gmsgid,
+			rich_location *richloc)
 {
   c_token *token = c_parser_peek_token (parser);
   if (parser->error)
-    return;
+    return false;
   parser->error = true;
   if (!gmsgid)
-    return;
+    return false;
 
   /* If this is actually a conflict marker, report it as such.  */
   if (token->type == CPP_LSHIFT
@@ -875,13 +880,10 @@  c_parser_error (c_parser *parser, const char *gmsgid)
       if (c_parser_peek_conflict_marker (parser, token->type, &loc))
 	{
 	  error_at (loc, "version control conflict marker in file");
-	  return;
+	  return true;
 	}
     }
 
-  /* This diagnostic makes more sense if it is tagged to the line of
-     the token we just peeked at.  */
-  c_parser_set_source_position_from_token (token);
   c_parse_error (gmsgid,
 		 /* Because c_parse_error does not understand
 		    CPP_KEYWORD, keywords are treated like
@@ -891,18 +893,157 @@  c_parser_error (c_parser *parser, const char *gmsgid)
 		    token, we need to pass 0 here and we will not get
 		    the source spelling of some tokens but rather the
 		    canonical spelling.  */
-		 token->value, /*flags=*/0);
+		 token->value, /*flags=*/0, richloc);
+  return true;
+}
+
+/* As c_parser_error_richloc, but issue the message at the
+   location of PARSER's next token, or at input_location
+   if the next token is EOF.  */
+
+bool
+c_parser_error (c_parser *parser, const char *gmsgid)
+{
+  c_token *token = c_parser_peek_token (parser);
+  c_parser_set_source_position_from_token (token);
+  rich_location richloc (line_table, input_location);
+  return c_parser_error_richloc (parser, gmsgid, &richloc);
+}
+
+/* Some tokens naturally come in pairs e.g.'(' and ')'.
+   This class is for tracking such a matching pair of symbols.
+   In particular, it tracks the location of the first token,
+   so that if the second token is missing, we can highlight the
+   location of the first token when notifying the user about the
+   problem.  */
+
+template <typename traits_t>
+class token_pair
+{
+ public:
+  /* token_pair's ctor.  */
+  token_pair () : m_open_loc (UNKNOWN_LOCATION) {}
+
+  /* If the next token is the opening symbol for this pair, consume it and
+     return true.
+     Otherwise, issue an error and return false.
+     In either case, record the location of the opening token.  */
+
+  bool require_open (c_parser *parser)
+  {
+    c_token *token = c_parser_peek_token (parser);
+    if (token)
+      m_open_loc = token->location;
+
+    return c_parser_require (parser, traits_t::open_token_type,
+			     traits_t::open_gmsgid);
+  }
+
+  /* Consume the next token from PARSER, recording its location as
+     that of the opening token within the pair.  */
+
+  void consume_open (c_parser *parser)
+  {
+    c_token *token = c_parser_peek_token (parser);
+    gcc_assert (token->type == traits_t::open_token_type);
+    m_open_loc = token->location;
+    c_parser_consume_token (parser);
+  }
+
+  /* If the next token is the closing symbol for this pair, consume it
+     and return true.
+     Otherwise, issue an error, highlighting the location of the
+     corresponding opening token, and return false.  */
+
+  bool require_close (c_parser *parser) const
+  {
+    return c_parser_require (parser, traits_t::close_token_type,
+			     traits_t::close_gmsgid, m_open_loc);
+  }
+
+  /* Like token_pair::require_close, except that tokens will be skipped
+     until the desired token is found.  An error message is still produced
+     if the next token is not as expected.  */
+
+  void skip_until_found_close (c_parser *parser) const
+  {
+    c_parser_skip_until_found (parser, traits_t::close_token_type,
+			       traits_t::close_gmsgid, m_open_loc);
+  }
+
+ private:
+  location_t m_open_loc;
+};
+
+/* Traits for token_pair<T> for tracking matching pairs of parentheses.  */
+
+struct matching_paren_traits
+{
+  static const enum cpp_ttype open_token_type = CPP_OPEN_PAREN;
+  static const char * const open_gmsgid;
+  static const enum cpp_ttype close_token_type = CPP_CLOSE_PAREN;
+  static const char * const close_gmsgid;
+};
+
+const char * const matching_paren_traits::open_gmsgid = "expected %<(%>";
+const char * const matching_paren_traits::close_gmsgid = "expected %<)%>";
+
+/* "matching_parens" is a token_pair<T> class for tracking matching
+   pairs of parentheses.  */
+
+typedef token_pair<matching_paren_traits> matching_parens;
+
+/* Traits for token_pair<T> for tracking matching pairs of braces.  */
+
+struct matching_brace_traits
+{
+  static const enum cpp_ttype open_token_type = CPP_OPEN_BRACE;
+  static const char * const open_gmsgid;
+  static const enum cpp_ttype close_token_type = CPP_CLOSE_BRACE;
+  static const char * const close_gmsgid;
+};
+
+const char * const matching_brace_traits::open_gmsgid = "expected %<{%>";
+const char * const matching_brace_traits::close_gmsgid = "expected %<}%>";
+
+/* "matching_braces" is a token_pair<T> class for tracking matching
+   pairs of braces.  */
+
+typedef token_pair<matching_brace_traits> matching_braces;
+
+/* Get a description of the matching symbol to TYPE e.g. "(" for
+   CPP_CLOSE_PAREN.  */
+
+static const char *
+get_matching_symbol (enum cpp_ttype type)
+{
+  switch (type)
+    {
+    default:
+      gcc_unreachable ();
+      return "";
+    case CPP_CLOSE_PAREN:
+      return "(";
+    case CPP_CLOSE_BRACE:
+      return "{";
+    }
 }
 
 /* If the next token is of the indicated TYPE, consume it.  Otherwise,
    issue the error MSGID.  If MSGID is NULL then a message has already
    been produced and no message will be produced this time.  Returns
-   true if found, false otherwise.  */
+   true if found, false otherwise.
+
+   If MATCHING_LOCATION is not UNKNOWN_LOCATION, then highlight it
+   within any error as the location of an "opening" token matching
+   the close token TYPE (e.g. the location of the '(' when TYPE is
+   CPP_CLOSE_PAREN).  */
 
 bool
 c_parser_require (c_parser *parser,
 		  enum cpp_ttype type,
-		  const char *msgid)
+		  const char *msgid,
+		  location_t matching_location)
 {
   if (c_parser_next_token_is (parser, type))
     {
@@ -911,7 +1052,24 @@  c_parser_require (c_parser *parser,
     }
   else
     {
-      c_parser_error (parser, msgid);
+      location_t next_token_loc = c_parser_peek_token (parser)->location;
+      gcc_rich_location richloc (next_token_loc);
+
+      /* If matching_location != UNKNOWN_LOCATION, highlight it.
+	 Attempt to consolidate diagnostics by printing it as a
+	 secondary range within the main diagnostic.  */
+      bool added_matching_location = false;
+      if (matching_location != UNKNOWN_LOCATION)
+	added_matching_location
+	  = richloc.add_location_if_nearby (matching_location);
+
+      if (c_parser_error_richloc (parser, msgid, &richloc))
+	/* If we weren't able to consolidate matching_location, then
+	   print it as a secondary diagnostic.  */
+	if (matching_location != UNKNOWN_LOCATION && !added_matching_location)
+	  inform (matching_location, "to match this %qs",
+		  get_matching_symbol (type));
+
       return false;
     }
 }
@@ -940,16 +1098,22 @@  c_parser_require_keyword (c_parser *parser,
    desired token is found.  An error message is still produced if the
    next token is not as expected.  If MSGID is NULL then a message has
    already been produced and no message will be produced this
-   time.  */
+   time.
+
+   If MATCHING_LOCATION is not UNKNOWN_LOCATION, then highlight it
+   within any error as the location of an "opening" token matching
+   the close token TYPE (e.g. the location of the '(' when TYPE is
+   CPP_CLOSE_PAREN).  */
 
 void
 c_parser_skip_until_found (c_parser *parser,
 			   enum cpp_ttype type,
-			   const char *msgid)
+			   const char *msgid,
+			   location_t matching_location)
 {
   unsigned nesting_depth = 0;
 
-  if (c_parser_require (parser, type, msgid))
+  if (c_parser_require (parser, type, msgid, matching_location))
     return;
 
   /* Skip tokens until the desired token is found.  */
@@ -2210,7 +2374,8 @@  c_parser_static_assert_declaration_no_semi (c_parser *parser)
     pedwarn_c99 (assert_loc, OPT_Wpedantic,
 		 "ISO C90 does not support %<_Static_assert%>");
   c_parser_consume_token (parser);
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return;
   location_t value_tok_loc = c_parser_peek_token (parser)->location;
   value = c_parser_expr_no_commas (parser, NULL).value;
@@ -2237,7 +2402,7 @@  c_parser_static_assert_declaration_no_semi (c_parser *parser)
       parser->lex_untranslated_string = false;
       return;
     }
-  c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.require_close (parser);
 
   if (!INTEGRAL_TYPE_P (TREE_TYPE (value)))
     {
@@ -2922,7 +3087,8 @@  c_parser_struct_or_union_specifier (c_parser *parser)
 	  tree name;
 	  gcc_assert (c_dialect_objc ());
 	  c_parser_consume_token (parser);
-	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+	  matching_parens parens;
+	  if (!parens.require_open (parser))
 	    goto end_at_defs;
 	  if (c_parser_next_token_is (parser, CPP_NAME)
 	      && c_parser_peek_token (parser)->id_kind == C_ID_CLASSNAME)
@@ -2936,8 +3102,7 @@  c_parser_struct_or_union_specifier (c_parser *parser)
 	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
 	      goto end_at_defs;
 	    }
-	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
-				     "expected %<)%>");
+	  parens.skip_until_found_close (parser);
 	  contents = nreverse (objc_get_class_ivars (name));
 	}
     end_at_defs:
@@ -3224,7 +3389,8 @@  c_parser_typeof_specifier (c_parser *parser)
   c_parser_consume_token (parser);
   c_inhibit_evaluation_warnings++;
   in_typeof++;
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     {
       c_inhibit_evaluation_warnings--;
       in_typeof--;
@@ -3265,7 +3431,7 @@  c_parser_typeof_specifier (c_parser *parser)
       if (ret.spec != error_mark_node && TYPE_ATOMIC (ret.spec))
 	ret.spec = c_build_qualified_type (ret.spec, TYPE_UNQUALIFIED);
     }
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return ret;
 }
 
@@ -3291,7 +3457,8 @@  c_parser_alignas_specifier (c_parser * parser)
   else
     pedwarn_c99 (loc, OPT_Wpedantic,
 		 "ISO C90 does not support %<_Alignas%>");
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return ret;
   if (c_parser_next_tokens_start_typename (parser, cla_prefer_id))
     {
@@ -3302,7 +3469,7 @@  c_parser_alignas_specifier (c_parser * parser)
     }
   else
     ret = c_parser_expr_no_commas (parser, NULL).value;
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return ret;
 }
 
@@ -3953,14 +4120,15 @@  c_parser_simple_asm_expr (c_parser *parser)
      lex_untranslated_string kludge.  */
   parser->lex_untranslated_string = true;
   c_parser_consume_token (parser);
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     {
       parser->lex_untranslated_string = false;
       return NULL_TREE;
     }
   str = c_parser_asm_string_literal (parser);
   parser->lex_untranslated_string = false;
-  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
+  if (!parens.require_close (parser))
     {
       c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
       return NULL_TREE;
@@ -4395,7 +4563,8 @@  c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
   location_t brace_loc = c_parser_peek_token (parser)->location;
   gcc_obstack_init (&braced_init_obstack);
   gcc_assert (c_parser_next_token_is (parser, CPP_OPEN_BRACE));
-  c_parser_consume_token (parser);
+  matching_braces braces;
+  braces.consume_open (parser);
   if (nested_p)
     {
       finish_implicit_inits (brace_loc, outer_obstack);
@@ -4433,7 +4602,7 @@  c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
       ret.value = error_mark_node;
       ret.original_code = ERROR_MARK;
       ret.original_type = NULL;
-      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, "expected %<}%>");
+      braces.skip_until_found_close (parser);
       pop_init_level (brace_loc, 0, &braced_init_obstack, last_init_list_comma);
       obstack_free (&braced_init_obstack, NULL);
       return ret;
@@ -5456,10 +5625,11 @@  static tree
 c_parser_paren_condition (c_parser *parser)
 {
   tree cond;
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return error_mark_node;
   cond = c_parser_condition (parser);
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return cond;
 }
 
@@ -5728,7 +5898,8 @@  c_parser_switch_statement (c_parser *parser, bool *if_p)
   c_parser_consume_token (parser);
   block = c_begin_compound_stmt (flag_isoc99);
   bool explicit_cast_p = false;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       switch_cond_loc = c_parser_peek_token (parser)->location;
       if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)
@@ -5743,7 +5914,7 @@  c_parser_switch_statement (c_parser *parser, bool *if_p)
 	 "%<_Cilk_spawn%> statement cannot be used as a condition for switch statement",
 			 switch_cond_loc))
         expr = error_mark_node;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   else
     {
@@ -5953,7 +6124,8 @@  c_parser_for_statement (c_parser *parser, bool ivdep, bool *if_p)
   block = c_begin_compound_stmt (flag_isoc99 || c_dialect_objc ());
   cond = error_mark_node;
   incr = error_mark_node;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       /* Parse the initialization declaration or expression.  */
       object_expression = error_mark_node;
@@ -6100,7 +6272,7 @@  c_parser_for_statement (c_parser *parser, bool ivdep, bool *if_p)
 	      incr = c_process_expr_stmt (loc, ce.value);
 	    }
 	}
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   save_break = c_break_label;
   c_break_label = NULL_TREE;
@@ -6194,7 +6366,8 @@  c_parser_asm_statement (c_parser *parser)
   parser->lex_untranslated_string = true;
   ret = NULL;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     goto error;
 
   str = c_parser_asm_string_literal (parser);
@@ -6252,7 +6425,7 @@  c_parser_asm_statement (c_parser *parser)
     }
 
  done_asm:
-  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
+  if (!parens.require_close (parser))
     {
       c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
       goto error;
@@ -6317,7 +6490,8 @@  c_parser_asm_operands (c_parser *parser)
       if (str == NULL_TREE)
 	return NULL_TREE;
       parser->lex_untranslated_string = false;
-      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+      matching_parens parens;
+      if (!parens.require_open (parser))
 	{
 	  parser->lex_untranslated_string = true;
 	  return NULL_TREE;
@@ -6325,7 +6499,7 @@  c_parser_asm_operands (c_parser *parser)
       expr = c_parser_expression (parser);
       mark_exp_read (expr.value);
       parser->lex_untranslated_string = true;
-      if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
+      if (!parens.require_close (parser))
 	{
 	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
 	  return NULL_TREE;
@@ -6934,9 +7108,10 @@  c_parser_cast_expression (c_parser *parser, struct c_expr *after)
       struct c_type_name *type_name;
       struct c_expr ret;
       struct c_expr expr;
-      c_parser_consume_token (parser);
+      matching_parens parens;
+      parens.consume_open (parser);
       type_name = c_parser_type_name (parser);
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
       if (type_name == NULL)
 	{
 	  ret.value = error_mark_node;
@@ -7163,10 +7338,11 @@  c_parser_sizeof_expression (c_parser *parser)
       /* Either sizeof ( type-name ) or sizeof unary-expression
 	 starting with a compound literal.  */
       struct c_type_name *type_name;
-      c_parser_consume_token (parser);
+      matching_parens parens;
+      parens.consume_open (parser);
       expr_loc = c_parser_peek_token (parser)->location;
       type_name = c_parser_type_name (parser);
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
       finish = parser->tokens_buf[0].location;
       if (type_name == NULL)
 	{
@@ -7245,11 +7421,12 @@  c_parser_alignof_expression (c_parser *parser)
       location_t loc;
       struct c_type_name *type_name;
       struct c_expr ret;
-      c_parser_consume_token (parser);
+      matching_parens parens;
+      parens.consume_open (parser);
       loc = c_parser_peek_token (parser)->location;
       type_name = c_parser_type_name (parser);
       end_loc = c_parser_peek_token (parser)->location;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
       if (type_name == NULL)
 	{
 	  struct c_expr ret;
@@ -7408,7 +7585,8 @@  c_parser_generic_selection (c_parser *parser)
     pedwarn_c99 (generic_loc, OPT_Wpedantic,
 		 "ISO C90 does not support %<_Generic%>");
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return error_expr;
 
   c_inhibit_evaluation_warnings++;
@@ -7550,7 +7728,7 @@  c_parser_generic_selection (c_parser *parser)
       c_parser_consume_token (parser);
     }
 
-  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
+  if (!parens.require_close (parser))
     {
       c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
       return error_expr;
@@ -7796,7 +7974,7 @@  c_parser_postfix_expression (c_parser *parser)
 	  location_t loc_close_paren = c_parser_peek_token (parser)->location;
 	  set_c_expr_source_range (&expr, loc_open_paren, loc_close_paren);
 	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
-				     "expected %<)%>");
+				     "expected %<)%>", loc_open_paren);
 	}
       break;
     case CPP_KEYWORD:
@@ -7833,7 +8011,8 @@  c_parser_postfix_expression (c_parser *parser)
 	  {
 	    location_t start_loc = loc;
 	    c_parser_consume_token (parser);
-	    if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+	    matching_parens parens;
+	    if (!parens.require_open (parser))
 	      {
 		expr.set_error ();
 		break;
@@ -7873,25 +8052,25 @@  c_parser_postfix_expression (c_parser *parser)
 	  }
 	  break;
 	case RID_OFFSETOF:
-	  c_parser_consume_token (parser);
-	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
-	    {
-	      expr.set_error ();
-	      break;
-	    }
-	  t1 = c_parser_type_name (parser);
-	  if (t1 == NULL)
-	    parser->error = true;
-	  if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
-            gcc_assert (parser->error);
-	  if (parser->error)
-	    {
-	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-	      expr.set_error ();
-	      break;
-	    }
-
 	  {
+	    c_parser_consume_token (parser);
+	    matching_parens parens;
+	    if (!parens.require_open (parser))
+	      {
+		expr.set_error ();
+		break;
+	      }
+	    t1 = c_parser_type_name (parser);
+	    if (t1 == NULL)
+	      parser->error = true;
+	    if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+	      gcc_assert (parser->error);
+	    if (parser->error)
+	      {
+		c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+		expr.set_error ();
+		break;
+	      }
 	    tree type = groktypename (t1, NULL, NULL);
 	    tree offsetof_ref;
 	    if (type == error_mark_node)
@@ -8008,34 +8187,34 @@  c_parser_postfix_expression (c_parser *parser)
 	    break;
 	  }
 	case RID_TYPES_COMPATIBLE_P:
-	  c_parser_consume_token (parser);
-	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
-	    {
-	      expr.set_error ();
-	      break;
-	    }
-	  t1 = c_parser_type_name (parser);
-	  if (t1 == NULL)
-	    {
-	      expr.set_error ();
-	      break;
-	    }
-	  if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
-	    {
-	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-	      expr.set_error ();
-	      break;
-	    }
-	  t2 = c_parser_type_name (parser);
-	  if (t2 == NULL)
-	    {
-	      expr.set_error ();
-	      break;
-	    }
 	  {
+	    c_parser_consume_token (parser);
+	    matching_parens parens;
+	    if (!parens.require_open (parser))
+	      {
+		expr.set_error ();
+		break;
+	      }
+	    t1 = c_parser_type_name (parser);
+	    if (t1 == NULL)
+	      {
+		expr.set_error ();
+		break;
+	      }
+	    if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+	      {
+		c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+		expr.set_error ();
+		break;
+	      }
+	    t2 = c_parser_type_name (parser);
+	    if (t2 == NULL)
+	      {
+		expr.set_error ();
+		break;
+	      }
 	    location_t close_paren_loc = c_parser_peek_token (parser)->location;
-	    c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
-				       "expected %<)%>");
+	    parens.skip_until_found_close (parser);
 	    tree e1, e2;
 	    e1 = groktypename (t1, NULL, NULL);
 	    e2 = groktypename (t2, NULL, NULL);
@@ -8201,67 +8380,67 @@  c_parser_postfix_expression (c_parser *parser)
 	    break;
 	  }
 	case RID_AT_SELECTOR:
-	  gcc_assert (c_dialect_objc ());
-	  c_parser_consume_token (parser);
-	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
-	    {
-	      expr.set_error ();
-	      break;
-	    }
 	  {
+	    gcc_assert (c_dialect_objc ());
+	    c_parser_consume_token (parser);
+	    matching_parens parens;
+	    if (!parens.require_open (parser))
+	      {
+		expr.set_error ();
+		break;
+	      }
 	    tree sel = c_parser_objc_selector_arg (parser);
 	    location_t close_loc = c_parser_peek_token (parser)->location;
-	    c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
-				       "expected %<)%>");
+	    parens.skip_until_found_close (parser);
 	    expr.value = objc_build_selector_expr (loc, sel);
 	    set_c_expr_source_range (&expr, loc, close_loc);
 	  }
 	  break;
 	case RID_AT_PROTOCOL:
-	  gcc_assert (c_dialect_objc ());
-	  c_parser_consume_token (parser);
-	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
-	    {
-	      expr.set_error ();
-	      break;
-	    }
-	  if (c_parser_next_token_is_not (parser, CPP_NAME))
-	    {
-	      c_parser_error (parser, "expected identifier");
-	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-	      expr.set_error ();
-	      break;
-	    }
 	  {
+	    gcc_assert (c_dialect_objc ());
+	    c_parser_consume_token (parser);
+	    matching_parens parens;
+	    if (!parens.require_open (parser))
+	      {
+		expr.set_error ();
+		break;
+	      }
+	    if (c_parser_next_token_is_not (parser, CPP_NAME))
+	      {
+		c_parser_error (parser, "expected identifier");
+		c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+		expr.set_error ();
+		break;
+	      }
 	    tree id = c_parser_peek_token (parser)->value;
 	    c_parser_consume_token (parser);
 	    location_t close_loc = c_parser_peek_token (parser)->location;
-	    c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
-				       "expected %<)%>");
+	    parens.skip_until_found_close (parser);
 	    expr.value = objc_build_protocol_expr (id);
 	    set_c_expr_source_range (&expr, loc, close_loc);
 	  }
 	  break;
 	case RID_AT_ENCODE:
-	  /* Extension to support C-structures in the archiver.  */
-	  gcc_assert (c_dialect_objc ());
-	  c_parser_consume_token (parser);
-	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
-	    {
-	      expr.set_error ();
-	      break;
-	    }
-	  t1 = c_parser_type_name (parser);
-	  if (t1 == NULL)
-	    {
-	      expr.set_error ();
-	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-	      break;
-	    }
 	  {
+	    /* Extension to support C-structures in the archiver.  */
+	    gcc_assert (c_dialect_objc ());
+	    c_parser_consume_token (parser);
+	    matching_parens parens;
+	    if (!parens.require_open (parser))
+	      {
+		expr.set_error ();
+		break;
+	      }
+	    t1 = c_parser_type_name (parser);
+	    if (t1 == NULL)
+	      {
+		expr.set_error ();
+		c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+		break;
+	      }
 	    location_t close_loc = c_parser_peek_token (parser)->location;
-	    c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
-				     "expected %<)%>");
+	    parens.skip_until_found_close (parser);
 	    tree type = groktypename (t1, NULL, NULL);
 	    expr.value = objc_build_encode_expr (type);
 	    set_c_expr_source_range (&expr, loc, close_loc);
@@ -8854,7 +9033,8 @@  c_parser_objc_class_definition (c_parser *parser, tree attributes)
       /* We have a category or class extension.  */
       tree id2;
       tree proto = NULL_TREE;
-      c_parser_consume_token (parser);
+      matching_parens parens;
+      parens.consume_open (parser);
       if (c_parser_next_token_is_not (parser, CPP_NAME))
 	{
 	  if (iface_p && c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
@@ -8874,7 +9054,7 @@  c_parser_objc_class_definition (c_parser *parser, tree attributes)
 	  id2 = c_parser_peek_token (parser)->value;
 	  c_parser_consume_token (parser);
 	}
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
       if (!iface_p)
 	{
 	  objc_start_category_implementation (id1, id2);
@@ -9408,9 +9588,10 @@  c_parser_objc_method_decl (c_parser *parser, bool is_class_method,
   *attributes = NULL_TREE;
   if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
-      c_parser_consume_token (parser);
+      matching_parens parens;
+      parens.consume_open (parser);
       type = c_parser_objc_type_name (parser);
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   sel = c_parser_objc_selector (parser);
   /* If there is no selector, or a colon follows, we have an
@@ -9616,7 +9797,8 @@  c_parser_objc_try_catch_finally_statement (c_parser *parser)
       bool seen_open_paren = false;
 
       c_parser_consume_token (parser);
-      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+      matching_parens parens;
+      if (!parens.require_open (parser))
 	seen_open_paren = true;
       if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
 	{
@@ -9639,7 +9821,7 @@  c_parser_objc_try_catch_finally_statement (c_parser *parser)
 	    parameter_declaration = grokparm (parm, NULL);
 	}
       if (seen_open_paren)
-	c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+	parens.require_close (parser);
       else
 	{
 	  /* If there was no open parenthesis, we are recovering from
@@ -9688,13 +9870,14 @@  c_parser_objc_synchronized_statement (c_parser *parser)
   c_parser_consume_token (parser);
   loc = c_parser_peek_token (parser)->location;
   objc_maybe_warn_exceptions (loc);
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       struct c_expr ce = c_parser_expression (parser);
       ce = convert_lvalue_to_rvalue (loc, ce, false, false);
       expr = ce.value;
       expr = c_fully_fold (expr, false, NULL);
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   else
     expr = error_mark_node;
@@ -9988,9 +10171,11 @@  c_parser_objc_at_property_declaration (c_parser *parser)
   /* Parse the optional attribute list...  */
   if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
+      matching_parens parens;
+
       /* Eat the '(' */
-      c_parser_consume_token (parser);
-      
+      parens.consume_open (parser);
+
       /* Property attribute keywords are valid now.  */
       parser->objc_property_attr_context = true;
 
@@ -10078,7 +10263,7 @@  c_parser_objc_at_property_declaration (c_parser *parser)
 	    break;
 	}
       parser->objc_property_attr_context = false;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   /* ... and the property declaration(s).  */
   properties = c_parser_struct_declaration (parser);
@@ -10738,7 +10923,8 @@  c_parser_oacc_wait_list (c_parser *parser, location_t clause_loc, tree list)
   vec<tree, va_gc> *args;
   tree t, args_tree;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   args = c_parser_expr_list (parser, false, true, NULL, NULL, NULL, NULL);
@@ -10775,7 +10961,7 @@  c_parser_oacc_wait_list (c_parser *parser, location_t clause_loc, tree list)
     }
 
   release_tree_vector (args);
-  c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.require_close (parser);
   return list;
 }
 
@@ -10933,10 +11119,11 @@  c_parser_omp_var_list_parens (c_parser *parser, enum omp_clause_code kind,
   /* The clauses location.  */
   location_t loc = c_parser_peek_token (parser)->location;
 
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       list = c_parser_omp_variable_list (parser, loc, kind, list);
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   return list;
 }
@@ -11073,10 +11260,11 @@  c_parser_omp_clause_collapse (c_parser *parser, tree list)
   check_no_duplicate_clause (list, OMP_CLAUSE_TILE, "tile");
 
   loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       num = c_parser_expr_no_commas (parser, NULL).value;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   if (num == error_mark_node)
     return list;
@@ -11128,7 +11316,8 @@  c_parser_omp_clause_default (c_parser *parser, tree list, bool is_oacc)
   location_t loc = c_parser_peek_token (parser)->location;
   tree c;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
   if (c_parser_next_token_is (parser, CPP_NAME))
     {
@@ -11168,7 +11357,7 @@  c_parser_omp_clause_default (c_parser *parser, tree list, bool is_oacc)
       else
 	c_parser_error (parser, "expected %<none%> or %<shared%>");
     }
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
 
   if (kind == OMP_CLAUSE_DEFAULT_UNSPECIFIED)
     return list;
@@ -11231,7 +11420,8 @@  c_parser_omp_clause_if (c_parser *parser, tree list, bool is_omp)
   location_t location = c_parser_peek_token (parser)->location;
   enum tree_code if_modifier = ERROR_MARK;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (is_omp && c_parser_next_token_is (parser, CPP_NAME))
@@ -11312,7 +11502,7 @@  c_parser_omp_clause_if (c_parser *parser, tree list, bool is_omp)
     }
 
   tree t = c_parser_condition (parser), c;
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
 
   for (c = list; c ; c = OMP_CLAUSE_CHAIN (c))
     if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IF)
@@ -11411,7 +11601,8 @@  static tree
 c_parser_omp_clause_num_threads (c_parser *parser, tree list)
 {
   location_t num_threads_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       location_t expr_loc = c_parser_peek_token (parser)->location;
       c_expr expr = c_parser_expression (parser);
@@ -11419,7 +11610,7 @@  c_parser_omp_clause_num_threads (c_parser *parser, tree list)
       tree c, t = expr.value;
       t = c_fully_fold (t, false, NULL);
 
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
 	{
@@ -11456,7 +11647,8 @@  static tree
 c_parser_omp_clause_num_tasks (c_parser *parser, tree list)
 {
   location_t num_tasks_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       location_t expr_loc = c_parser_peek_token (parser)->location;
       c_expr expr = c_parser_expression (parser);
@@ -11464,7 +11656,7 @@  c_parser_omp_clause_num_tasks (c_parser *parser, tree list)
       tree c, t = expr.value;
       t = c_fully_fold (t, false, NULL);
 
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
 	{
@@ -11501,7 +11693,8 @@  static tree
 c_parser_omp_clause_grainsize (c_parser *parser, tree list)
 {
   location_t grainsize_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       location_t expr_loc = c_parser_peek_token (parser)->location;
       c_expr expr = c_parser_expression (parser);
@@ -11509,7 +11702,7 @@  c_parser_omp_clause_grainsize (c_parser *parser, tree list)
       tree c, t = expr.value;
       t = c_fully_fold (t, false, NULL);
 
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
 	{
@@ -11546,7 +11739,8 @@  static tree
 c_parser_omp_clause_priority (c_parser *parser, tree list)
 {
   location_t priority_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       location_t expr_loc = c_parser_peek_token (parser)->location;
       c_expr expr = c_parser_expression (parser);
@@ -11554,7 +11748,7 @@  c_parser_omp_clause_priority (c_parser *parser, tree list)
       tree c, t = expr.value;
       t = c_fully_fold (t, false, NULL);
 
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
 	{
@@ -11592,7 +11786,8 @@  static tree
 c_parser_omp_clause_hint (c_parser *parser, tree list)
 {
   location_t hint_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       location_t expr_loc = c_parser_peek_token (parser)->location;
       c_expr expr = c_parser_expression (parser);
@@ -11600,7 +11795,7 @@  c_parser_omp_clause_hint (c_parser *parser, tree list)
       tree c, t = expr.value;
       t = c_fully_fold (t, false, NULL);
 
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
 	{
@@ -11629,7 +11824,8 @@  c_parser_omp_clause_defaultmap (c_parser *parser, tree list)
   tree c;
   const char *p;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
   if (!c_parser_next_token_is (parser, CPP_NAME))
     {
@@ -11657,14 +11853,14 @@  c_parser_omp_clause_defaultmap (c_parser *parser, tree list)
       goto out_err;
     }
   c_parser_consume_token (parser);
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   check_no_duplicate_clause (list, OMP_CLAUSE_DEFAULTMAP, "defaultmap");
   c = build_omp_clause (loc, OMP_CLAUSE_DEFAULTMAP);
   OMP_CLAUSE_CHAIN (c) = list;
   return c;
 
  out_err:
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return list;
 }
 
@@ -11701,7 +11897,8 @@  c_parser_oacc_single_int_clause (c_parser *parser, omp_clause_code code,
 {
   location_t loc = c_parser_peek_token (parser)->location;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   location_t expr_loc = c_parser_peek_token (parser)->location;
@@ -11710,7 +11907,7 @@  c_parser_oacc_single_int_clause (c_parser *parser, omp_clause_code code,
   tree c, t = expr.value;
   t = c_fully_fold (t, false, NULL);
 
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
 
   if (t == error_mark_node)
     return list;
@@ -12040,9 +12237,10 @@  c_parser_omp_clause_ordered (c_parser *parser, tree list)
   location_t loc = c_parser_peek_token (parser)->location;
   if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
-      c_parser_consume_token (parser);
+      matching_parens parens;
+      parens.consume_open (parser);
       num = c_parser_expr_no_commas (parser, NULL).value;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   if (num == error_mark_node)
     return list;
@@ -12096,7 +12294,8 @@  static tree
 c_parser_omp_clause_reduction (c_parser *parser, tree list)
 {
   location_t clause_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       enum tree_code code = ERROR_MARK;
       tree reduc_id = NULL_TREE;
@@ -12194,7 +12393,7 @@  c_parser_omp_clause_reduction (c_parser *parser, tree list)
 
 	  list = nl;
 	}
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   return list;
 }
@@ -12222,7 +12421,8 @@  c_parser_omp_clause_schedule (c_parser *parser, tree list)
   location_t loc = c_parser_peek_token (parser)->location;
   int modifiers = 0, nmodifiers = 0;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   c = build_omp_clause (loc, OMP_CLAUSE_SCHEDULE);
@@ -12333,7 +12533,7 @@  c_parser_omp_clause_schedule (c_parser *parser, tree list)
       else
 	c_parser_error (parser, "expected integer expression");
 
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   else
     c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
@@ -12446,7 +12646,8 @@  static tree
 c_parser_omp_clause_num_teams (c_parser *parser, tree list)
 {
   location_t num_teams_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       location_t expr_loc = c_parser_peek_token (parser)->location;
       c_expr expr = c_parser_expression (parser);
@@ -12454,7 +12655,7 @@  c_parser_omp_clause_num_teams (c_parser *parser, tree list)
       tree c, t = expr.value;
       t = c_fully_fold (t, false, NULL);
 
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
 	{
@@ -12490,7 +12691,8 @@  static tree
 c_parser_omp_clause_thread_limit (c_parser *parser, tree list)
 {
   location_t num_thread_limit_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       location_t expr_loc = c_parser_peek_token (parser)->location;
       c_expr expr = c_parser_expression (parser);
@@ -12498,7 +12700,7 @@  c_parser_omp_clause_thread_limit (c_parser *parser, tree list)
       tree c, t = expr.value;
       t = c_fully_fold (t, false, NULL);
 
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
 	{
@@ -12538,7 +12740,8 @@  c_parser_omp_clause_aligned (c_parser *parser, tree list)
   location_t clause_loc = c_parser_peek_token (parser)->location;
   tree nl, c;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   nl = c_parser_omp_variable_list (parser, clause_loc,
@@ -12565,7 +12768,7 @@  c_parser_omp_clause_aligned (c_parser *parser, tree list)
 	OMP_CLAUSE_ALIGNED_ALIGNMENT (c) = alignment;
     }
 
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return nl;
 }
 
@@ -12584,7 +12787,8 @@  c_parser_omp_clause_linear (c_parser *parser, tree list, bool is_cilk_simd_fn)
   tree nl, c, step;
   enum omp_clause_linear_kind kind = OMP_CLAUSE_LINEAR_DEFAULT;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (!is_cilk_simd_fn
@@ -12607,7 +12811,7 @@  c_parser_omp_clause_linear (c_parser *parser, tree list, bool is_cilk_simd_fn)
 				   OMP_CLAUSE_LINEAR, list);
 
   if (kind != OMP_CLAUSE_LINEAR_DEFAULT)
-    c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+    parens.skip_until_found_close (parser);
 
   if (c_parser_next_token_is (parser, CPP_COLON))
     {
@@ -12639,7 +12843,7 @@  c_parser_omp_clause_linear (c_parser *parser, tree list, bool is_cilk_simd_fn)
       OMP_CLAUSE_LINEAR_KIND (c) = kind;
     }
 
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return nl;
 }
 
@@ -12652,7 +12856,8 @@  c_parser_omp_clause_safelen (c_parser *parser, tree list)
   location_t clause_loc = c_parser_peek_token (parser)->location;
   tree c, t;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   location_t expr_loc = c_parser_peek_token (parser)->location;
@@ -12669,7 +12874,7 @@  c_parser_omp_clause_safelen (c_parser *parser, tree list)
       t = NULL_TREE;
     }
 
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   if (t == NULL_TREE || t == error_mark_node)
     return list;
 
@@ -12690,7 +12895,8 @@  c_parser_omp_clause_simdlen (c_parser *parser, tree list)
   location_t clause_loc = c_parser_peek_token (parser)->location;
   tree c, t;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   location_t expr_loc = c_parser_peek_token (parser)->location;
@@ -12707,7 +12913,7 @@  c_parser_omp_clause_simdlen (c_parser *parser, tree list)
       t = NULL_TREE;
     }
 
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   if (t == NULL_TREE || t == error_mark_node)
     return list;
 
@@ -12819,7 +13025,8 @@  c_parser_omp_clause_depend (c_parser *parser, tree list)
   enum omp_clause_depend_kind kind = OMP_CLAUSE_DEPEND_INOUT;
   tree nl, c;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (c_parser_next_token_is (parser, CPP_NAME))
@@ -12849,7 +13056,7 @@  c_parser_omp_clause_depend (c_parser *parser, tree list)
       OMP_CLAUSE_DEPEND_KIND (c) = kind;
       OMP_CLAUSE_DECL (c) = NULL_TREE;
       OMP_CLAUSE_CHAIN (c) = list;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
       return c;
     }
 
@@ -12867,13 +13074,13 @@  c_parser_omp_clause_depend (c_parser *parser, tree list)
 	OMP_CLAUSE_DEPEND_KIND (c) = kind;
     }
 
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return nl;
 
  invalid_kind:
   c_parser_error (parser, "invalid depend kind");
  resync_fail:
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return list;
 }
 
@@ -12901,7 +13108,8 @@  c_parser_omp_clause_map (c_parser *parser, tree list)
   tree always_id = NULL_TREE;
   tree nl, c;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (c_parser_next_token_is (parser, CPP_NAME))
@@ -12968,7 +13176,7 @@  c_parser_omp_clause_map (c_parser *parser, tree list)
       if (always_id_kind != C_ID_ID)
 	{
 	  c_parser_error (parser, "expected identifier");
-	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+	  parens.skip_until_found_close (parser);
 	  return list;
 	}
 
@@ -12988,7 +13196,7 @@  c_parser_omp_clause_map (c_parser *parser, tree list)
 	}
       if (always == 1)
 	{
-	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+	  parens.skip_until_found_close (parser);
 	  return list;
 	}
     }
@@ -12998,7 +13206,7 @@  c_parser_omp_clause_map (c_parser *parser, tree list)
   for (c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
     OMP_CLAUSE_SET_MAP_KIND (c, kind);
 
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return nl;
 }
 
@@ -13009,7 +13217,8 @@  static tree
 c_parser_omp_clause_device (c_parser *parser, tree list)
 {
   location_t clause_loc = c_parser_peek_token (parser)->location;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       location_t expr_loc = c_parser_peek_token (parser)->location;
       c_expr expr = c_parser_expr_no_commas (parser, NULL);
@@ -13017,7 +13226,7 @@  c_parser_omp_clause_device (c_parser *parser, tree list)
       tree c, t = expr.value;
       t = c_fully_fold (t, false, NULL);
 
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (t)))
 	{
@@ -13046,7 +13255,8 @@  c_parser_omp_clause_dist_schedule (c_parser *parser, tree list)
   tree c, t = NULL_TREE;
   location_t loc = c_parser_peek_token (parser)->location;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (!c_parser_next_token_is_keyword (parser, RID_STATIC))
@@ -13067,7 +13277,7 @@  c_parser_omp_clause_dist_schedule (c_parser *parser, tree list)
       expr = convert_lvalue_to_rvalue (expr_loc, expr, false, true);
       t = expr.value;
       t = c_fully_fold (t, false, NULL);
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   else
     c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
@@ -13096,7 +13306,8 @@  c_parser_omp_clause_proc_bind (c_parser *parser, tree list)
   enum omp_clause_proc_bind_kind kind;
   tree c;
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (c_parser_next_token_is (parser, CPP_NAME))
@@ -13115,7 +13326,7 @@  c_parser_omp_clause_proc_bind (c_parser *parser, tree list)
     goto invalid_kind;
 
   c_parser_consume_token (parser);
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   c = build_omp_clause (clause_loc, OMP_CLAUSE_PROC_BIND);
   OMP_CLAUSE_PROC_BIND_KIND (c) = kind;
   OMP_CLAUSE_CHAIN (c) = list;
@@ -13123,7 +13334,7 @@  c_parser_omp_clause_proc_bind (c_parser *parser, tree list)
 
  invalid_kind:
   c_parser_error (parser, "invalid proc_bind kind");
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
   return list;
 }
 
@@ -13154,11 +13365,12 @@  c_parser_omp_clause_uniform (c_parser *parser, tree list)
   /* The clauses location.  */
   location_t loc = c_parser_peek_token (parser)->location;
 
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       list = c_parser_omp_variable_list (parser, loc, OMP_CLAUSE_UNIFORM,
 					 list);
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
     }
   return list;
 }
@@ -14951,7 +15163,8 @@  c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
     {
       int bracecount = 0;
 
-      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+      matching_parens parens;
+      if (!parens.require_open (parser))
 	goto pop_scopes;
 
       /* Parse the initialization declaration or expression.  */
@@ -15058,7 +15271,7 @@  c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
 	  incr = c_process_expr_stmt (incr_loc,
 				      c_parser_expression (parser).value);
 	}
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      parens.skip_until_found_close (parser);
 
       if (decl == NULL || decl == error_mark_node || init == error_mark_node)
 	fail = true;
@@ -17565,7 +17778,8 @@  c_parser_cilk_clause_vectorlength (c_parser *parser, tree clauses,
      clause.  Represent it in OpenMP terms.  */
     check_no_duplicate_clause (clauses, OMP_CLAUSE_SAFELEN, "vectorlength");
 
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return clauses;
 
   location_t loc = c_parser_peek_token (parser)->location;
@@ -17601,7 +17815,7 @@  c_parser_cilk_clause_vectorlength (c_parser *parser, tree clauses,
 	}
     }
 
-  c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.require_close (parser);
 
   return clauses;
 }
@@ -17623,7 +17837,8 @@  c_parser_cilk_clause_vectorlength (c_parser *parser, tree clauses,
 static tree
 c_parser_cilk_clause_linear (c_parser *parser, tree clauses)
 {
-  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return clauses;
 
   location_t loc = c_parser_peek_token (parser)->location;
@@ -17685,7 +17900,7 @@  c_parser_cilk_clause_linear (c_parser *parser, tree clauses)
       c_parser_consume_token (parser);
     }
 
-  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+  parens.skip_until_found_close (parser);
 
   return clauses;
 }
@@ -18049,7 +18264,8 @@  c_parser_transaction_expression (c_parser *parser, enum rid keyword)
     }
 
   parser->in_transaction = this_in;
-  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+  matching_parens parens;
+  if (parens.require_open (parser))
     {
       tree expr = c_parser_expression (parser).value;
       ret.original_type = TREE_TYPE (expr);
@@ -18058,7 +18274,7 @@  c_parser_transaction_expression (c_parser *parser, enum rid keyword)
 	TRANSACTION_EXPR_RELAXED (ret.value) = 1;
       SET_EXPR_LOCATION (ret.value, loc);
       ret.original_code = TRANSACTION_EXPR;
-      if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
+      if (!parens.require_close (parser))
 	{
 	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
 	  goto error;
diff --git a/gcc/c/c-parser.h b/gcc/c/c-parser.h
index 1e344c4..01a7b72 100644
--- a/gcc/c/c-parser.h
+++ b/gcc/c/c-parser.h
@@ -136,11 +136,13 @@  extern c_token * c_parser_peek_token (c_parser *parser);
 extern c_token * c_parser_peek_2nd_token (c_parser *parser);
 extern c_token * c_parser_peek_nth_token (c_parser *parser, unsigned int n);
 extern bool c_parser_require (c_parser *parser, enum cpp_ttype type,
-			      const char *msgid);
-extern void c_parser_error (c_parser *parser, const char *gmsgid);
+			      const char *msgid,
+			      location_t matching_location = UNKNOWN_LOCATION);
+extern bool c_parser_error (c_parser *parser, const char *gmsgid);
 extern void c_parser_consume_token (c_parser *parser);
 extern void c_parser_skip_until_found (c_parser *parser, enum cpp_ttype type,
-				       const char *msgid);
+				       const char *msgid,
+				       location_t = UNKNOWN_LOCATION);
 extern bool c_parser_next_token_starts_declspecs (c_parser *parser);
 bool c_parser_next_tokens_start_declaration (c_parser *parser);
 bool c_token_starts_typename (c_token *token);
diff --git a/gcc/testsuite/gcc.dg/unclosed-init.c b/gcc/testsuite/gcc.dg/unclosed-init.c
new file mode 100644
index 0000000..c0e4dd8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/unclosed-init.c
@@ -0,0 +1,3 @@ 
+int unclosed[] = { /* { dg-message "18: to match this '.'" } */
+  42
+ /* { dg-error "0: expected '.' at end of input" } */