matching tokens: C++ parts (v3)

Submitted by David Malcolm on Aug. 8, 2017, 9:01 p.m.

Details

Message ID 1502226094-9282-1-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm Aug. 8, 2017, 9:01 p.m.
On Mon, 2017-08-07 at 14:25 -0400, Jason Merrill wrote:

Thanks for looking at this.

> On 08/01/2017 04:21 PM, David Malcolm wrote:
> > @@ -27632,6 +27769,9 @@ cp_parser_sizeof_operand (cp_parser*
> > parser, enum rid keyword)
> >      {
> >        tree type = NULL_TREE;
> >
> > +      matching_parens parens;
> > +      parens.peek_open (parser);
>
> I was puzzled by this until I found that
> cp_parser_compound_literal_p
> consumes the open paren.  Let's remove that in favor of calling
> consume_open here, so we don't need peek_open anymore.

Done.

> 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.

Thanks; I'll keep "parser" out of the new classes then.

Here's an updated "v3" patch.

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu in conjunction
with the other patches (1 and 2 of the v2 kit).

OK for trunk, assuming the other patches are approved? (patch 2 in the kit,
for the C frontend, still needs approval).

Changes in v3:

Here's a ChangeLog for the change relative to the previous v2 patch:

gcc/cp/ChangeLog:
	* parser.c (token_pair::require_open): Use tabs rather than spaces.
	(token_pair::peek_open): Delete.
	(token_pair::require_close): Use tabs rather than spaces.
	(cp_parser_compound_literal_p): Remove consumption of opening
	paren.
	(cp_parser_postfix_expression): Add matching_parens instance.  Use
	it to consume the opening paren previously consumed by
	cp_parser_compound_literal_p.  Convert call to cp_parser_require
	to parens.require_close.
	(cp_parser_sizeof_operand): Convert call to parens.peek_open to
	call to consume_open to consume the opening paren previously
	consumed by cp_parser_compound_literal_p.

Here's an integrated ChangeLog for the patch as a whole:

gcc/cp/ChangeLog:
	* parser.c (cp_parser_error): Update for new param to
	c_parse_error.
	(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.
	(cp_parser_statement_expr): 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.
	(cp_parser_primary_expression): Likewise.
	(cp_parser_compound_literal_p): Remove consumption of opening
	paren.
	(cp_parser_postfix_expression): Convert explicit parsing of
	CPP_OPEN_PAREN and CPP_CLOSE_PAREN to use matching parens, as
	above.  Use it to consume the opening paren previously consumed by
	cp_parser_compound_literal_p.
	(cp_parser_parenthesized_expression_list): Likewise.
	(cp_parser_unary_expression): Likewise.
	(cp_parser_new_expression): Likewise.
	(cp_parser_cast_expression): Likewise.
	(cp_parser_builtin_offsetof): Likewise.
	(cp_parser_trait_expr): Likewise.
	(cp_parser_lambda_declarator_opt): Likewise.
	(cp_parser_lambda_body): Likewise, for matching_braces.
	(cp_parser_compound_statement): Likewise.
	(cp_parser_selection_statement): Likewise, for matching_parens.
	(cp_parser_iteration_statement): Likewise.
	(cp_parser_already_scoped_statement): Likewise, for
	matching_braces.
	(cp_parser_linkage_specification): Likewise.
	(cp_parser_static_assert): Likewise, for matching_parens.
	(cp_parser_decltype): Likewise.
	(cp_parser_operator): Likewise.
	(cp_parser_enum_specifier): Likewise.
	(cp_parser_namespace_definition): Likewise.
	(cp_parser_direct_declarator): Likewise.
	(cp_parser_braced_list): Likewise.
	(cp_parser_class_specifier_1): Likewise, for matching_braces.
	(cp_parser_constant_initializer): Likewise.
	(cp_parser_noexcept_specification_opt): Likewise, for
	matching_parens.
	(cp_parser_exception_specification_opt): Likewise.
	(cp_parser_handler): Likewise.
	(cp_parser_asm_specification_opt): Likewise.
	(cp_parser_asm_operand_list): Likewise.
	(cp_parser_gnu_attributes_opt): Likewise.
	(cp_parser_std_attribute_spec): Likewise.
	(cp_parser_requirement_parameter_list): Likewise.
	(cp_parser_requirement_body): Likewise, for matching_braces.
	(cp_parser_compound_requirement): Likewise.
	(cp_parser_template_introduction): Likewise.
	(cp_parser_sizeof_pack): Likewise, for matching_parens.
	(cp_parser_sizeof_operand): Likewise; use it to consume the
	opening paren previously consumed by cp_parser_compound_literal_p.
	(get_matching_symbol): New function.
	(cp_parser_required_error): Add param "matching_location".  Remove
	calls to cp_parser_error, instead setting a non-NULL gmsgid, and
	handling it if set by calling c_parse_error, potentially with a
	secondary location if matching_location was set.
	(cp_parser_require): Add param "matching_location", with a default
	value of UNKNOWN_LOCATION.
	(cp_parser_require_keyword): Update for new param of
	cp_parser_required_error.
	(cp_parser_objc_encode_expression): Update to class matching_parens
	as above.
	(cp_parser_objc_defs_expression): Likewise.
	(cp_parser_objc_protocol_expression): Likewise.
	(cp_parser_objc_selector_expression): Likewise.
	(cp_parser_objc_typename): Likewise.
	(cp_parser_objc_superclass_or_category): Likewise.
	(cp_parser_objc_try_catch_finally_statement): Likewise.
	(cp_parser_objc_synchronized_statement): Likewise.
	(cp_parser_objc_at_property_declaration): Likewise.
	(cp_parser_oacc_single_int_clause): Likewise.
	(cp_parser_oacc_shape_clause): Likewise.
	(cp_parser_omp_clause_collapse): Likewise.
	(cp_parser_omp_clause_default): Likewise.
	(cp_parser_omp_clause_final): Likewise.
	(cp_parser_omp_clause_if): Likewise.
	(cp_parser_omp_clause_num_threads): Likewise.
	(cp_parser_omp_clause_num_tasks): Likewise.
	(cp_parser_omp_clause_grainsize): Likewise.
	(cp_parser_omp_clause_priority): Likewise.
	(cp_parser_omp_clause_hint): Likewise.
	(cp_parser_omp_clause_defaultmap): Likewise.
	(cp_parser_omp_clause_ordered): Likewise.
	(cp_parser_omp_clause_schedule): Likewise.
	(cp_parser_omp_clause_num_teams): Likewise.
	(cp_parser_omp_clause_thread_limit): Likewise.
	(cp_parser_omp_clause_aligned): Likewise.
	(cp_parser_omp_clause_linear): Likewise.
	(cp_parser_omp_clause_safelen): Likewise.
	(cp_parser_omp_clause_simdlen): Likewise.
	(cp_parser_omp_clause_depend): Likewise.
	(cp_parser_omp_clause_device): Likewise.
	(cp_parser_omp_clause_dist_schedule): Likewise.
	(cp_parser_oacc_clause_async): Likewise.
	(cp_parser_omp_critical): Likewise.
	(cp_parser_omp_for_loop): Likewise.
	(cp_parser_omp_sections_scope): Likewise.
	(cp_parser_omp_declare_reduction_exprs): Likewise.
	Update for new param to cp_parser_required_error.
	(cp_parser_oacc_routine): Likewise.
	(cp_parser_transaction_expression): Likewise.
	(cp_parser_cilk_simd_vectorlength): Likewise.

gcc/testsuite/ChangeLog:
	* g++.dg/diagnostic/unclosed-extern-c.C: New test case.
	* g++.dg/diagnostic/unclosed-function.C: New test case.
	* g++.dg/diagnostic/unclosed-namespace.C: New test case.
	* g++.dg/diagnostic/unclosed-struct.C: New test case.
	* g++.dg/parse/pragma2.C: Update to reflect movement of the
	"expected identifier" error.
---
 gcc/cp/parser.c                                    | 811 +++++++++++++--------
 .../g++.dg/diagnostic/unclosed-extern-c.C          |   3 +
 .../g++.dg/diagnostic/unclosed-function.C          |   3 +
 .../g++.dg/diagnostic/unclosed-namespace.C         |   2 +
 gcc/testsuite/g++.dg/diagnostic/unclosed-struct.C  |   3 +
 gcc/testsuite/g++.dg/parse/pragma2.C               |   4 +-
 6 files changed, 534 insertions(+), 292 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/unclosed-function.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/unclosed-namespace.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/unclosed-struct.C

Comments

Jason Merrill Aug. 9, 2017, 7:25 p.m.
On Tue, Aug 8, 2017 at 5:01 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> On Mon, 2017-08-07 at 14:25 -0400, Jason Merrill wrote:
>
> Thanks for looking at this.
>
>> On 08/01/2017 04:21 PM, David Malcolm wrote:
>> > @@ -27632,6 +27769,9 @@ cp_parser_sizeof_operand (cp_parser*
>> > parser, enum rid keyword)
>> >      {
>> >        tree type = NULL_TREE;
>> >
>> > +      matching_parens parens;
>> > +      parens.peek_open (parser);
>>
>> I was puzzled by this until I found that
>> cp_parser_compound_literal_p
>> consumes the open paren.  Let's remove that in favor of calling
>> consume_open here, so we don't need peek_open anymore.
>
> Done.
>
>> 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.
>
> Thanks; I'll keep "parser" out of the new classes then.
>
> Here's an updated "v3" patch.
>
> Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu in conjunction
> with the other patches (1 and 2 of the v2 kit).
>
> OK for trunk, assuming the other patches are approved? (patch 2 in the kit,
> for the C frontend, still needs approval).

OK.

Jason

Patch hide | download patch | download mbox

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 23bd278..0da92ab 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2551,9 +2551,9 @@  static void set_and_check_decl_spec_loc
 static bool cp_parser_friend_p
   (const cp_decl_specifier_seq *);
 static void cp_parser_required_error
-  (cp_parser *, required_token, bool);
+  (cp_parser *, required_token, bool, location_t);
 static cp_token *cp_parser_require
-  (cp_parser *, enum cpp_ttype, required_token);
+  (cp_parser *, enum cpp_ttype, required_token, location_t = UNKNOWN_LOCATION);
 static cp_token *cp_parser_require_keyword
   (cp_parser *, enum rid, required_token);
 static bool cp_parser_token_starts_function_definition_p
@@ -2804,12 +2804,13 @@  cp_parser_error (cp_parser* parser, const char* gmsgid)
 	    }
 	}
 
+      rich_location richloc (line_table, input_location);
       c_parse_error (gmsgid,
 		     /* Because c_parser_error does not understand
 			CPP_KEYWORD, keywords are treated like
 			identifiers.  */
 		     (token->type == CPP_KEYWORD ? CPP_NAME : token->type),
-		     token->u.value, token->flags);
+		     token->u.value, token->flags, &richloc);
     }
 }
 
@@ -4503,6 +4504,90 @@  struct tentative_firewall
   }
 };
 
+/* 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 (cp_parser *parser)
+  {
+    m_open_loc = cp_lexer_peek_token (parser->lexer)->location;
+    return cp_parser_require (parser, traits_t::open_token_type,
+			      traits_t::required_token_open);
+  }
+
+  /* Consume the next token from PARSER, recording its location as
+     that of the opening token within the pair.  */
+
+  cp_token * consume_open (cp_parser *parser)
+  {
+    cp_token *tok = cp_lexer_consume_token (parser->lexer);
+    gcc_assert (tok->type == traits_t::open_token_type);
+    m_open_loc = tok->location;
+    return tok;
+  }
+
+  /* If the next token is the closing symbol for this pair, consume it
+     and return it.
+     Otherwise, issue an error, highlighting the location of the
+     corresponding opening token, and return NULL.  */
+
+  cp_token *require_close (cp_parser *parser) const
+  {
+    return cp_parser_require (parser, traits_t::close_token_type,
+			      traits_t::required_token_close,
+			      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 enum required_token required_token_open  = RT_OPEN_PAREN;
+  static const enum cpp_ttype close_token_type = CPP_CLOSE_PAREN;
+  static const enum required_token required_token_close = RT_CLOSE_PAREN;
+};
+
+/* "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 enum required_token required_token_open = RT_OPEN_BRACE;
+  static const enum cpp_ttype close_token_type = CPP_CLOSE_BRACE;
+  static const enum required_token required_token_close = RT_CLOSE_BRACE;
+};
+
+/* "matching_braces" is a token_pair<T> class for tracking matching
+   pairs of braces.  */
+
+typedef token_pair<matching_brace_traits> matching_braces;
+
+
 /* Parse a GNU statement-expression, i.e. ({ stmts }), except for the
    enclosing parentheses.  */
 
@@ -4513,7 +4598,8 @@  cp_parser_statement_expr (cp_parser *parser)
 
   /* Consume the '('.  */
   location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
-  cp_lexer_consume_token (parser->lexer);
+  matching_parens parens;
+  parens.consume_open (parser);
   /* Start the statement-expression.  */
   tree expr = begin_stmt_expr ();
   /* Parse the compound-statement.  */
@@ -4522,7 +4608,7 @@  cp_parser_statement_expr (cp_parser *parser)
   expr = finish_stmt_expr (expr, false);
   /* Consume the ')'.  */
   location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location;
-  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+  if (!parens.require_close (parser))
     cp_parser_skip_to_end_of_statement (parser);
 
   cp_parser_end_tentative_firewall (parser, start, expr);
@@ -4928,7 +5014,8 @@  cp_parser_primary_expression (cp_parser *parser,
 	location_t open_paren_loc = token->location;
 
 	/* Consume the `('.  */
-	cp_lexer_consume_token (parser->lexer);
+	matching_parens parens;
+	parens.consume_open (parser);
 	/* Within a parenthesized expression, a `>' token is always
 	   the greater-than operator.  */
 	saved_greater_than_is_operator_p
@@ -4976,7 +5063,7 @@  cp_parser_primary_expression (cp_parser *parser,
 	token = cp_lexer_peek_token (parser->lexer);
 	location_t close_paren_loc = token->location;
 	expr.set_range (open_paren_loc, close_paren_loc);
-	if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
+	if (!parens.require_close (parser)
 	    && !cp_parser_uncommitted_to_tentative_parse_p (parser))
 	  cp_parser_skip_to_end_of_statement (parser);
 
@@ -5098,7 +5185,8 @@  cp_parser_primary_expression (cp_parser *parser,
 	       `va_arg'.  Consume the `__builtin_va_arg' token.  */
 	    cp_lexer_consume_token (parser->lexer);
 	    /* Look for the opening `('.  */
-	    cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+	    matching_parens parens;
+	    parens.require_open (parser);
 	    /* Now, parse the assignment-expression.  */
 	    expression = cp_parser_assignment_expression (parser);
 	    /* Look for the `,'.  */
@@ -5112,7 +5200,7 @@  cp_parser_primary_expression (cp_parser *parser,
 	    /* Look for the closing `)'.  */
 	    location_t finish_loc
 	      = cp_lexer_peek_token (parser->lexer)->location;
-	    cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	    parens.require_close (parser);
 	    /* Using `va_arg' in a constant-expression is not
 	       allowed.  */
 	    if (cp_parser_non_integral_constant_expression (parser,
@@ -6345,9 +6433,6 @@  cp_parser_qualifying_entity (cp_parser *parser,
 static bool
 cp_parser_compound_literal_p (cp_parser *parser)
 {
-  /* Consume the `('.  */
-  cp_lexer_consume_token (parser->lexer);
-
   cp_lexer_save_tokens (parser->lexer);
 
   /* Skip tokens until the next token is a closing parenthesis.
@@ -6465,7 +6550,8 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	parser->greater_than_is_operator_p = true;
 
 	/* And the expression which is being cast.  */
-	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+	matching_parens parens;
+	parens.require_open (parser);
 	expression = cp_parser_expression (parser, & idk, /*cast_p=*/true);
 	cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN,
 						   RT_CLOSE_PAREN);
@@ -6526,7 +6612,8 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* Consume the `typeid' token.  */
 	cp_lexer_consume_token (parser->lexer);
 	/* Look for the `(' token.  */
-	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+	matching_parens parens;
+	parens.require_open (parser);
 	/* Types cannot be defined in a `typeid' expression.  */
 	saved_message = parser->type_definition_forbidden_message;
 	parser->type_definition_forbidden_message
@@ -6542,8 +6629,7 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* Look for the `)' token.  Otherwise, we can't be sure that
 	   we're not looking at an expression: consider `typeid (int
 	   (3))', for example.  */
-	cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN,
-						   RT_CLOSE_PAREN);
+	cp_token *close_paren = parens.require_close (parser);
 	/* If all went well, simply lookup the type-id.  */
 	if (cp_parser_parse_definitely (parser))
 	  postfix_expression = get_typeid (type, tf_warning_or_error);
@@ -6557,8 +6643,7 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    /* Compute its typeid.  */
 	    postfix_expression = build_typeid (expression, tf_warning_or_error);
 	    /* Look for the `)' token.  */
-	    close_paren
-	      = cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	    close_paren = parens.require_close (parser);
 	  }
 	/* Restore the saved message.  */
 	parser->type_definition_forbidden_message = saved_message;
@@ -6759,6 +6844,9 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 
 	    cp_parser_parse_tentatively (parser);
 
+	    matching_parens parens;
+	    parens.consume_open (parser);
+
 	    /* Avoid calling cp_parser_type_id pointlessly, see comment
 	       in cp_parser_cast_expression about c++/29234.  */
 	    if (!cp_parser_compound_literal_p (parser))
@@ -6770,8 +6858,7 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 		parser->in_type_id_in_expr_p = true;
 		type = cp_parser_type_id (parser);
 		parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
-		/* Look for the `)'.  */
-		cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+		parens.require_close (parser);
 	      }
 
 	    /* If things aren't going well, there's no need to
@@ -7561,7 +7648,8 @@  cp_parser_parenthesized_expression_list (cp_parser* parser,
   if (non_constant_p)
     *non_constant_p = false;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return NULL;
 
   expression_list = make_tree_vector ();
@@ -7657,7 +7745,7 @@  cp_parser_parenthesized_expression_list (cp_parser* parser,
   if (close_paren_loc)
     *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location;
 
-  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+  if (!parens.require_close (parser))
     {
       int ending;
 
@@ -7957,7 +8045,8 @@  cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	    bool saved_greater_than_is_operator_p;
 
 	    cp_lexer_consume_token (parser->lexer);
-	    cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+	    matching_parens parens;
+	    parens.require_open (parser);
 
 	    saved_message = parser->type_definition_forbidden_message;
 	    parser->type_definition_forbidden_message
@@ -7991,7 +8080,7 @@  cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 
 	    parser->type_definition_forbidden_message = saved_message;
 
-	    cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	    parens.require_close (parser);
 	    return finish_noexcept_expr (expr, tf_warning_or_error);
 	  }
 
@@ -8235,7 +8324,8 @@  cp_parser_new_expression (cp_parser* parser)
       const char *saved_message = parser->type_definition_forbidden_message;
 
       /* Consume the `('.  */
-      cp_lexer_consume_token (parser->lexer);
+      matching_parens parens;
+      parens.consume_open (parser);
 
       /* Parse the type-id.  */
       parser->type_definition_forbidden_message
@@ -8247,7 +8337,7 @@  cp_parser_new_expression (cp_parser* parser)
       parser->type_definition_forbidden_message = saved_message;
 
       /* Look for the closing `)'.  */
-      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+      parens.require_close (parser);
       token = cp_lexer_peek_token (parser->lexer);
       /* There should not be a direct-new-declarator in this production,
 	 but GCC used to allowed this, so we check and emit a sensible error
@@ -8787,7 +8877,8 @@  cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
       parser->type_definition_forbidden_message
 	= G_("types may not be defined in casts");
       /* Consume the `('.  */
-      cp_token *open_paren = cp_lexer_consume_token (parser->lexer);
+      matching_parens parens;
+      cp_token *open_paren = parens.consume_open (parser);
       location_t open_paren_loc = open_paren->location;
       location_t close_paren_loc = UNKNOWN_LOCATION;
 
@@ -8852,8 +8943,7 @@  cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  /* Look for the type-id.  */
 	  type = cp_parser_type_id (parser);
 	  /* Look for the closing `)'.  */
-	  cp_token *close_paren
-	    = cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	  cp_token *close_paren = parens.require_close (parser);
 	  if (close_paren)
 	    close_paren_loc = close_paren->location;
 	  parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
@@ -9638,7 +9728,8 @@  cp_parser_builtin_offsetof (cp_parser *parser)
   /* Consume the "__builtin_offsetof" token.  */
   cp_lexer_consume_token (parser->lexer);
   /* Consume the opening `('.  */
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
   /* Parse the type-id.  */
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
   type = cp_parser_type_id (parser);
@@ -9688,7 +9779,7 @@  cp_parser_builtin_offsetof (cp_parser *parser)
 	default:
 	  /* Error.  We know the following require will fail, but
 	     that gives the proper error message.  */
-	  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	  parens.require_close (parser);
 	  cp_parser_skip_to_closing_parenthesis (parser, true, false, true);
 	  expr = error_mark_node;
 	  goto failure;
@@ -9834,7 +9925,8 @@  cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
   /* Consume the token.  */
   cp_lexer_consume_token (parser->lexer);
 
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
 
   {
     type_id_in_expr_sentinel s (parser);
@@ -9873,7 +9965,7 @@  cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
 	}
     }
 
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  parens.require_close (parser);
 
   /* Complete the trait expression, which may mean either processing
      the trait expr now or saving it for template instantiation.  */
@@ -10354,7 +10446,8 @@  cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
      opening parenthesis if present.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
-      cp_lexer_consume_token (parser->lexer);
+      matching_parens parens;
+      parens.consume_open (parser);
 
       begin_scope (sk_function_parms, /*entity=*/NULL_TREE);
 
@@ -10369,7 +10462,7 @@  cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
 	    pedwarn (DECL_SOURCE_LOCATION (TREE_VALUE (t)), OPT_Wpedantic,
 		     "default argument specified for lambda parameter");
 
-      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+      parens.require_close (parser);
 
       attributes = cp_parser_attributes_opt (parser);
 
@@ -10526,7 +10619,8 @@  cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
     start_lambda_scope (fco);
     body = begin_function_body ();
 
-    if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+    matching_braces braces;
+    if (!braces.require_open (parser))
       goto out;
 
     /* Push the proxies for any explicit captures.  */
@@ -10567,7 +10661,7 @@  cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
 	expr = cp_parser_expression (parser, &idk);
 
 	cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
-	cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+	braces.require_close (parser);
 
 	if (cp_parser_parse_definitely (parser))
 	  {
@@ -10591,7 +10685,7 @@  cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
 	while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
 	  cp_parser_label_declaration (parser);
 	cp_parser_statement_seq_opt (parser, NULL_TREE);
-	cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+	braces.require_close (parser);
       }
 
     finish_compound_stmt (compound_stmt);
@@ -11127,9 +11221,10 @@  cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr,
 			      int bcs_flags, bool function_body)
 {
   tree compound_stmt;
+  matching_braces braces;
 
   /* Consume the `{'.  */
-  if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+  if (!braces.require_open (parser))
     return error_mark_node;
   if (DECL_DECLARED_CONSTEXPR_P (current_function_decl)
       && !function_body && cxx_dialect < cxx14)
@@ -11145,7 +11240,7 @@  cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr,
   /* Finish the compound-statement.  */
   finish_compound_stmt (compound_stmt);
   /* Consume the `}'.  */
-  cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+  braces.require_close (parser);
 
   return compound_stmt;
 }
@@ -11266,7 +11361,8 @@  cp_parser_selection_statement (cp_parser* parser, bool *if_p,
 	  }
 
 	/* Look for the `('.  */
-	if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+	matching_parens parens;
+	if (!parens.require_open (parser))
 	  {
 	    cp_parser_skip_to_end_of_statement (parser);
 	    return error_mark_node;
@@ -11295,7 +11391,7 @@  cp_parser_selection_statement (cp_parser* parser, bool *if_p,
 	/* Parse the condition.  */
 	condition = cp_parser_condition (parser);
 	/* Look for the `)'.  */
-	if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+	if (!parens.require_close (parser))
 	  cp_parser_skip_to_closing_parenthesis (parser, true, false,
 						 /*consume_paren=*/true);
 
@@ -12082,12 +12178,13 @@  cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep)
 	/* Begin the while-statement.  */
 	statement = begin_while_stmt ();
 	/* Look for the `('.  */
-	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+	matching_parens parens;
+	parens.require_open (parser);
 	/* Parse the condition.  */
 	condition = cp_parser_condition (parser);
 	finish_while_stmt_cond (condition, statement, ivdep);
 	/* Look for the `)'.  */
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	parens.require_close (parser);
 	/* Parse the dependent statement.  */
 	parser->in_statement = IN_ITERATION_STMT;
 	cp_parser_already_scoped_statement (parser, if_p, guard_tinfo);
@@ -12111,13 +12208,14 @@  cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep)
 	/* Look for the `while' keyword.  */
 	cp_parser_require_keyword (parser, RID_WHILE, RT_WHILE);
 	/* Look for the `('.  */
-	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+	matching_parens parens;
+	parens.require_open (parser);
 	/* Parse the expression.  */
 	expression = cp_parser_expression (parser);
 	/* We're done with the do-statement.  */
 	finish_do_stmt (expression, statement, ivdep);
 	/* Look for the `)'.  */
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	parens.require_close (parser);
 	/* Look for the `;'.  */
 	cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
       }
@@ -12126,12 +12224,13 @@  cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep)
     case RID_FOR:
       {
 	/* Look for the `('.  */
-	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+	matching_parens parens;
+	parens.require_open (parser);
 
 	statement = cp_parser_for (parser, ivdep);
 
 	/* Look for the `)'.  */
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	parens.require_close (parser);
 
 	/* Parse the body of the for-statement.  */
 	parser->in_statement = IN_ITERATION_STMT;
@@ -12481,13 +12580,14 @@  cp_parser_already_scoped_statement (cp_parser* parser, bool *if_p,
     {
       /* Avoid calling cp_parser_compound_statement, so that we
 	 don't create a new scope.  Do everything else by hand.  */
-      cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE);
+      matching_braces braces;
+      braces.require_open (parser);
       /* If the next keyword is `__label__' we have a label declaration.  */
       while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
 	cp_parser_label_declaration (parser);
       /* Parse an (optional) statement-seq.  */
       cp_parser_statement_seq_opt (parser, NULL_TREE);
-      cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+      braces.require_close (parser);
     }
 }
 
@@ -13714,11 +13814,12 @@  cp_parser_linkage_specification (cp_parser* parser)
       cp_ensure_no_oacc_routine (parser);
 
       /* Consume the `{' token.  */
-      cp_lexer_consume_token (parser->lexer);
+      matching_braces braces;
+      braces.consume_open (parser)->location;
       /* Parse the declarations.  */
       cp_parser_declaration_seq_opt (parser);
       /* Look for the closing `}'.  */
-      cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+      braces.require_close (parser);
     }
   /* Otherwise, there's just one declaration.  */
   else
@@ -13770,7 +13871,8 @@  cp_parser_static_assert(cp_parser *parser, bool member_p)
     cp_parser_commit_to_tentative_parse (parser);
 
   /* Parse the `(' starting the static assertion condition.  */
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
 
   /* Parse the constant-expression.  Allow a non-constant expression
      here in order to give better diagnostics in finish_static_assert.  */
@@ -13802,7 +13904,7 @@  cp_parser_static_assert(cp_parser *parser, bool member_p)
                                 	  /*wide_ok=*/true);
 
       /* A `)' completes the static assertion.  */
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	cp_parser_skip_to_closing_parenthesis (parser, 
                                                /*recovering=*/true, 
                                                /*or_comma=*/false,
@@ -13956,7 +14058,8 @@  cp_parser_decltype (cp_parser *parser)
     return error_mark_node;
 
   /* Parse the opening `('.  */
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return error_mark_node;
 
   /* decltype (auto) */
@@ -13964,7 +14067,7 @@  cp_parser_decltype (cp_parser *parser)
       && cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO))
     {
       cp_lexer_consume_token (parser->lexer);
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	return error_mark_node;
       expr = make_decltype_auto ();
       AUTO_IS_DECLTYPE (expr) = true;
@@ -14019,7 +14122,7 @@  cp_parser_decltype (cp_parser *parser)
     = saved_non_integral_constant_expression_p;
 
   /* Parse to the closing `)'.  */
-  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+  if (!parens.require_close (parser))
     {
       cp_parser_skip_to_closing_parenthesis (parser, true, false,
 					     /*consume_paren=*/true);
@@ -14718,11 +14821,14 @@  cp_parser_operator (cp_parser* parser)
       break;
 
     case CPP_OPEN_PAREN:
-      /* Consume the `('.  */
-      cp_lexer_consume_token (parser->lexer);
-      /* Look for the matching `)'.  */
-      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
-      return cp_operator_id (CALL_EXPR);
+      {
+        /* Consume the `('.  */
+        matching_parens parens;
+        parens.consume_open (parser);
+        /* Look for the matching `)'.  */
+        parens.require_close (parser);
+        return cp_operator_id (CALL_EXPR);
+      }
 
     case CPP_OPEN_SQUARE:
       /* Consume the `['.  */
@@ -18095,7 +18201,8 @@  cp_parser_enum_specifier (cp_parser* parser)
 	begin_scope (sk_scoped_enum, type);
 
       /* Consume the opening brace.  */
-      cp_lexer_consume_token (parser->lexer);
+      matching_braces braces;
+      braces.consume_open (parser);
 
       if (type == error_mark_node)
 	; /* Nothing to add */
@@ -18128,7 +18235,7 @@  cp_parser_enum_specifier (cp_parser* parser)
 	cp_parser_enumerator_list (parser, type);
 
       /* Consume the final '}'.  */
-      cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+      braces.require_close (parser);
 
       if (scoped_enum_p)
 	finish_scope ();
@@ -18421,13 +18528,14 @@  cp_parser_namespace_definition (cp_parser* parser)
   warning  (OPT_Wnamespaces, "namespace %qD entered", current_namespace);
 
   /* Look for the `{' to validate starting the namespace.  */
-  if (cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+  matching_braces braces;
+  if (braces.require_open (parser))
     {
       /* Parse the body of the namespace.  */
       cp_parser_namespace_body (parser);
 
       /* Look for the final `}'.  */
-      cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+      braces.require_close (parser);
     }
 
   if (has_visibility)
@@ -19808,7 +19916,8 @@  cp_parser_direct_declarator (cp_parser* parser,
 		cp_parser_parse_tentatively (parser);
 
 	      /* Consume the `('.  */
-	      cp_lexer_consume_token (parser->lexer);
+	      matching_parens parens;
+	      parens.consume_open (parser);
 	      if (first)
 		{
 		  /* If this is going to be an abstract declarator, we're
@@ -19823,7 +19932,7 @@  cp_parser_direct_declarator (cp_parser* parser,
 	      params = cp_parser_parameter_declaration_clause (parser);
 
 	      /* Consume the `)'.  */
-	      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	      parens.require_close (parser);
 
 	      /* If all went well, parse the cv-qualifier-seq,
 		 ref-qualifier and the exception-specification.  */
@@ -19919,7 +20028,8 @@  cp_parser_direct_declarator (cp_parser* parser,
 	      parser->in_declarator_p = saved_in_declarator_p;
 
 	      /* Consume the `('.  */
-	      cp_lexer_consume_token (parser->lexer);
+	      matching_parens parens;
+	      parens.consume_open (parser);
 	      /* Parse the nested declarator.  */
 	      saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
 	      parser->in_type_id_in_expr_p = true;
@@ -19930,7 +20040,7 @@  cp_parser_direct_declarator (cp_parser* parser,
 	      parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
 	      first = false;
 	      /* Expect a `)'.  */
-	      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+	      if (!parens.require_close (parser))
 		declarator = cp_error_declarator;
 	      if (declarator == cp_error_declarator)
 		break;
@@ -21778,7 +21888,8 @@  cp_parser_braced_list (cp_parser* parser, bool* non_constant_p)
   location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
 
   /* Consume the `{' token.  */
-  cp_lexer_consume_token (parser->lexer);
+  matching_braces braces;
+  braces.consume_open (parser);
   /* Create a CONSTRUCTOR to represent the braced-initializer.  */
   initializer = make_node (CONSTRUCTOR);
   /* If it's not a `}', then there is a non-trivial initializer.  */
@@ -21795,7 +21906,7 @@  cp_parser_braced_list (cp_parser* parser, bool* non_constant_p)
     *non_constant_p = false;
   /* Now, there should be a trailing `}'.  */
   location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location;
-  cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+  braces.require_close (parser);
   TREE_TYPE (initializer) = init_list_type_node;
 
   cp_expr result (initializer);
@@ -22222,7 +22333,8 @@  cp_parser_class_specifier_1 (cp_parser* parser)
     }
 
   /* Look for the `{'.  */
-  if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+  matching_braces braces;
+  if (!braces.require_open (parser))
     {
       pop_deferring_access_checks ();
       return error_mark_node;
@@ -22274,7 +22386,7 @@  cp_parser_class_specifier_1 (cp_parser* parser)
     cp_parser_member_specification_opt (parser);
 
   /* Look for the trailing `}'.  */
-  closing_brace = cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+  closing_brace = braces.require_close (parser);
   /* Look for trailing attributes to apply to this class.  */
   if (cp_parser_allow_gnu_extensions_p (parser))
     attributes = cp_parser_gnu_attributes_opt (parser);
@@ -23753,11 +23865,12 @@  cp_parser_constant_initializer (cp_parser* parser)
       cp_parser_error (parser,
 		       "a brace-enclosed initializer is not allowed here");
       /* Consume the opening brace.  */
-      cp_lexer_consume_token (parser->lexer);
+      matching_braces braces;
+      braces.consume_open (parser);
       /* Skip the initializer.  */
       cp_parser_skip_to_closing_brace (parser);
       /* Look for the trailing `}'.  */
-      cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+      braces.require_close (parser);
 
       return error_mark_node;
     }
@@ -24013,7 +24126,8 @@  cp_parser_noexcept_specification_opt (cp_parser* parser,
 
       if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
 	{
-	  cp_lexer_consume_token (parser->lexer);
+	  matching_parens parens;
+	  parens.consume_open (parser);
 
 	  if (require_constexpr)
 	    {
@@ -24033,7 +24147,7 @@  cp_parser_noexcept_specification_opt (cp_parser* parser,
 	      *consumed_expr = true;
 	    }
 
-	  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	  parens.require_close (parser);
 	}
       else
 	{
@@ -24087,7 +24201,8 @@  cp_parser_exception_specification_opt (cp_parser* parser)
   cp_lexer_consume_token (parser->lexer);
 
   /* Look for the `('.  */
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
@@ -24123,7 +24238,7 @@  cp_parser_exception_specification_opt (cp_parser* parser)
     type_id_list = empty_except_spec;
 
   /* Look for the `)'.  */
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  parens.require_close (parser);
 
   return type_id_list;
 }
@@ -24270,10 +24385,11 @@  cp_parser_handler (cp_parser* parser)
 
   cp_parser_require_keyword (parser, RID_CATCH, RT_CATCH);
   handler = begin_handler ();
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
   declaration = cp_parser_exception_declaration (parser);
   finish_handler_parms (declaration, handler);
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  parens.require_close (parser);
   cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false);
   finish_handler (handler);
 }
@@ -24389,13 +24505,14 @@  cp_parser_asm_specification_opt (cp_parser* parser)
   /* Consume the `asm' token.  */
   cp_lexer_consume_token (parser->lexer);
   /* Look for the `('.  */
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
 
   /* Look for the string-literal.  */
   asm_specification = cp_parser_string_literal (parser, false, false);
 
   /* Look for the `)'.  */
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  parens.require_close (parser);
 
   return asm_specification;
 }
@@ -24447,11 +24564,12 @@  cp_parser_asm_operand_list (cp_parser* parser)
       string_literal = cp_parser_string_literal (parser, false, false);
 
       /* Look for the `('.  */
-      cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+      matching_parens parens;
+      parens.require_open (parser);
       /* Parse the expression.  */
       expression = cp_parser_expression (parser);
       /* Look for the `)'.  */
-      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+      parens.require_close (parser);
 
       if (name == error_mark_node 
 	  || string_literal == error_mark_node 
@@ -24705,8 +24823,10 @@  cp_parser_gnu_attributes_opt (cp_parser* parser)
       /* Consume the `__attribute__' keyword.  */
       cp_lexer_consume_token (parser->lexer);
       /* Look for the two `(' tokens.  */
-      cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
-      cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+      matching_parens outer_parens;
+      outer_parens.require_open (parser);
+      matching_parens inner_parens;
+      inner_parens.require_open (parser);
 
       /* Peek at the next token.  */
       token = cp_lexer_peek_token (parser->lexer);
@@ -24719,9 +24839,9 @@  cp_parser_gnu_attributes_opt (cp_parser* parser)
 	attribute_list = NULL;
 
       /* Look for the two `)' tokens.  */
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!inner_parens.require_close (parser))
 	ok = false;
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!outer_parens.require_close (parser))
 	ok = false;
       if (!ok)
 	cp_parser_skip_to_end_of_statement (parser);
@@ -25148,7 +25268,8 @@  cp_parser_std_attribute_spec (cp_parser *parser)
       cp_lexer_consume_token (parser->lexer);
       maybe_warn_cpp0x (CPP0X_ATTRIBUTES);
 
-      if (cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN) == NULL)
+      matching_parens parens;
+      if (!parens.require_open (parser))
 	{
 	  cp_parser_error (parser, "expected %<(%>");
 	  return error_mark_node;
@@ -25181,7 +25302,7 @@  cp_parser_std_attribute_spec (cp_parser *parser)
       if (alignas_expr == error_mark_node)
 	return error_mark_node;
 
-      if (cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN) == NULL)
+      if (!parens.require_close (parser))
 	{
 	  cp_parser_error (parser, "expected %<)%>");
 	  return error_mark_node;
@@ -25413,12 +25534,13 @@  cp_parser_requires_expression (cp_parser *parser)
 static tree
 cp_parser_requirement_parameter_list (cp_parser *parser)
 {
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return error_mark_node;
 
   tree parms = cp_parser_parameter_declaration_clause (parser);
 
-  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+  if (!parens.require_close (parser))
     return error_mark_node;
 
   return parms;
@@ -25431,12 +25553,13 @@  cp_parser_requirement_parameter_list (cp_parser *parser)
 static tree
 cp_parser_requirement_body (cp_parser *parser)
 {
-  if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+  matching_braces braces;
+  if (!braces.require_open (parser))
     return error_mark_node;
 
   tree reqs = cp_parser_requirement_list (parser);
 
-  if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE))
+  if (!braces.require_close (parser))
     return error_mark_node;
 
   return reqs;
@@ -25575,14 +25698,15 @@  static tree
 cp_parser_compound_requirement (cp_parser *parser)
 {
   /* Parse an expression enclosed in '{ }'s. */
-  if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+  matching_braces braces;
+  if (!braces.require_open (parser))
     return error_mark_node;
 
   tree expr = cp_parser_expression (parser, NULL, false, false);
   if (!expr || expr == error_mark_node)
     return error_mark_node;
 
-  if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE))
+  if (!braces.require_close (parser))
     return error_mark_node;
 
   /* Parse the optional noexcept. */
@@ -26653,7 +26777,8 @@  cp_parser_template_introduction (cp_parser* parser, bool member_p)
     cp_parser_simulate_error (parser);
 
   /* Look for opening brace for introduction.  */
-  cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE);
+  matching_braces braces;
+  braces.require_open (parser);
 
   if (!cp_parser_parse_definitely (parser))
     return false;
@@ -26673,7 +26798,7 @@  cp_parser_template_introduction (cp_parser* parser, bool member_p)
     }
 
   /* Look for closing brace for introduction.  */
-  if (!cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE))
+  if (!braces.require_close (parser))
     return true;
 
   if (tmpl_decl == error_mark_node)
@@ -27558,9 +27683,10 @@  cp_parser_sizeof_pack (cp_parser *parser)
   cp_lexer_consume_token (parser->lexer);
   maybe_warn_variadic_templates ();
 
+  matching_parens parens;
   bool paren = cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN);
   if (paren)
-    cp_lexer_consume_token (parser->lexer);
+    parens.consume_open (parser);
   else
     permerror (cp_lexer_peek_token (parser->lexer)->location,
 	       "%<sizeof...%> argument must be surrounded by parentheses");
@@ -27585,7 +27711,7 @@  cp_parser_sizeof_pack (cp_parser *parser)
   PACK_EXPANSION_SIZEOF_P (expr) = true;
 
   if (paren)
-    cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+    parens.require_close (parser);
 
   return expr;
 }
@@ -27639,6 +27765,10 @@  cp_parser_sizeof_operand (cp_parser* parser, enum rid keyword)
       /* We can't be sure yet whether we're looking at a type-id or an
 	 expression.  */
       cp_parser_parse_tentatively (parser);
+
+      matching_parens parens;
+      parens.consume_open (parser);
+
       /* Note: as a GNU Extension, compound literals are considered
 	 postfix-expressions as they are in C99, so they are valid
 	 arguments to sizeof.  See comment in cp_parser_cast_expression
@@ -27652,7 +27782,7 @@  cp_parser_sizeof_operand (cp_parser* parser, enum rid keyword)
 	  /* Look for the type-id.  */
 	  type = cp_parser_type_id (parser);
 	  /* Look for the closing `)'.  */
-	  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	  parens.require_close (parser);
 	  parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
 	}
 
@@ -27934,187 +28064,245 @@  cp_parser_friend_p (const cp_decl_specifier_seq *decl_specifiers)
   return decl_spec_seq_has_spec_p (decl_specifiers, ds_friend);
 }
 
+/* Get a description of the matching symbol to TOKEN_DESC e.g. "(" for
+   RT_CLOSE_PAREN.  */
+
+static const char *
+get_matching_symbol (required_token token_desc)
+{
+  switch (token_desc)
+    {
+    default:
+      gcc_unreachable ();
+      return "";
+    case RT_CLOSE_BRACE:
+      return "{";
+    case RT_CLOSE_PAREN:
+      return "(";
+    }
+}
+
 /* Issue an error message indicating that TOKEN_DESC was expected.
    If KEYWORD is true, it indicated this function is called by
    cp_parser_require_keword and the required token can only be
-   a indicated keyword. */
+   a indicated keyword.
+
+   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 TOKEN_DESC is
+   RT_CLOSE_PAREN).  */
 
 static void
 cp_parser_required_error (cp_parser *parser,
 			  required_token token_desc,
-			  bool keyword)
+			  bool keyword,
+			  location_t matching_location)
 {
+  if (cp_parser_simulate_error (parser))
+    return;
+
+  const char *gmsgid = NULL;
   switch (token_desc)
     {
       case RT_NEW:
-	cp_parser_error (parser, "expected %<new%>");
-	return;
+	gmsgid = G_("expected %<new%>");
+	break;
       case RT_DELETE:
-	cp_parser_error (parser, "expected %<delete%>");
-	return;
+	gmsgid = G_("expected %<delete%>");
+	break;
       case RT_RETURN:
-	cp_parser_error (parser, "expected %<return%>");
-	return;
+	gmsgid = G_("expected %<return%>");
+	break;
       case RT_WHILE:
-	cp_parser_error (parser, "expected %<while%>");
-	return;
+	gmsgid = G_("expected %<while%>");
+	break;
       case RT_EXTERN:
-	cp_parser_error (parser, "expected %<extern%>");
-	return;
+	gmsgid = G_("expected %<extern%>");
+	break;
       case RT_STATIC_ASSERT:
-	cp_parser_error (parser, "expected %<static_assert%>");
-	return;
+	gmsgid = G_("expected %<static_assert%>");
+	break;
       case RT_DECLTYPE:
-	cp_parser_error (parser, "expected %<decltype%>");
-	return;
+	gmsgid = G_("expected %<decltype%>");
+	break;
       case RT_OPERATOR:
-	cp_parser_error (parser, "expected %<operator%>");
-	return;
+	gmsgid = G_("expected %<operator%>");
+	break;
       case RT_CLASS:
-	cp_parser_error (parser, "expected %<class%>");
-	return;
+	gmsgid = G_("expected %<class%>");
+	break;
       case RT_TEMPLATE:
-	cp_parser_error (parser, "expected %<template%>");
-	return;
+	gmsgid = G_("expected %<template%>");
+	break;
       case RT_NAMESPACE:
-	cp_parser_error (parser, "expected %<namespace%>");
-	return;
+	gmsgid = G_("expected %<namespace%>");
+	break;
       case RT_USING:
-	cp_parser_error (parser, "expected %<using%>");
-	return;
+	gmsgid = G_("expected %<using%>");
+	break;
       case RT_ASM:
-	cp_parser_error (parser, "expected %<asm%>");
-	return;
+	gmsgid = G_("expected %<asm%>");
+	break;
       case RT_TRY:
-	cp_parser_error (parser, "expected %<try%>");
-	return;
+	gmsgid = G_("expected %<try%>");
+	break;
       case RT_CATCH:
-	cp_parser_error (parser, "expected %<catch%>");
-	return;
+	gmsgid = G_("expected %<catch%>");
+	break;
       case RT_THROW:
-	cp_parser_error (parser, "expected %<throw%>");
-	return;
+	gmsgid = G_("expected %<throw%>");
+	break;
       case RT_LABEL:
-	cp_parser_error (parser, "expected %<__label__%>");
-	return;
+	gmsgid = G_("expected %<__label__%>");
+	break;
       case RT_AT_TRY:
-	cp_parser_error (parser, "expected %<@try%>");
-	return;
+	gmsgid = G_("expected %<@try%>");
+	break;
       case RT_AT_SYNCHRONIZED:
-	cp_parser_error (parser, "expected %<@synchronized%>");
-	return;
+	gmsgid = G_("expected %<@synchronized%>");
+	break;
       case RT_AT_THROW:
-	cp_parser_error (parser, "expected %<@throw%>");
-	return;
+	gmsgid = G_("expected %<@throw%>");
+	break;
       case RT_TRANSACTION_ATOMIC:
-	cp_parser_error (parser, "expected %<__transaction_atomic%>");
-	return;
+	gmsgid = G_("expected %<__transaction_atomic%>");
+	break;
       case RT_TRANSACTION_RELAXED:
-	cp_parser_error (parser, "expected %<__transaction_relaxed%>");
-	return;
+	gmsgid = G_("expected %<__transaction_relaxed%>");
+	break;
       default:
 	break;
     }
-  if (!keyword)
+
+  if (!gmsgid && !keyword)
     {
       switch (token_desc)
         {
 	  case RT_SEMICOLON:
-	    cp_parser_error (parser, "expected %<;%>");
-	    return;
+	    gmsgid = G_("expected %<;%>");
+	    break;
 	  case RT_OPEN_PAREN:
-	    cp_parser_error (parser, "expected %<(%>");
-	    return;
+	    gmsgid = G_("expected %<(%>");
+	    break;
 	  case RT_CLOSE_BRACE:
-	    cp_parser_error (parser, "expected %<}%>");
-	    return;
+	    gmsgid = G_("expected %<}%>");
+	    break;
 	  case RT_OPEN_BRACE:
-	    cp_parser_error (parser, "expected %<{%>");
-	    return;
+	    gmsgid = G_("expected %<{%>");
+	    break;
 	  case RT_CLOSE_SQUARE:
-	    cp_parser_error (parser, "expected %<]%>");
-	    return;
+	    gmsgid = G_("expected %<]%>");
+	    break;
 	  case RT_OPEN_SQUARE:
-	    cp_parser_error (parser, "expected %<[%>");
-	    return;
+	    gmsgid = G_("expected %<[%>");
+	    break;
 	  case RT_COMMA:
-	    cp_parser_error (parser, "expected %<,%>");
-	    return;
+	    gmsgid = G_("expected %<,%>");
+	    break;
 	  case RT_SCOPE:
-	    cp_parser_error (parser, "expected %<::%>");
-	    return;
+	    gmsgid = G_("expected %<::%>");
+	    break;
 	  case RT_LESS:
-	    cp_parser_error (parser, "expected %<<%>");
-	    return;
+	    gmsgid = G_("expected %<<%>");
+	    break;
 	  case RT_GREATER:
-	    cp_parser_error (parser, "expected %<>%>");
-	    return;
+	    gmsgid = G_("expected %<>%>");
+	    break;
 	  case RT_EQ:
-	    cp_parser_error (parser, "expected %<=%>");
-	    return;
+	    gmsgid = G_("expected %<=%>");
+	    break;
 	  case RT_ELLIPSIS:
-	    cp_parser_error (parser, "expected %<...%>");
-	    return;
+	    gmsgid = G_("expected %<...%>");
+	    break;
 	  case RT_MULT:
-	    cp_parser_error (parser, "expected %<*%>");
-	    return;
+	    gmsgid = G_("expected %<*%>");
+	    break;
 	  case RT_COMPL:
-	    cp_parser_error (parser, "expected %<~%>");
-	    return;
+	    gmsgid = G_("expected %<~%>");
+	    break;
 	  case RT_COLON:
-	    cp_parser_error (parser, "expected %<:%>");
-	    return;
+	    gmsgid = G_("expected %<:%>");
+	    break;
 	  case RT_COLON_SCOPE:
-	    cp_parser_error (parser, "expected %<:%> or %<::%>");
-	    return;
+	    gmsgid = G_("expected %<:%> or %<::%>");
+	    break;
 	  case RT_CLOSE_PAREN:
-	    cp_parser_error (parser, "expected %<)%>");
-	    return;
+	    gmsgid = G_("expected %<)%>");
+	    break;
 	  case RT_COMMA_CLOSE_PAREN:
-	    cp_parser_error (parser, "expected %<,%> or %<)%>");
-	    return;
+	    gmsgid = G_("expected %<,%> or %<)%>");
+	    break;
 	  case RT_PRAGMA_EOL:
-	    cp_parser_error (parser, "expected end of line");
-	    return;
+	    gmsgid = G_("expected end of line");
+	    break;
 	  case RT_NAME:
-	    cp_parser_error (parser, "expected identifier");
-	    return;
+	    gmsgid = G_("expected identifier");
+	    break;
 	  case RT_SELECT:
-	    cp_parser_error (parser, "expected selection-statement");
-	    return;
+	    gmsgid = G_("expected selection-statement");
+	    break;
 	  case RT_ITERATION:
-	    cp_parser_error (parser, "expected iteration-statement");
-	    return;
+	    gmsgid = G_("expected iteration-statement");
+	    break;
 	  case RT_JUMP:
-	    cp_parser_error (parser, "expected jump-statement");
-	    return;
+	    gmsgid = G_("expected jump-statement");
+	    break;
 	  case RT_CLASS_KEY:
-	    cp_parser_error (parser, "expected class-key");
-	    return;
+	    gmsgid = G_("expected class-key");
+	    break;
 	  case RT_CLASS_TYPENAME_TEMPLATE:
-	    cp_parser_error (parser,
-	  	 "expected %<class%>, %<typename%>, or %<template%>");
-	    return;
+	    gmsgid = G_("expected %<class%>, %<typename%>, or %<template%>");
+	    break;
 	  default:
 	    gcc_unreachable ();
 	}
     }
-  else
-    gcc_unreachable ();
-}
 
+  if (gmsgid)
+    {
+      /* Emulate rest of cp_parser_error.  */
+      cp_token *token = cp_lexer_peek_token (parser->lexer);
+      cp_lexer_set_source_position_from_token (token);
+
+      gcc_rich_location richloc (input_location);
+
+      /* 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);
+
+      c_parse_error (gmsgid,
+		     (token->type == CPP_KEYWORD ? CPP_NAME : token->type),
+		     token->u.value, token->flags, &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 (token_desc));
+    }
+}
 
 
 /* If the next token is of the indicated TYPE, consume it.  Otherwise,
    issue an error message indicating that TOKEN_DESC was expected.
 
    Returns the token consumed, if the token had the appropriate type.
-   Otherwise, returns NULL.  */
+   Otherwise, returns NULL.
+
+   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 TOKEN_DESC is
+   RT_CLOSE_PAREN).  */
 
 static cp_token *
 cp_parser_require (cp_parser* parser,
 		   enum cpp_ttype type,
-		   required_token token_desc)
+		   required_token token_desc,
+		   location_t matching_location)
 {
   if (cp_lexer_next_token_is (parser->lexer, type))
     return cp_lexer_consume_token (parser->lexer);
@@ -28122,7 +28310,8 @@  cp_parser_require (cp_parser* parser,
     {
       /* Output the MESSAGE -- unless we're parsing tentatively.  */
       if (!cp_parser_simulate_error (parser))
-	cp_parser_required_error (parser, token_desc, /*keyword=*/false);
+	cp_parser_required_error (parser, token_desc, /*keyword=*/false,
+				  matching_location);
       return NULL;
     }
 }
@@ -28224,7 +28413,8 @@  cp_parser_require_keyword (cp_parser* parser,
 
   if (token && token->keyword != keyword)
     {
-      cp_parser_required_error (parser, token_desc, /*keyword=*/true); 
+      cp_parser_required_error (parser, token_desc, /*keyword=*/true,
+                                UNKNOWN_LOCATION);
       return NULL;
     }
 
@@ -29035,10 +29225,11 @@  cp_parser_objc_encode_expression (cp_parser* parser)
   location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@encode'.  */
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
   token = cp_lexer_peek_token (parser->lexer);
   type = complete_type (cp_parser_type_id (parser));
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  parens.require_close (parser);
 
   if (!type)
     {
@@ -29080,9 +29271,10 @@  cp_parser_objc_defs_expression (cp_parser *parser)
   tree name;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@defs'.  */
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
   name = cp_parser_identifier (parser);
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  parens.require_close (parser);
 
   return objc_get_class_ivars (name);
 }
@@ -29101,9 +29293,10 @@  cp_parser_objc_protocol_expression (cp_parser* parser)
   location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@protocol'.  */
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
   proto = cp_parser_identifier (parser);
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  parens.require_close (parser);
 
   /* Build a location of the form:
        @protocol(prot)
@@ -29141,7 +29334,8 @@  cp_parser_objc_selector_expression (cp_parser* parser)
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@selector'.  */
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
   token = cp_lexer_peek_token (parser->lexer);
 
   while (cp_parser_objc_selector_p (token->type) || token->type == CPP_COLON
@@ -29188,7 +29382,7 @@  cp_parser_objc_selector_expression (cp_parser* parser)
     }
 
  finish_selector:
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  parens.require_close (parser);
 
 
   /* Build a location of the form:
@@ -29395,7 +29589,8 @@  cp_parser_objc_typename (cp_parser* parser)
     {
       tree proto_quals, cp_type = NULL_TREE;
 
-      cp_lexer_consume_token (parser->lexer);  /* Eat '('.  */
+      matching_parens parens;
+      parens.consume_open (parser); /* Eat '('.  */
       proto_quals = cp_parser_objc_protocol_qualifiers (parser);
 
       /* An ObjC type name may consist of just protocol qualifiers, in which
@@ -29421,7 +29616,7 @@  cp_parser_objc_typename (cp_parser* parser)
 	    }
 	}
 
-      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+      parens.require_close (parser);
       type_name = build_tree_list (proto_quals, cp_type);
     }
 
@@ -30035,7 +30230,8 @@  cp_parser_objc_superclass_or_category (cp_parser *parser,
     }
   else if (next->type == CPP_OPEN_PAREN)
     {
-      cp_lexer_consume_token (parser->lexer);  /* Eat '('.  */
+      matching_parens parens;
+      parens.consume_open (parser);  /* Eat '('.  */
 
       /* If there is no category name, and this is an @interface, we
 	 have a class extension.  */
@@ -30047,7 +30243,7 @@  cp_parser_objc_superclass_or_category (cp_parser *parser,
       else
 	*categ = cp_parser_identifier (parser);
 
-      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+      parens.require_close (parser);
     }
 }
 
@@ -30235,9 +30431,10 @@  cp_parser_objc_try_catch_finally_statement (cp_parser *parser)
       cp_parameter_declarator *parm;
       tree parameter_declaration = error_mark_node;
       bool seen_open_paren = false;
+      matching_parens parens;
 
       cp_lexer_consume_token (parser->lexer);
-      if (cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+      if (parens.require_open (parser))
 	seen_open_paren = true;
       if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
 	{
@@ -30263,7 +30460,7 @@  cp_parser_objc_try_catch_finally_statement (cp_parser *parser)
 						    /*attrlist=*/NULL);
 	}
       if (seen_open_paren)
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	parens.require_close (parser);
       else
 	{
 	  /* If there was no open parenthesis, we are recovering from
@@ -30317,9 +30514,10 @@  cp_parser_objc_synchronized_statement (cp_parser *parser)
 
   location = cp_lexer_peek_token (parser->lexer)->location;
   objc_maybe_warn_exceptions (location);
-  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  matching_parens parens;
+  parens.require_open (parser);
   lock = cp_parser_expression (parser);
-  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+  parens.require_close (parser);
 
   /* NB: The @synchronized block needs to be wrapped in its own STATEMENT_LIST
      node, lest it get absorbed into the surrounding block.  */
@@ -30560,7 +30758,8 @@  cp_parser_objc_at_property_declaration (cp_parser *parser)
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
       /* Eat the '('.  */
-      cp_lexer_consume_token (parser->lexer);
+      matching_parens parens;
+      parens.consume_open (parser);
 
       while (true)
 	{
@@ -30648,7 +30847,7 @@  cp_parser_objc_at_property_declaration (cp_parser *parser)
 	 "error: expected ‘)’ before ‘,’ token".  This is because
 	 cp_parser_require, unlike the C counterpart, will produce an
 	 error even if we are in error recovery.  */
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	{
 	  cp_parser_skip_to_closing_parenthesis (parser,
 						 /*recovering=*/true,
@@ -31339,13 +31538,14 @@  cp_parser_oacc_single_int_clause (cp_parser *parser, omp_clause_code code,
 {
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   tree t = cp_parser_assignment_expression (parser, NULL, false, false);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     {
       cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					     /*or_comma=*/false,
@@ -31392,7 +31592,8 @@  cp_parser_oacc_shape_clause (cp_parser *parser, omp_clause_code kind,
 
   if (cp_lexer_next_token_is (lexer, CPP_OPEN_PAREN))
     {
-      cp_lexer_consume_token (lexer);
+      matching_parens parens;
+      parens.consume_open (parser);
 
       do
 	{
@@ -31466,7 +31667,7 @@  cp_parser_oacc_shape_clause (cp_parser *parser, omp_clause_code kind,
 	}
       while (1);
 
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	goto cleanup_error;
     }
 
@@ -31612,12 +31813,13 @@  cp_parser_omp_clause_collapse (cp_parser *parser, tree list, location_t location
   HOST_WIDE_INT n;
 
   loc = cp_lexer_peek_token (parser->lexer)->location;
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   num = cp_parser_constant_expression (parser);
 
-  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+  if (!parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -31656,7 +31858,8 @@  cp_parser_omp_clause_default (cp_parser *parser, tree list,
   enum omp_clause_default_kind kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED;
   tree c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
   if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
     {
@@ -31699,7 +31902,7 @@  cp_parser_omp_clause_default (cp_parser *parser, tree list,
     }
 
   if (kind == OMP_CLAUSE_DEFAULT_UNSPECIFIED
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -31723,13 +31926,14 @@  cp_parser_omp_clause_final (cp_parser *parser, tree list, location_t location)
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_condition (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -31760,7 +31964,8 @@  cp_parser_omp_clause_if (cp_parser *parser, tree list, location_t location,
   tree t, c;
   enum tree_code if_modifier = ERROR_MARK;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (is_omp && cp_lexer_next_token_is (parser->lexer, CPP_NAME))
@@ -31843,7 +32048,7 @@  cp_parser_omp_clause_if (cp_parser *parser, tree list, location_t location,
   t = cp_parser_condition (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -31938,13 +32143,14 @@  cp_parser_omp_clause_num_threads (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -31968,13 +32174,14 @@  cp_parser_omp_clause_num_tasks (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -31998,13 +32205,14 @@  cp_parser_omp_clause_grainsize (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -32028,13 +32236,14 @@  cp_parser_omp_clause_priority (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -32058,13 +32267,14 @@  cp_parser_omp_clause_hint (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -32088,7 +32298,8 @@  cp_parser_omp_clause_defaultmap (cp_parser *parser, tree list,
   tree c, id;
   const char *p;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (!cp_lexer_next_token_is (parser->lexer, CPP_NAME))
@@ -32120,7 +32331,7 @@  cp_parser_omp_clause_defaultmap (cp_parser *parser, tree list,
       goto out_err;
     }
   cp_lexer_consume_token (parser->lexer);
-  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+  if (!parens.require_close (parser))
     goto out_err;
 
   check_no_duplicate_clause (list, OMP_CLAUSE_DEFAULTMAP, "defaultmap",
@@ -32155,11 +32366,12 @@  cp_parser_omp_clause_ordered (cp_parser *parser,
 
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
-      cp_lexer_consume_token (parser->lexer);
+      matching_parens parens;
+      parens.consume_open (parser);
 
       num = cp_parser_constant_expression (parser);
 
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					       /*or_comma=*/false,
 					       /*consume_paren=*/true);
@@ -32316,7 +32528,8 @@  cp_parser_omp_clause_schedule (cp_parser *parser, tree list, location_t location
   tree c, t;
   int modifiers = 0, nmodifiers = 0;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   c = build_omp_clause (location, OMP_CLAUSE_SCHEDULE);
@@ -32410,7 +32623,7 @@  cp_parser_omp_clause_schedule (cp_parser *parser, tree list, location_t location
       else
 	OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (c) = t;
 
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	goto resync_fail;
     }
   else if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_COMMA_CLOSE_PAREN))
@@ -32516,13 +32729,14 @@  cp_parser_omp_clause_num_teams (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -32546,13 +32760,14 @@  cp_parser_omp_clause_thread_limit (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -32577,7 +32792,8 @@  cp_parser_omp_clause_aligned (cp_parser *parser, tree list)
   tree nlist, c, alignment = NULL_TREE;
   bool colon;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_ALIGNED, list,
@@ -32587,7 +32803,7 @@  cp_parser_omp_clause_aligned (cp_parser *parser, tree list)
     {
       alignment = cp_parser_constant_expression (parser);
 
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					       /*or_comma=*/false,
 					       /*consume_paren=*/true);
@@ -32618,7 +32834,8 @@  cp_parser_omp_clause_linear (cp_parser *parser, tree list,
   bool colon;
   enum omp_clause_linear_kind kind = OMP_CLAUSE_LINEAR_DEFAULT;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (!is_cilk_simd_fn
@@ -32648,7 +32865,7 @@  cp_parser_omp_clause_linear (cp_parser *parser, tree list,
       colon = cp_lexer_next_token_is (parser->lexer, CPP_COLON);
       if (colon)
 	cp_parser_require (parser, CPP_COLON, RT_COLON);
-      else if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      else if (!parens.require_close (parser))
 	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					       /*or_comma=*/false,
 					       /*consume_paren=*/true);
@@ -32686,7 +32903,7 @@  cp_parser_omp_clause_linear (cp_parser *parser, tree list,
 	  sorry ("using parameters for %<linear%> step is not supported yet");
 	  step = integer_one_node;
 	}
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					       /*or_comma=*/false,
 					       /*consume_paren=*/true);
@@ -32713,13 +32930,14 @@  cp_parser_omp_clause_safelen (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_constant_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -32742,13 +32960,14 @@  cp_parser_omp_clause_simdlen (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_constant_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -32864,7 +33083,8 @@  cp_parser_omp_clause_depend (cp_parser *parser, tree list, location_t loc)
   tree nlist, c;
   enum omp_clause_depend_kind kind = OMP_CLAUSE_DEPEND_INOUT;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
@@ -32896,7 +33116,7 @@  cp_parser_omp_clause_depend (cp_parser *parser, tree list, location_t loc)
       OMP_CLAUSE_DEPEND_KIND (c) = kind;
       OMP_CLAUSE_DECL (c) = NULL_TREE;
       OMP_CLAUSE_CHAIN (c) = list;
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					       /*or_comma=*/false,
 					       /*consume_paren=*/true);
@@ -33027,13 +33247,14 @@  cp_parser_omp_clause_device (cp_parser *parser, tree list,
 {
   tree t, c;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   t = cp_parser_expression (parser);
 
   if (t == error_mark_node
-      || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      || !parens.require_close (parser))
     cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					   /*or_comma=*/false,
 					   /*consume_paren=*/true);
@@ -33058,7 +33279,8 @@  cp_parser_omp_clause_dist_schedule (cp_parser *parser, tree list,
 {
   tree c, t;
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return list;
 
   c = build_omp_clause (location, OMP_CLAUSE_DIST_SCHEDULE);
@@ -33077,7 +33299,7 @@  cp_parser_omp_clause_dist_schedule (cp_parser *parser, tree list,
 	goto resync_fail;
       OMP_CLAUSE_DIST_SCHEDULE_CHUNK_EXPR (c) = t;
 
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	goto resync_fail;
     }
   else if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_COMMA_CLOSE_PAREN))
@@ -33163,11 +33385,12 @@  cp_parser_oacc_clause_async (cp_parser *parser, tree list)
 
   if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
     {
-      cp_lexer_consume_token (parser->lexer);
+      matching_parens parens;
+      parens.consume_open (parser);
 
       t = cp_parser_expression (parser);
       if (t == error_mark_node
-	  || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+	  || !parens.require_close (parser))
 	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 						/*or_comma=*/false,
 						/*consume_paren=*/true);
@@ -34217,12 +34440,13 @@  cp_parser_omp_critical (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
 
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
-      cp_lexer_consume_token (parser->lexer);
+      matching_parens parens;
+      parens.consume_open (parser);
 
       name = cp_parser_identifier (parser);
 
       if (name == error_mark_node
-	  || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+	  || !parens.require_close (parser))
 	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					       /*or_comma=*/false,
 					       /*consume_paren=*/true);
@@ -34720,7 +34944,8 @@  cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses,
 	}
       loc = cp_lexer_consume_token (parser->lexer)->location;
 
-      if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+      matching_parens parens;
+      if (!parens.require_open (parser))
 	return NULL;
 
       init = orig_init = decl = real_decl = NULL;
@@ -34852,7 +35077,7 @@  cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses,
 	    protected_set_expr_location (incr, input_location);
 	}
 
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
 					       /*or_comma=*/false,
 					       /*consume_paren=*/true);
@@ -35211,7 +35436,8 @@  cp_parser_omp_sections_scope (cp_parser *parser)
   bool error_suppress = false;
   cp_token *tok;
 
-  if (!cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE))
+  matching_braces braces;
+  if (!braces.require_open (parser))
     return NULL_TREE;
 
   stmt = push_stmt_list ();
@@ -35248,7 +35474,7 @@  cp_parser_omp_sections_scope (cp_parser *parser)
       substmt = build1 (OMP_SECTION, void_type_node, substmt);
       add_stmt (substmt);
     }
-  cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+  braces.require_close (parser);
 
   substmt = pop_stmt_list (stmt);
 
@@ -37110,7 +37336,8 @@  cp_parser_omp_declare_reduction_exprs (tree fndecl, cp_parser *parser)
   if (strcmp (p, "initializer") == 0)
     {
       cp_lexer_consume_token (parser->lexer);
-      if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+      matching_parens parens;
+      if (!parens.require_open (parser))
 	return false;
 
       p = "";
@@ -37201,12 +37428,13 @@  cp_parser_omp_declare_reduction_exprs (tree fndecl, cp_parser *parser)
       if (ctor)
 	add_decl_expr (omp_orig);
 
-      if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+      if (!parens.require_close (parser))
 	return false;
     }
 
   if (!cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA_EOL))
-    cp_parser_required_error (parser, RT_PRAGMA_EOL, /*keyword=*/false);
+    cp_parser_required_error (parser, RT_PRAGMA_EOL, /*keyword=*/false,
+                              UNKNOWN_LOCATION);
 
   return true;
 }
@@ -37634,7 +37862,8 @@  cp_parser_oacc_routine (cp_parser *parser, cp_token *pragma_tok,
   /* Look for optional '( name )'.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
-      cp_lexer_consume_token (parser->lexer); /* '(' */
+      matching_parens parens;
+      parens.consume_open (parser); /* '(' */
 
       /* We parse the name as an id-expression.  If it resolves to
 	 anything other than a non-overloaded function at namespace
@@ -37651,7 +37880,7 @@  cp_parser_oacc_routine (cp_parser *parser, cp_token *pragma_tok,
 	cp_parser_name_lookup_error (parser, name, decl, NLE_NULL, name_loc);
 
       if (decl == error_mark_node
-	  || !cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+	  || !parens.require_close (parser))
 	{
 	  cp_parser_skip_to_pragma_eol (parser, pragma_tok);
 	  parser->oacc_routine = NULL;
@@ -38083,12 +38312,13 @@  cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
   if (!noex || !noex_expr
       || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
     {
-      cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+      matching_parens parens;
+      parens.require_open (parser);
 
       expr = cp_parser_expression (parser);
       expr = finish_parenthesized_expr (expr);
 
-      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+      parens.require_close (parser);
     }
   else
     {
@@ -38676,7 +38906,8 @@  cp_parser_cilk_simd_vectorlength (cp_parser *parser, tree clauses,
     check_no_duplicate_clause (clauses, OMP_CLAUSE_SIMDLEN, "vectorlength",
 			       loc);
 
-  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+  matching_parens parens;
+  if (!parens.require_open (parser))
     return error_mark_node;
 
   expr = cp_parser_constant_expression (parser);
@@ -38713,7 +38944,7 @@  cp_parser_cilk_simd_vectorlength (cp_parser *parser, tree clauses,
 	}
     }
 
-  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+  if (!parens.require_close (parser))
     return error_mark_node;
   return clauses;
 }
diff --git a/gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C b/gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C
new file mode 100644
index 0000000..fda3532
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C
@@ -0,0 +1,3 @@ 
+extern "C" { /* { dg-message "12: to match this '.'" } */
+
+void test (void); /* { dg-error "17: expected '.' at end of input" } */
diff --git a/gcc/testsuite/g++.dg/diagnostic/unclosed-function.C b/gcc/testsuite/g++.dg/diagnostic/unclosed-function.C
new file mode 100644
index 0000000..e1e1550
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/unclosed-function.C
@@ -0,0 +1,3 @@ 
+void test (void)
+{ /* { dg-message "1: to match this '.'" } */
+  int filler; /* { dg-error "13: expected '.' at end of input" } */
diff --git a/gcc/testsuite/g++.dg/diagnostic/unclosed-namespace.C b/gcc/testsuite/g++.dg/diagnostic/unclosed-namespace.C
new file mode 100644
index 0000000..ff11322
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/unclosed-namespace.C
@@ -0,0 +1,2 @@ 
+namespace unclosed { /* { dg-message "20: to match this '.'" } */
+int filler; /* { dg-error "11: expected '.' at end of input" } */
diff --git a/gcc/testsuite/g++.dg/diagnostic/unclosed-struct.C b/gcc/testsuite/g++.dg/diagnostic/unclosed-struct.C
new file mode 100644
index 0000000..8c206bb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/unclosed-struct.C
@@ -0,0 +1,3 @@ 
+struct unclosed { /* { dg-message "17: to match this '.'" } */
+  int dummy; /* { dg-error "12: expected '.' at end of input" } */
+  // { dg-error "expected unqualified-id at end of input" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/parse/pragma2.C b/gcc/testsuite/g++.dg/parse/pragma2.C
index c5616ff..3dc5fc1 100644
--- a/gcc/testsuite/g++.dg/parse/pragma2.C
+++ b/gcc/testsuite/g++.dg/parse/pragma2.C
@@ -4,5 +4,5 @@ 
 // does not.
 int f(int x,
 #pragma interface  // { dg-error "not allowed here" }
-      // The parser gets confused and issues an error on the next line.
-      int y); // { dg-bogus "" "" { xfail *-*-* } } 
+      // { dg-bogus "expected identifier" "" { xfail *-*-* } .-1 }
+      int y);