public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 3/3] matching tokens: C++ parts
  2017-07-11 14:51 [PATCH 0/3] C/C++: show pertinent open token when missing a close token David Malcolm
  2017-07-11 14:51 ` [PATCH 2/3] matching tokens: C parts David Malcolm
@ 2017-07-11 14:51 ` David Malcolm
  2017-07-12 13:13   ` Trevor Saunders
  2017-07-11 14:51 ` [PATCH 1/3] matching tokens: c-family parts David Malcolm
  2017-07-11 17:28 ` [PATCH 0/3] C/C++: show pertinent open token when missing a close token Martin Sebor
  3 siblings, 1 reply; 27+ messages in thread
From: David Malcolm @ 2017-07-11 14:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

OK for trunk? (assuming the rest is approved)

gcc/cp/ChangeLog:
	* parser.c (cp_parser_error): Update for new param to
	c_parse_error.
	(class token_pair): New class.
	(class matching_parens): New class.
	(class matching_braces): New class.
	(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_postfix_expression): Likewise.
	(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.
	(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                                    | 821 ++++++++++++++-------
 .../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, 549 insertions(+), 287 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

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c81b1a1f..30cdc53 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,110 @@ 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 token_pair_traits_t>
+class token_pair
+{
+ private:
+  typedef token_pair_traits_t traits_t;
+
+ 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;
+  }
+
+  /* Peek the next token from PARSER, recording its location as
+     that of the opening token within the pair.  */
+
+  void peek_open (cp_parser *parser)
+  {
+    cp_token *tok = cp_lexer_peek_token (parser->lexer);
+    gcc_assert (tok->type == traits_t::open_token_type);
+    m_open_loc = tok->location;
+  }
+
+  /* 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
+     corrsponding 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;
+};
+
+/* A subclass of token_pair for tracking matching pairs of parentheses.  */
+
+class matching_parens : public token_pair<matching_parens>
+{
+ public:
+  static const enum cpp_ttype open_token_type;
+  static const enum required_token required_token_open;
+  static const enum cpp_ttype close_token_type;
+  static const enum required_token required_token_close;
+};
+
+/* Constant data for class matching_parens.  */
+
+const enum cpp_ttype matching_parens::open_token_type = CPP_OPEN_PAREN;
+const enum required_token matching_parens::required_token_open = RT_OPEN_PAREN;
+const enum cpp_ttype matching_parens::close_token_type = CPP_CLOSE_PAREN;
+const enum required_token matching_parens::required_token_close
+  = RT_CLOSE_PAREN;
+
+/* A subclass of token_pair for tracking matching pairs of braces.  */
+
+class matching_braces : public token_pair<matching_braces>
+{
+ public:
+  static const enum cpp_ttype open_token_type;
+  static const enum required_token required_token_open;
+  static const enum cpp_ttype close_token_type;
+  static const enum required_token required_token_close;
+};
+
+/* Constant data for class matching_braces.  */
+
+const enum cpp_ttype matching_braces::open_token_type = CPP_OPEN_BRACE;
+const enum required_token matching_braces::required_token_open = RT_OPEN_BRACE;
+const enum cpp_ttype matching_braces::close_token_type = CPP_CLOSE_BRACE;
+const enum required_token matching_braces::required_token_close
+  = RT_CLOSE_BRACE;
+
 /* Parse a GNU statement-expression, i.e. ({ stmts }), except for the
    enclosing parentheses.  */
 
@@ -4513,7 +4618,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 +4628,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 +5034,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 +5083,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 +5205,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 +5220,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,
@@ -6465,7 +6573,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 +6635,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 +6652,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 +6666,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;
@@ -7561,7 +7669,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 +7766,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 +8066,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 +8101,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 +8345,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 +8358,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 +8898,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 +8964,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 +9749,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 +9800,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 +9946,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 +9986,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 +10467,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 +10483,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 +10640,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 +10682,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 +10706,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 +11242,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 +11261,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 +11382,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 +11412,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 +12199,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 +12229,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 +12245,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 +12601,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);
     }
 }
 
@@ -13709,11 +13830,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
@@ -13765,7 +13887,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.  */
@@ -13797,7 +13920,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,
@@ -13951,7 +14074,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) */
@@ -13959,7 +14083,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;
@@ -14014,7 +14138,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);
@@ -14713,11 +14837,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 `['.  */
@@ -18090,7 +18217,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 */
@@ -18123,7 +18251,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 ();
@@ -18416,13 +18544,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)
@@ -19803,7 +19932,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
@@ -19818,7 +19948,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.  */
@@ -19914,7 +20044,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;
@@ -19925,7 +20056,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;
@@ -21773,7 +21904,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.  */
@@ -21790,7 +21922,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);
@@ -22217,7 +22349,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;
@@ -22269,7 +22402,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);
@@ -23748,11 +23881,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;
     }
@@ -24008,7 +24142,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)
 	    {
@@ -24028,7 +24163,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
 	{
@@ -24082,7 +24217,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);
@@ -24118,7 +24254,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;
 }
@@ -24265,10 +24401,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);
 }
@@ -24384,13 +24521,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;
 }
@@ -24442,11 +24580,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 
@@ -24700,8 +24839,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);
@@ -24714,9 +24855,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);
@@ -25139,7 +25280,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;
@@ -25172,7 +25314,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;
@@ -25404,12 +25546,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;
@@ -25422,12 +25565,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;
@@ -25566,14 +25710,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. */
@@ -26644,7 +26789,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;
@@ -26664,7 +26810,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)
@@ -27549,9 +27695,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");
@@ -27576,7 +27723,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;
 }
@@ -27627,6 +27774,9 @@ cp_parser_sizeof_operand (cp_parser* parser, enum rid keyword)
     {
       tree type = NULL_TREE;
 
+      matching_parens parens;
+      parens.peek_open (parser);
+
       /* We can't be sure yet whether we're looking at a type-id or an
 	 expression.  */
       cp_parser_parse_tentatively (parser);
@@ -27643,7 +27793,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;
 	}
 
@@ -27925,187 +28075,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);
@@ -28113,7 +28321,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;
     }
 }
@@ -28215,7 +28424,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;
     }
 
@@ -29026,10 +29236,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)
     {
@@ -29071,9 +29282,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);
 }
@@ -29092,9 +29304,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)
@@ -29132,7 +29345,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
@@ -29179,7 +29393,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:
@@ -29386,7 +29600,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
@@ -29412,7 +29627,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);
     }
 
@@ -30026,7 +30241,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.  */
@@ -30038,7 +30254,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);
     }
 }
 
@@ -30226,9 +30442,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))
 	{
@@ -30254,7 +30471,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
@@ -30308,9 +30525,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.  */
@@ -30551,7 +30769,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)
 	{
@@ -30639,7 +30858,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,
@@ -31330,13 +31549,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,
@@ -31383,7 +31603,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
 	{
@@ -31457,7 +31678,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;
     }
 
@@ -31603,12 +31824,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);
@@ -31647,7 +31869,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))
     {
@@ -31690,7 +31913,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);
@@ -31714,13 +31937,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);
@@ -31751,7 +31975,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))
@@ -31834,7 +32059,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);
@@ -31929,13 +32154,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);
@@ -31959,13 +32185,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);
@@ -31989,13 +32216,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);
@@ -32019,13 +32247,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);
@@ -32049,13 +32278,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);
@@ -32079,7 +32309,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))
@@ -32111,7 +32342,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",
@@ -32146,11 +32377,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);
@@ -32307,7 +32539,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);
@@ -32401,7 +32634,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))
@@ -32507,13 +32740,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);
@@ -32537,13 +32771,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);
@@ -32568,7 +32803,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,
@@ -32578,7 +32814,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);
@@ -32609,7 +32845,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
@@ -32639,7 +32876,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);
@@ -32677,7 +32914,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);
@@ -32704,13 +32941,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);
@@ -32733,13 +32971,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);
@@ -32855,7 +33094,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))
@@ -32887,7 +33127,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);
@@ -33018,13 +33258,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);
@@ -33049,7 +33290,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);
@@ -33068,7 +33310,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))
@@ -33154,11 +33396,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);
@@ -34208,12 +34451,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);
@@ -34711,7 +34955,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;
@@ -34843,7 +35088,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);
@@ -35202,7 +35447,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 ();
@@ -35239,7 +35485,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);
 
@@ -37101,7 +37347,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 = "";
@@ -37192,12 +37439,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;
 }
@@ -37625,7 +37873,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
@@ -37642,7 +37891,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;
@@ -38074,12 +38323,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
     {
@@ -38667,7 +38917,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);
@@ -38704,7 +38955,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);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 0/3] C/C++: show pertinent open token when missing a close token
@ 2017-07-11 14:51 David Malcolm
  2017-07-11 14:51 ` [PATCH 2/3] matching tokens: C parts David Malcolm
                   ` (3 more replies)
  0 siblings, 4 replies; 27+ messages in thread
From: David Malcolm @ 2017-07-11 14:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

[This patch kit is effectively just one patch; I've split it up into
 3 parts, in the hope of making it easier to review:
 the c-family parts, the C parts, and the C++ parts]

This patch adds a hint to the user to various errors generated
in the C frontend by:

  c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")
  c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, "expected %<}%>")

etc (where there's a non-NULL msgid), and in the C++ frontend by:

  cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
  cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE)

The hint shows the user where the pertinent open paren or open brace
is, which ought to be very helpful for complicated nested collections
of parentheses, and somewhat helpful even for simple cases;
consider e.g.:

  ...lots of lines of code...
  extern "C" {
  ...lots of lines of code...
  int test ();
  EOF

where the user currently has to hunt through the source file to find
the unclosed '{':

test.cc:262:12: error: expected '}' at end of input
 int test ();
            ^

With this patch we tell them:

test.cc:262:12: error: expected '}' at end of input
 int test ();
            ^
test.cc:98:12: note: to match this '{'
 extern "C" {
            ^

The patch avoids using a note if the tokens are on the same line,
highlighting the unclosed open token with an underline:

test.c:3:32: error: expected ')' before ';' token
   return ((b * b) - (4 * a * c);
          ~                     ^

The bulk of the changes in the patch are to the parsers, done using
new classes "matching_braces" and "matching_parens", which stash the
location of the opening token during parsing, so that e.g.:

  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
     return;
  ...do stuff...
  c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>");

becomes:

  matching_parens parens;
  if (!parens.require_open (parser))
     return;
  ...do stuff...
  parens.require_close (parser);

The exact implementation of these classes varies somewhat between the
C and C++ frontends, to deal with implementation differences between
them (I tried to keep them as similar as possible).

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu;
adds 23 PASS results to gcc.sum; adds 99 PASS results to g++.sum.

OK for trunk?

 gcc/c-family/c-common.c                            |  17 +-
 gcc/c-family/c-common.h                            |   3 +-
 gcc/c/c-parser.c                                   | 647 ++++++++++------
 gcc/c/c-parser.h                                   |   8 +-
 gcc/cp/parser.c                                    | 821 ++++++++++++++-------
 gcc/testsuite/c-c++-common/missing-close-symbol.c  |  33 +
 gcc/testsuite/c-c++-common/missing-symbol.c        |  50 ++
 .../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 +-
 gcc/testsuite/gcc.dg/unclosed-init.c               |   3 +
 13 files changed, 1084 insertions(+), 513 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/missing-close-symbol.c
 create mode 100644 gcc/testsuite/c-c++-common/missing-symbol.c
 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
 create mode 100644 gcc/testsuite/gcc.dg/unclosed-init.c

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 1/3] matching tokens: c-family parts
  2017-07-11 14:51 [PATCH 0/3] C/C++: show pertinent open token when missing a close token David Malcolm
  2017-07-11 14:51 ` [PATCH 2/3] matching tokens: C parts David Malcolm
  2017-07-11 14:51 ` [PATCH 3/3] matching tokens: C++ parts David Malcolm
@ 2017-07-11 14:51 ` David Malcolm
  2017-07-18 17:23   ` Marek Polacek
  2017-07-11 17:28 ` [PATCH 0/3] C/C++: show pertinent open token when missing a close token Martin Sebor
  3 siblings, 1 reply; 27+ messages in thread
From: David Malcolm @ 2017-07-11 14:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

OK for trunk? (assuming the rest is approved)

gcc/c-family/ChangeLog:
	* c-common.c (c_parse_error): Add rich_location * param, using it
	rather implicitly using input_location.
	* c-common.h (c_parse_error): Add rich_location * param.

gcc/testsuite/ChangeLog:
	* c-c++-common/missing-close-symbol.c: New test case.
	* c-c++-common/missing-symbol.c: New test case.
---
 gcc/c-family/c-common.c                           | 17 ++++----
 gcc/c-family/c-common.h                           |  3 +-
 gcc/testsuite/c-c++-common/missing-close-symbol.c | 33 +++++++++++++++
 gcc/testsuite/c-c++-common/missing-symbol.c       | 50 +++++++++++++++++++++++
 4 files changed, 94 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/missing-close-symbol.c
 create mode 100644 gcc/testsuite/c-c++-common/missing-symbol.c

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index b4217f3..b168cb5 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5949,12 +5949,13 @@ catenate_strings (const char *lhs, const char *rhs_start, int rhs_size)
   return result;
 }
 
-/* Issue the error given by GMSGID, indicating that it occurred before
-   TOKEN, which had the associated VALUE.  */
+/* Issue the error given by GMSGID at RICHLOC, indicating that it occurred
+   before TOKEN, which had the associated VALUE.  */
 
 void
 c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
-	       tree value, unsigned char token_flags)
+	       tree value, unsigned char token_flags,
+	       rich_location *richloc)
 {
 #define catenate_messages(M1, M2) catenate_strings ((M1), (M2), sizeof (M2))
 
@@ -5995,7 +5996,7 @@ c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
       else
 	message = catenate_messages (gmsgid, " before %s'\\x%x'");
 
-      error (message, prefix, val);
+      error_at_rich_loc (richloc, message, prefix, val);
       free (message);
       message = NULL;
     }
@@ -6023,7 +6024,7 @@ c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
   else if (token_type == CPP_NAME)
     {
       message = catenate_messages (gmsgid, " before %qE");
-      error (message, value);
+      error_at_rich_loc (richloc, message, value);
       free (message);
       message = NULL;
     }
@@ -6036,16 +6037,16 @@ c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
   else if (token_type < N_TTYPES)
     {
       message = catenate_messages (gmsgid, " before %qs token");
-      error (message, cpp_type2name (token_type, token_flags));
+      error_at_rich_loc (richloc, message, cpp_type2name (token_type, token_flags));
       free (message);
       message = NULL;
     }
   else
-    error (gmsgid);
+    error_at_rich_loc (richloc, gmsgid);
 
   if (message)
     {
-      error (message);
+      error_at_rich_loc (richloc, message);
       free (message);
     }
 #undef catenate_messages
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 7e7efb2..de92701 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1124,7 +1124,8 @@ extern void builtin_define_with_int_value (const char *, HOST_WIDE_INT);
 extern void builtin_define_type_sizeof (const char *, tree);
 extern void c_stddef_cpp_builtins (void);
 extern void fe_file_change (const line_map_ordinary *);
-extern void c_parse_error (const char *, enum cpp_ttype, tree, unsigned char);
+extern void c_parse_error (const char *, enum cpp_ttype, tree, unsigned char,
+			   rich_location *richloc);
 
 /* In c-ppoutput.c  */
 extern void init_pp_output (FILE *);
diff --git a/gcc/testsuite/c-c++-common/missing-close-symbol.c b/gcc/testsuite/c-c++-common/missing-close-symbol.c
new file mode 100644
index 0000000..85b96f28
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/missing-close-symbol.c
@@ -0,0 +1,33 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+/* Verify that the C/C++ frontends show the pertinent opening symbol when
+   a closing symbol is missing.  */
+
+/* Verify that, when they are on the same line, that the opening symbol is
+   shown as a secondary range within the main diagnostic.  */
+
+void test_static_assert_same_line (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char), "msg"; /* { dg-error "expected '\\)' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char), "msg";
+                 ~                                  ^
+     { dg-end-multiline-output "" } */
+}
+
+/* Verify that, when they are on different lines, that the opening symbol is
+   shown via a secondary diagnostic.  */
+
+void test_static_assert_different_line (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char), /* { dg-message "to match this '\\('" } */
+		 "msg"; /* { dg-error "expected '\\)' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+    "msg";
+         ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char),
+                 ^
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/c-c++-common/missing-symbol.c b/gcc/testsuite/c-c++-common/missing-symbol.c
new file mode 100644
index 0000000..33a501b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/missing-symbol.c
@@ -0,0 +1,50 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+extern int foo (void);
+extern int bar (void);
+
+int missing_close_paren_in_switch (int i)
+{
+  switch (i /* { dg-message "10: to match this '\\('" } */
+    { /* { dg-error "5: expected '\\)' before '.' token" } */
+  /* { dg-begin-multiline-output "" }
+     {
+     ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   switch (i
+          ^
+     { dg-end-multiline-output "" } */
+
+    case 0:
+      return 5;
+    default:
+      return i;
+    }
+} /* { dg-error "1: expected" } */
+  /* { dg-begin-multiline-output "" }
+ }
+ ^
+     { dg-end-multiline-output "" } */
+
+void missing_close_paren_in_if (void)
+{
+  if (foo () /* { dg-line start_of_if } */
+      && bar () 
+    { /* { dg-error "5: expected '\\)' before '.' token" } */
+      /* { dg-begin-multiline-output "" }
+     {
+     ^
+         { dg-end-multiline-output "" } */
+      /* { dg-message "6: to match this '\\('" "" { target *-*-* } start_of_if } */
+      /* { dg-begin-multiline-output "" }
+   if (foo ()
+      ^
+      { dg-end-multiline-output "" } */
+    }
+
+} /* { dg-error "1: expected" } */
+  /* { dg-begin-multiline-output "" }
+ }
+ ^
+     { dg-end-multiline-output "" } */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 2/3] matching tokens: C parts
  2017-07-11 14:51 [PATCH 0/3] C/C++: show pertinent open token when missing a close token David Malcolm
@ 2017-07-11 14:51 ` David Malcolm
  2017-07-11 14:51 ` [PATCH 3/3] matching tokens: C++ parts David Malcolm
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 27+ messages in thread
From: David Malcolm @ 2017-07-11 14:51 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

OK for trunk? (assuming the rest is approved)

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.
	(class matching_parens): New class.
	(class matching_braces): New class.
	(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                     | 647 +++++++++++++++++++++++------------
 gcc/c/c-parser.h                     |   8 +-
 gcc/testsuite/gcc.dg/unclosed-init.c |   3 +
 3 files changed, 441 insertions(+), 217 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/unclosed-init.c

diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index f8fbc92..2dca060 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,160 @@ 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 token_pair_traits_t>
+class token_pair
+{
+ private:
+  typedef token_pair_traits_t traits_t;
+
+ 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
+     corrsponding 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;
+};
+
+/* A subclass of token_pair for tracking matching pairs of parentheses.  */
+
+class matching_parens : public token_pair<matching_parens>
+{
+ public:
+  static const enum cpp_ttype open_token_type;
+  static const char * const open_gmsgid;
+  static const enum cpp_ttype close_token_type;
+  static const char * const close_gmsgid;
+};
+
+/* Constant data for use by matching_parens.  */
+
+const enum cpp_ttype matching_parens::open_token_type = CPP_OPEN_PAREN;
+const char * const matching_parens::open_gmsgid = G_("expected %<(%>");
+const enum cpp_ttype matching_parens::close_token_type = CPP_CLOSE_PAREN;
+const char * const matching_parens::close_gmsgid = G_("expected %<)%>");
+
+/* A subclass of token_pair for tracking matching pairs of braces.  */
+
+class matching_braces : public token_pair<matching_braces>
+{
+ public:
+  static const enum cpp_ttype open_token_type;
+  static const char * const open_gmsgid;
+  static const enum cpp_ttype close_token_type;
+  static const char * const close_gmsgid;
+};
+
+/* Constant data for use by matching_braces.  */
+
+const enum cpp_ttype matching_braces::open_token_type = CPP_OPEN_BRACE;
+const char * const matching_braces::open_gmsgid = G_("expected %<{%>");
+const enum cpp_ttype matching_braces::close_token_type = CPP_CLOSE_BRACE;
+const char * const matching_braces::close_gmsgid = G_("expected %<}%>");
+
+/* 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 +1055,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 +1101,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 +2377,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 +2405,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 +3090,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 +3105,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 +3392,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 +3434,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 +3460,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 +3472,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 +4123,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 +4566,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 +4605,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 +5628,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;
 }
 
@@ -5727,7 +5900,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)
@@ -5742,7 +5916,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
     {
@@ -5951,7 +6125,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;
@@ -6098,7 +6273,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;
@@ -6191,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);
@@ -6249,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;
@@ -6314,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;
@@ -6322,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;
@@ -6931,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;
@@ -7160,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)
 	{
@@ -7242,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;
@@ -7405,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++;
@@ -7547,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;
@@ -7793,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:
@@ -7830,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;
@@ -7870,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)
@@ -8005,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);
@@ -8198,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);
@@ -8851,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))
@@ -8871,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);
@@ -9405,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
@@ -9613,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))
 	{
@@ -9636,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
@@ -9685,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;
@@ -9985,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;
 
@@ -10075,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);
@@ -10735,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);
@@ -10772,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;
 }
 
@@ -10930,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;
 }
@@ -11070,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;
@@ -11125,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))
     {
@@ -11165,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;
@@ -11228,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))
@@ -11309,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)
@@ -11408,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);
@@ -11416,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)))
 	{
@@ -11453,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);
@@ -11461,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)))
 	{
@@ -11498,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);
@@ -11506,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)))
 	{
@@ -11543,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);
@@ -11551,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)))
 	{
@@ -11589,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);
@@ -11597,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)))
 	{
@@ -11626,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))
     {
@@ -11654,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;
 }
 
@@ -11698,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;
@@ -11707,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;
@@ -12037,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;
@@ -12093,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;
@@ -12191,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;
 }
@@ -12219,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);
@@ -12330,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,
@@ -12443,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);
@@ -12451,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)))
 	{
@@ -12487,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);
@@ -12495,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)))
 	{
@@ -12535,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,
@@ -12562,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;
 }
 
@@ -12581,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
@@ -12604,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))
     {
@@ -12636,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;
 }
 
@@ -12649,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;
@@ -12666,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;
 
@@ -12687,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;
@@ -12704,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;
 
@@ -12816,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))
@@ -12846,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;
     }
 
@@ -12864,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;
 }
 
@@ -12898,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))
@@ -12965,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;
 	}
 
@@ -12985,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;
 	}
     }
@@ -12995,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;
 }
 
@@ -13006,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);
@@ -13014,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)))
 	{
@@ -13043,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))
@@ -13064,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,
@@ -13093,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))
@@ -13112,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;
@@ -13120,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;
 }
 
@@ -13151,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;
 }
@@ -14948,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.  */
@@ -15055,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;
@@ -17562,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;
@@ -17598,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;
 }
@@ -17620,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;
@@ -17682,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;
 }
@@ -18046,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);
@@ -18055,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" } */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/3] C/C++: show pertinent open token when missing a close token
  2017-07-11 14:51 [PATCH 0/3] C/C++: show pertinent open token when missing a close token David Malcolm
                   ` (2 preceding siblings ...)
  2017-07-11 14:51 ` [PATCH 1/3] matching tokens: c-family parts David Malcolm
@ 2017-07-11 17:28 ` Martin Sebor
  2017-07-11 18:32   ` David Malcolm
  3 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2017-07-11 17:28 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 07/11/2017 09:24 AM, David Malcolm wrote:
> [This patch kit is effectively just one patch; I've split it up into
>  3 parts, in the hope of making it easier to review:
>  the c-family parts, the C parts, and the C++ parts]
>
> This patch adds a hint to the user to various errors generated
> in the C frontend by:
>
>   c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")
>   c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, "expected %<}%>")
>
> etc (where there's a non-NULL msgid), and in the C++ frontend by:
>
>   cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
>   cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE)
>
> The hint shows the user where the pertinent open paren or open brace
> is, which ought to be very helpful for complicated nested collections
> of parentheses, and somewhat helpful even for simple cases;

I've played with the patch a bit.  First, let me say that I like
how it associates the curlies.  I agree that it will be helpful.
There are other cases where highlighting mismatched or missing
tokens might be useful, such as for pairs of < and > in complex
template declarations.

But I mainly experimented with it to see if I could get it to
manifest some of the same symptoms I described in bug 81269.
I'm not sure it does reproduce the exact same thing or if it's
a feature, so let me use this as an opportunity to ask.  Given
something like

   namespace {

   enum { e
   <EOF>

I see this output:

   a.C:3:8: error: expected ‘}’ at end of input
    enum { e
         ~ ^
   a.C:3:8: error: expected unqualified-id at end of input
   a.C:3:8: error: expected ‘}’ at end of input
   a.C:1:11: note: to match this ‘{’
    namespace {
              ^

with the first open curly/caret in green, the 'e' in red, and
the last open curly/caret in cyan.

Is the green color intended?  And if yes, what is the intent of
distinguishing it from the red 'e'?  I note that the caret is
red (and there are no other colors in the output) in this case:

   namespace { enum {
   <EOF>

but becomes green again when I add an enumerator:

   namespace { enum { e
   <EOF>

I ask because in the test case in 81269, highlighting the different
tokens in the three colors seems especially confusing and I'd like
to better understand if it's intentional (and what it means).

Incidentally, I tried to make use of this feature in the middle
end (in gimple-ssa-sprintf.c), to achieve the same effect as
-Wrestric does, but it led to even stranger-looking results so
I went back to using plain old warning.   See the attachment to
bug 81269:
https://gcc.gnu.org/bugzilla/attachment.cgi?id=41660

Martin

> consider e.g.:
>
>   ...lots of lines of code...
>   extern "C" {
>   ...lots of lines of code...
>   int test ();
>   EOF
>
> where the user currently has to hunt through the source file to find
> the unclosed '{':
>
> test.cc:262:12: error: expected '}' at end of input
>  int test ();
>             ^
>
> With this patch we tell them:
>
> test.cc:262:12: error: expected '}' at end of input
>  int test ();
>             ^
> test.cc:98:12: note: to match this '{'
>  extern "C" {
>             ^
>
> The patch avoids using a note if the tokens are on the same line,
> highlighting the unclosed open token with an underline:
>
> test.c:3:32: error: expected ')' before ';' token
>    return ((b * b) - (4 * a * c);
>           ~                     ^
>
> The bulk of the changes in the patch are to the parsers, done using
> new classes "matching_braces" and "matching_parens", which stash the
> location of the opening token during parsing, so that e.g.:
>
>   if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
>      return;
>   ...do stuff...
>   c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>");
>
> becomes:
>
>   matching_parens parens;
>   if (!parens.require_open (parser))
>      return;
>   ...do stuff...
>   parens.require_close (parser);
>
> The exact implementation of these classes varies somewhat between the
> C and C++ frontends, to deal with implementation differences between
> them (I tried to keep them as similar as possible).
>
> Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu;
> adds 23 PASS results to gcc.sum; adds 99 PASS results to g++.sum.
>
> OK for trunk?
>
>  gcc/c-family/c-common.c                            |  17 +-
>  gcc/c-family/c-common.h                            |   3 +-
>  gcc/c/c-parser.c                                   | 647 ++++++++++------
>  gcc/c/c-parser.h                                   |   8 +-
>  gcc/cp/parser.c                                    | 821 ++++++++++++++-------
>  gcc/testsuite/c-c++-common/missing-close-symbol.c  |  33 +
>  gcc/testsuite/c-c++-common/missing-symbol.c        |  50 ++
>  .../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 +-
>  gcc/testsuite/gcc.dg/unclosed-init.c               |   3 +
>  13 files changed, 1084 insertions(+), 513 deletions(-)
>  create mode 100644 gcc/testsuite/c-c++-common/missing-close-symbol.c
>  create mode 100644 gcc/testsuite/c-c++-common/missing-symbol.c
>  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
>  create mode 100644 gcc/testsuite/gcc.dg/unclosed-init.c
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/3] C/C++: show pertinent open token when missing a close token
  2017-07-11 17:28 ` [PATCH 0/3] C/C++: show pertinent open token when missing a close token Martin Sebor
@ 2017-07-11 18:32   ` David Malcolm
  2017-07-11 19:30     ` Martin Sebor
  0 siblings, 1 reply; 27+ messages in thread
From: David Malcolm @ 2017-07-11 18:32 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches

On Tue, 2017-07-11 at 11:28 -0600, Martin Sebor wrote:
> On 07/11/2017 09:24 AM, David Malcolm wrote:
> > [This patch kit is effectively just one patch; I've split it up
> > into
> >  3 parts, in the hope of making it easier to review:
> >  the c-family parts, the C parts, and the C++ parts]
> > 
> > This patch adds a hint to the user to various errors generated
> > in the C frontend by:
> > 
> >   c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")
> >   c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, "expected
> > %<}%>")
> > 
> > etc (where there's a non-NULL msgid), and in the C++ frontend by:
> > 
> >   cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
> >   cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE)
> > 
> > The hint shows the user where the pertinent open paren or open
> > brace
> > is, which ought to be very helpful for complicated nested
> > collections
> > of parentheses, and somewhat helpful even for simple cases;
> 
> I've played with the patch a bit.  First, let me say that I like
> how it associates the curlies.  I agree that it will be helpful.
> There are other cases where highlighting mismatched or missing
> tokens might be useful, such as for pairs of < and > in complex
> template declarations.

Indeed; braces and parens seemed most useful; my plan is to leave
< > and [ ] for followups.

> But I mainly experimented with it to see if I could get it to
> manifest some of the same symptoms I described in bug 81269.
> I'm not sure it does reproduce the exact same thing or if it's
> a feature, so let me use this as an opportunity to ask.  Given
> something like
> 
>    namespace {
> 
>    enum { e
>    <EOF>
> 
> I see this output:
> 
>    a.C:3:8: error: expected ‘}’ at end of input
>     enum { e
>          ~ ^
>    a.C:3:8: error: expected unqualified-id at end of input
>    a.C:3:8: error: expected ‘}’ at end of input
>    a.C:1:11: note: to match this ‘{’
>     namespace {
>               ^

I think this is an issue with how the diagnostics subsystem chooses
colors; it looks like it's time to rethink that.

Here's what's currently going on:

The color-selection is done in class colorizer in diagnostic-show
-locus.c; the exact colors are in diagnostic-colors.c

> with the first open curly/caret in green, 

This is range 1 within its rich_location, and so colorizer uses state
1, and hence uses the color named "range1", which is implemented as
COLOR_FG_GREEN aka "range1=32" in GCC_COLORS.


> the 'e' in red, and
> the last open curly/caret in cyan.

This is range 0 within its rich_location, and so colorizer uses the
state 0:
      /* Make range 0 be the same color as the "kind" text
	 (error vs warning vs note).  */
This diagnostic is an error, and hence it uses the color named "error",
which is bold red.


> Is the green color intended?  And if yes, what is the intent of
> distinguishing it from the red 'e'?  I note that the caret is
> red (and there are no other colors in the output) in this case:
> 
>    namespace { enum {
>    <EOF>
> 
> but becomes green again when I add an enumerator:
> 
>    namespace { enum { e
>    <EOF>

Is there an extra line here that you trimmed?  Presumably in this case
the '{' is being underlined with a "~", and the "e" has the "^" (the
caret)?

In this case, the red is used for that of the caret, and the green for
the secondary location.

I picked this system for coloring the source and annotations in gcc 6
(IIRC), but it seems garish to me now; I think I now favor emulating
what clang does, which is to *not* color the printed source lines, and
to use just one color (green) for underlines and carets.

Can I use PR 81269 for tracking a refresh of how we do colorization in
diagnostics?


> I ask because in the test case in 81269, highlighting the different
> tokens in the three colors seems especially confusing and I'd like
> to better understand if it's intentional (and what it means).
> 
> Incidentally, I tried to make use of this feature in the middle
> end (in gimple-ssa-sprintf.c), to achieve the same effect as
> -Wrestric does, but it led to even stranger-looking results so
> I went back to using plain old warning.   See the attachment to
> bug 81269:
> https://gcc.gnu.org/bugzilla/attachment.cgi?id=41660

Looks like you're seeing a different bug there: you're seeing:

  sprintf (d, "%s%s%s", d, d + 1, d + 2);
  ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

with weird-looking colorization of some arguments.

I think what's going on is we have:

primary location:
  sprintf (d, "%s%s%s", d, d + 1, d + 2);
	   ^

secondary location 1:
  sprintf (d, "%s%s%s", d, d + 1, d + 2);
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
to cover:               ^
but this PARAM_DECL usage doesn't have a location, and so
it uses the whole of the call

secondary location 1:
  sprintf (d, "%s%s%s", d, d + 1, d + 2);
                           ~~^~~

The bug here I think is that diagnostic_show_locus is printing all of these annotation on top of each other, rather than trying to add new lines:

  sprintf (d, "%s%s%s", d, d + 1, d + 2);
	   ^                                 (for loc 0)
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      (for loc 1)
                           ~~^~~             (for loc 2)

or:

  sprintf (d, "%s%s%s", d, d + 1, d + 2);
	   ^               ~~^~~             (for locs 0 and 2)
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      (for loc 1)

Although the ideal here would be for the usages of "d" to have their own locations, giving:

  sprintf (d, "%s%s%s", d, d + 1, d + 2);
           ^            ^  ~~^~~

(Not sure if it's worth implementing the extra smarts within diagnostic_show_locus vs trying to fix things so that uses of PARM_DELCs have their own source locations).


> Martin

[...snip...]

Thanks for trying the patch
Dave

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/3] C/C++: show pertinent open token when missing a close token
  2017-07-11 18:32   ` David Malcolm
@ 2017-07-11 19:30     ` Martin Sebor
  0 siblings, 0 replies; 27+ messages in thread
From: Martin Sebor @ 2017-07-11 19:30 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 07/11/2017 12:32 PM, David Malcolm wrote:
> On Tue, 2017-07-11 at 11:28 -0600, Martin Sebor wrote:
>> On 07/11/2017 09:24 AM, David Malcolm wrote:
>>> [This patch kit is effectively just one patch; I've split it up
>>> into
>>>  3 parts, in the hope of making it easier to review:
>>>  the c-family parts, the C parts, and the C++ parts]
>>>
>>> This patch adds a hint to the user to various errors generated
>>> in the C frontend by:
>>>
>>>   c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")
>>>   c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, "expected
>>> %<}%>")
>>>
>>> etc (where there's a non-NULL msgid), and in the C++ frontend by:
>>>
>>>   cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
>>>   cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE)
>>>
>>> The hint shows the user where the pertinent open paren or open
>>> brace
>>> is, which ought to be very helpful for complicated nested
>>> collections
>>> of parentheses, and somewhat helpful even for simple cases;
>>
>> I've played with the patch a bit.  First, let me say that I like
>> how it associates the curlies.  I agree that it will be helpful.
>> There are other cases where highlighting mismatched or missing
>> tokens might be useful, such as for pairs of < and > in complex
>> template declarations.
>
> Indeed; braces and parens seemed most useful; my plan is to leave
> < > and [ ] for followups.
>
>> But I mainly experimented with it to see if I could get it to
>> manifest some of the same symptoms I described in bug 81269.
>> I'm not sure it does reproduce the exact same thing or if it's
>> a feature, so let me use this as an opportunity to ask.  Given
>> something like
>>
>>    namespace {
>>
>>    enum { e
>>    <EOF>
>>
>> I see this output:
>>
>>    a.C:3:8: error: expected ‘}’ at end of input
>>     enum { e
>>          ~ ^
>>    a.C:3:8: error: expected unqualified-id at end of input
>>    a.C:3:8: error: expected ‘}’ at end of input
>>    a.C:1:11: note: to match this ‘{’
>>     namespace {
>>               ^
>
> I think this is an issue with how the diagnostics subsystem chooses
> colors; it looks like it's time to rethink that.
>
> Here's what's currently going on:
>
> The color-selection is done in class colorizer in diagnostic-show
> -locus.c; the exact colors are in diagnostic-colors.c
>
>> with the first open curly/caret in green,
>
> This is range 1 within its rich_location, and so colorizer uses state
> 1, and hence uses the color named "range1", which is implemented as
> COLOR_FG_GREEN aka "range1=32" in GCC_COLORS.
>
>
>> the 'e' in red, and
>> the last open curly/caret in cyan.
>
> This is range 0 within its rich_location, and so colorizer uses the
> state 0:
>       /* Make range 0 be the same color as the "kind" text
> 	 (error vs warning vs note).  */
> This diagnostic is an error, and hence it uses the color named "error",
> which is bold red.

I see.  Thanks for the detailed explanation!  It cleared things
up for me.

>
>
>> Is the green color intended?  And if yes, what is the intent of
>> distinguishing it from the red 'e'?  I note that the caret is
>> red (and there are no other colors in the output) in this case:
>>
>>    namespace { enum {
>>    <EOF>
>>
>> but becomes green again when I add an enumerator:
>>
>>    namespace { enum { e
>>    <EOF>
>
> Is there an extra line here that you trimmed?  Presumably in this case
> the '{' is being underlined with a "~", and the "e" has the "^" (the
> caret)?

The source file has just one line in both instances, but yes,
the underlining is as you described.

>
> In this case, the red is used for that of the caret, and the green for
> the secondary location.

Aha!  I think I understand now.  In the first case there is no
green because both the primary and the secondary location are
the unmatched open curly.  That makes sense, though it's not
quite obvious (I haven't worked with secondary locations yet
or noticed them in other diagnostics).

If you don't completely rework things (and even if you do) it
would be helpful to document this in more detail in the manual.
E.g., describe what range1 and range2 (and other less obvious
elements) are used for and show a couple of examples.  Or for
GCC developers, add some of this detail to one of the Wiki
pages.

> I picked this system for coloring the source and annotations in gcc 6
> (IIRC), but it seems garish to me now; I think I now favor emulating
> what clang does, which is to *not* color the printed source lines, and
> to use just one color (green) for underlines and carets.

I'm sure it's a matter of personal preference but overall I like
the GCC highlighting scheme better.  It makes it clear what the
source of the problem is.  Especially with fix-it hints in green,
it makes it clear what's "good" and what's "bad."

>
> Can I use PR 81269 for tracking a refresh of how we do colorization in
> diagnostics?

Sure, feel free to use it however you see fit.

>
>
>> I ask because in the test case in 81269, highlighting the different
>> tokens in the three colors seems especially confusing and I'd like
>> to better understand if it's intentional (and what it means).
>>
>> Incidentally, I tried to make use of this feature in the middle
>> end (in gimple-ssa-sprintf.c), to achieve the same effect as
>> -Wrestric does, but it led to even stranger-looking results so
>> I went back to using plain old warning.   See the attachment to
>> bug 81269:
>> https://gcc.gnu.org/bugzilla/attachment.cgi?id=41660
>
> Looks like you're seeing a different bug there: you're seeing:
>
>   sprintf (d, "%s%s%s", d, d + 1, d + 2);
>   ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> with weird-looking colorization of some arguments.
>
> I think what's going on is we have:
>
> primary location:
>   sprintf (d, "%s%s%s", d, d + 1, d + 2);
> 	   ^
>
> secondary location 1:
>   sprintf (d, "%s%s%s", d, d + 1, d + 2);
>   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> to cover:               ^
> but this PARAM_DECL usage doesn't have a location, and so
> it uses the whole of the call
>
> secondary location 1:
>   sprintf (d, "%s%s%s", d, d + 1, d + 2);
>                            ~~^~~
>
> The bug here I think is that diagnostic_show_locus is printing all of these annotation on top of each other, rather than trying to add new lines:
>
>   sprintf (d, "%s%s%s", d, d + 1, d + 2);
> 	   ^                                 (for loc 0)
>   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      (for loc 1)
>                            ~~^~~             (for loc 2)
>
> or:
>
>   sprintf (d, "%s%s%s", d, d + 1, d + 2);
> 	   ^               ~~^~~             (for locs 0 and 2)
>   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      (for loc 1)
>
> Although the ideal here would be for the usages of "d" to have their own locations, giving:
>
>   sprintf (d, "%s%s%s", d, d + 1, d + 2);
>            ^            ^  ~~^~~
>

That could be.  The effect I was hoping for was close to this

   https://gcc.gnu.org/bugzilla/attachment.cgi?id=41659

except either with all arguments in the same color, or with
the first in the primary color and the rest in the secondary
color.  I  don't quite understand where the cyan comes from.
Maybe the caller (warn_for_restrict in c-family/c-warn.c)
needs to do some more work to make it look like that?

> (Not sure if it's worth implementing the extra smarts within diagnostic_show_locus vs trying to fix things so that uses of PARM_DELCs have their own source locations).

The latter please :)

Despite some of these minor glitches what GCC does today is
fantastic -- thank you!

Martin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] matching tokens: C++ parts
  2017-07-11 14:51 ` [PATCH 3/3] matching tokens: C++ parts David Malcolm
@ 2017-07-12 13:13   ` Trevor Saunders
  2017-07-12 15:12     ` Martin Sebor
  2017-08-01 19:47     ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token David Malcolm
  0 siblings, 2 replies; 27+ messages in thread
From: Trevor Saunders @ 2017-07-12 13:13 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On Tue, Jul 11, 2017 at 11:24:45AM -0400, David Malcolm wrote:
> +/* 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 token_pair_traits_t>

the style guide says template arguments should be in mixed case, so
TokenPairTraits, and the _t looks odd to my eyes.

> +class token_pair
> +{
> + private:
> +  typedef token_pair_traits_t traits_t;

I'm not really sure what this is about, you can name it whatever you
like as a template argument, and this name seems less descriptive of
what its about.

> + public:
> +  /* token_pair's ctor.  */
> +  token_pair () : m_open_loc (UNKNOWN_LOCATION) {}

What do you think of passing the parser to the ctor, and dropping it
from the other arguments?  It doesn't seem to make sense to support
passing in different parsers?

> +  /* 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
> +     corrsponding opening token, and return NULL.  */

typo.

> +/* A subclass of token_pair for tracking matching pairs of parentheses.  */
> +
> +class matching_parens : public token_pair<matching_parens>

It seems a little strange for this class to both subclass and be the
traits, given that the token_pair class doesn't need objeects of the
template argument type.  I'd consider writing this as

struct matching_paren_traits
{
  static const cpp_ttype open_token_type = CPP_OPEN_PAREN;
  ...
};

typedef token_pair<matching_paren_traits> matching_parens;

> +{
> + public:
> +  static const enum cpp_ttype open_token_type;
> +  static const enum required_token required_token_open;
> +  static const enum cpp_ttype close_token_type;
> +  static const enum required_token required_token_close;

Given that these are static consts of integer type I think its fine to
define them inline in the class.

> +class matching_braces : public token_pair<matching_braces>

same comments here.

thanks

Trev

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] matching tokens: C++ parts
  2017-07-12 13:13   ` Trevor Saunders
@ 2017-07-12 15:12     ` Martin Sebor
  2017-07-16 17:55       ` Trevor Saunders
  2017-08-01 19:47     ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token David Malcolm
  1 sibling, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2017-07-12 15:12 UTC (permalink / raw)
  To: Trevor Saunders, David Malcolm; +Cc: gcc-patches

On 07/12/2017 07:13 AM, Trevor Saunders wrote:
> On Tue, Jul 11, 2017 at 11:24:45AM -0400, David Malcolm wrote:
>> +/* 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 token_pair_traits_t>
>
> the style guide says template arguments should be in mixed case, so
> TokenPairTraits, and the _t looks odd to my eyes.
>
>> +class token_pair
>> +{
>> + private:
>> +  typedef token_pair_traits_t traits_t;
>
> I'm not really sure what this is about, you can name it whatever you
> like as a template argument, and this name seems less descriptive of
> what its about.

In generic code, a typedef for a template parameter makes it
possible to refer to the parameter even when it's a member of
a type whose template parameters aren't known (or that's not
even a template).  In the C++ standard library the naming
convention is to end such typedefs with _type (e.g., value_type,
allocator_type, etc.)  GCC itself makes use of this convention
in its hash_table template. (I have no idea if token_pair is
ever used in type generic contexts where the typedef is needed.)

As an aside, it's interesting to note that names that end in _t
are reserved by POSIX, so (purely) pedantically speaking, making
use of them for own symbols is undefined (this is probably one
of the most commonly abused POSIX requirements; even the C++
standard flagrantly disregards it).

Martin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] matching tokens: C++ parts
  2017-07-12 15:12     ` Martin Sebor
@ 2017-07-16 17:55       ` Trevor Saunders
  0 siblings, 0 replies; 27+ messages in thread
From: Trevor Saunders @ 2017-07-16 17:55 UTC (permalink / raw)
  To: Martin Sebor; +Cc: David Malcolm, gcc-patches

On Wed, Jul 12, 2017 at 09:11:55AM -0600, Martin Sebor wrote:
> On 07/12/2017 07:13 AM, Trevor Saunders wrote:
> > On Tue, Jul 11, 2017 at 11:24:45AM -0400, David Malcolm wrote:
> > > +/* 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 token_pair_traits_t>
> > 
> > the style guide says template arguments should be in mixed case, so
> > TokenPairTraits, and the _t looks odd to my eyes.
> > 
> > > +class token_pair
> > > +{
> > > + private:
> > > +  typedef token_pair_traits_t traits_t;
> > 
> > I'm not really sure what this is about, you can name it whatever you
> > like as a template argument, and this name seems less descriptive of
> > what its about.
> 
> In generic code, a typedef for a template parameter makes it
> possible to refer to the parameter even when it's a member of
> a type whose template parameters aren't known (or that's not
> even a template).  In the C++ standard library the naming
> convention is to end such typedefs with _type (e.g., value_type,
> allocator_type, etc.)  GCC itself makes use of this convention
> in its hash_table template. (I have no idea if token_pair is
> ever used in type generic contexts where the typedef is needed.)

yes, I was asking because I strongly suspect it is not, and if someday
that turns out to be wrong we can add it then.

> As an aside, it's interesting to note that names that end in _t
> are reserved by POSIX, so (purely) pedantically speaking, making
> use of them for own symbols is undefined (this is probably one
> of the most commonly abused POSIX requirements; even the C++
> standard flagrantly disregards it).

heh, ok.

Trev

> 
> Martin

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] matching tokens: c-family parts
  2017-07-11 14:51 ` [PATCH 1/3] matching tokens: c-family parts David Malcolm
@ 2017-07-18 17:23   ` Marek Polacek
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Polacek @ 2017-07-18 17:23 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On Tue, Jul 11, 2017 at 11:24:43AM -0400, David Malcolm wrote:
> OK for trunk? (assuming the rest is approved)
 
This is ok.  I'll have to play with this some more before I approve the C part.

Thanks,

	Marek

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 3/3] matching tokens: C++ parts (v2)
  2017-08-01 19:47     ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token David Malcolm
  2017-08-01 19:47       ` [PATCH 2/3] Matching tokens: C parts (v2) David Malcolm
@ 2017-08-01 19:47       ` David Malcolm
  2017-08-07 18:25         ` Jason Merrill
  2017-08-01 19:47       ` [PATCH 1/3] matching tokens: c-family parts David Malcolm
                         ` (2 subsequent siblings)
  4 siblings, 1 reply; 27+ messages in thread
From: David Malcolm @ 2017-08-01 19:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: Trevor Saunders, David Malcolm

Changed in v2:

* Renamed template argument to traits_t; eliminated subclasses, just
  using traits struct.
* Moved constants into struct bodies.
* Fixed typo.

OK for trunk?

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_postfix_expression): Likewise.
	(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.
	(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, 539 insertions(+), 287 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

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 88d0b2b..efbc376 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,100 @@ 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;
+  }
+
+  /* Peek the next token from PARSER, recording its location as
+     that of the opening token within the pair.  */
+
+  void peek_open (cp_parser *parser)
+  {
+    cp_token *tok = cp_lexer_peek_token (parser->lexer);
+    gcc_assert (tok->type == traits_t::open_token_type);
+    m_open_loc = tok->location;
+  }
+
+  /* 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 +4608,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 +4618,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 +5024,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 +5073,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 +5195,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 +5210,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,
@@ -6465,7 +6563,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 +6625,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 +6642,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 +6656,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;
@@ -7561,7 +7659,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 +7756,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 +8056,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 +8091,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 +8335,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 +8348,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 +8888,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 +8954,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 +9739,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 +9790,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 +9936,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 +9976,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 +10457,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 +10473,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 +10630,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 +10672,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 +10696,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 +11232,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 +11251,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 +11372,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 +11402,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 +12189,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 +12219,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 +12235,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 +12591,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 +13825,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 +13882,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 +13915,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 +14069,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 +14078,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 +14133,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 +14832,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 +18212,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 +18246,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 +18539,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 +19927,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 +19943,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 +20039,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 +20051,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 +21899,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 +21917,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 +22344,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 +22397,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 +23876,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 +24137,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 +24158,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 +24212,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 +24249,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 +24396,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 +24516,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 +24575,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 +24834,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 +24850,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);
@@ -25144,7 +25275,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;
@@ -25177,7 +25309,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;
@@ -25409,12 +25541,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;
@@ -25427,12 +25560,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;
@@ -25571,14 +25705,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. */
@@ -26649,7 +26784,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;
@@ -26669,7 +26805,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)
@@ -27554,9 +27690,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");
@@ -27581,7 +27718,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;
 }
@@ -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);
+
       /* We can't be sure yet whether we're looking at a type-id or an
 	 expression.  */
       cp_parser_parse_tentatively (parser);
@@ -27648,7 +27788,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;
 	}
 
@@ -27930,187 +28070,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);
@@ -28118,7 +28316,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;
     }
 }
@@ -28220,7 +28419,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;
     }
 
@@ -29031,10 +29231,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)
     {
@@ -29076,9 +29277,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);
 }
@@ -29097,9 +29299,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)
@@ -29137,7 +29340,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
@@ -29184,7 +29388,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:
@@ -29391,7 +29595,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
@@ -29417,7 +29622,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);
     }
 
@@ -30031,7 +30236,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.  */
@@ -30043,7 +30249,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);
     }
 }
 
@@ -30231,9 +30437,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))
 	{
@@ -30259,7 +30466,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
@@ -30313,9 +30520,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.  */
@@ -30556,7 +30764,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)
 	{
@@ -30644,7 +30853,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,
@@ -31335,13 +31544,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,
@@ -31388,7 +31598,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
 	{
@@ -31462,7 +31673,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;
     }
 
@@ -31608,12 +31819,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);
@@ -31652,7 +31864,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))
     {
@@ -31695,7 +31908,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);
@@ -31719,13 +31932,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);
@@ -31756,7 +31970,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))
@@ -31839,7 +32054,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);
@@ -31934,13 +32149,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);
@@ -31964,13 +32180,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);
@@ -31994,13 +32211,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);
@@ -32024,13 +32242,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);
@@ -32054,13 +32273,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);
@@ -32084,7 +32304,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))
@@ -32116,7 +32337,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",
@@ -32151,11 +32372,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);
@@ -32312,7 +32534,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);
@@ -32406,7 +32629,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))
@@ -32512,13 +32735,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);
@@ -32542,13 +32766,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);
@@ -32573,7 +32798,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,
@@ -32583,7 +32809,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);
@@ -32614,7 +32840,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
@@ -32644,7 +32871,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);
@@ -32682,7 +32909,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);
@@ -32709,13 +32936,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);
@@ -32738,13 +32966,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);
@@ -32860,7 +33089,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))
@@ -32892,7 +33122,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);
@@ -33023,13 +33253,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);
@@ -33054,7 +33285,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);
@@ -33073,7 +33305,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))
@@ -33159,11 +33391,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);
@@ -34213,12 +34446,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);
@@ -34716,7 +34950,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;
@@ -34848,7 +35083,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);
@@ -35207,7 +35442,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 ();
@@ -35244,7 +35480,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);
 
@@ -37106,7 +37342,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 = "";
@@ -37197,12 +37434,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;
 }
@@ -37630,7 +37868,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
@@ -37647,7 +37886,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;
@@ -38079,12 +38318,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
     {
@@ -38672,7 +38912,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);
@@ -38709,7 +38950,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);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 2/3] Matching tokens: C parts (v2)
  2017-08-01 19:47     ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token David Malcolm
@ 2017-08-01 19:47       ` David Malcolm
  2017-08-03 17:34         ` Jeff Law
  2017-08-01 19:47       ` [PATCH 3/3] matching tokens: C++ " David Malcolm
                         ` (3 subsequent siblings)
  4 siblings, 1 reply; 27+ messages in thread
From: David Malcolm @ 2017-08-01 19:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: Trevor Saunders, David Malcolm

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

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" } */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 1/3] matching tokens: c-family parts
  2017-08-01 19:47     ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token David Malcolm
  2017-08-01 19:47       ` [PATCH 2/3] Matching tokens: C parts (v2) David Malcolm
  2017-08-01 19:47       ` [PATCH 3/3] matching tokens: C++ " David Malcolm
@ 2017-08-01 19:47       ` David Malcolm
  2017-08-03 17:22         ` Jeff Law
  2017-08-02  3:03       ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token Trevor Saunders
  2017-08-10 13:39       ` [committed, v3] " David Malcolm
  4 siblings, 1 reply; 27+ messages in thread
From: David Malcolm @ 2017-08-01 19:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: Trevor Saunders, David Malcolm

(Unchanged since v1; already approved by Marek, assuming rest is approved)

gcc/c-family/ChangeLog:
	* c-common.c (c_parse_error): Add rich_location * param, using it
	rather implicitly using input_location.
	* c-common.h (c_parse_error): Add rich_location * param.

gcc/testsuite/ChangeLog:
	* c-c++-common/missing-close-symbol.c: New test case.
	* c-c++-common/missing-symbol.c: New test case.
---
 gcc/c-family/c-common.c                           | 17 ++++----
 gcc/c-family/c-common.h                           |  3 +-
 gcc/testsuite/c-c++-common/missing-close-symbol.c | 33 +++++++++++++++
 gcc/testsuite/c-c++-common/missing-symbol.c       | 50 +++++++++++++++++++++++
 4 files changed, 94 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/missing-close-symbol.c
 create mode 100644 gcc/testsuite/c-c++-common/missing-symbol.c

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index feb0904..30b0603 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5949,12 +5949,13 @@ catenate_strings (const char *lhs, const char *rhs_start, int rhs_size)
   return result;
 }
 
-/* Issue the error given by GMSGID, indicating that it occurred before
-   TOKEN, which had the associated VALUE.  */
+/* Issue the error given by GMSGID at RICHLOC, indicating that it occurred
+   before TOKEN, which had the associated VALUE.  */
 
 void
 c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
-	       tree value, unsigned char token_flags)
+	       tree value, unsigned char token_flags,
+	       rich_location *richloc)
 {
 #define catenate_messages(M1, M2) catenate_strings ((M1), (M2), sizeof (M2))
 
@@ -5995,7 +5996,7 @@ c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
       else
 	message = catenate_messages (gmsgid, " before %s'\\x%x'");
 
-      error (message, prefix, val);
+      error_at_rich_loc (richloc, message, prefix, val);
       free (message);
       message = NULL;
     }
@@ -6023,7 +6024,7 @@ c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
   else if (token_type == CPP_NAME)
     {
       message = catenate_messages (gmsgid, " before %qE");
-      error (message, value);
+      error_at_rich_loc (richloc, message, value);
       free (message);
       message = NULL;
     }
@@ -6036,16 +6037,16 @@ c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
   else if (token_type < N_TTYPES)
     {
       message = catenate_messages (gmsgid, " before %qs token");
-      error (message, cpp_type2name (token_type, token_flags));
+      error_at_rich_loc (richloc, message, cpp_type2name (token_type, token_flags));
       free (message);
       message = NULL;
     }
   else
-    error (gmsgid);
+    error_at_rich_loc (richloc, gmsgid);
 
   if (message)
     {
-      error (message);
+      error_at_rich_loc (richloc, message);
       free (message);
     }
 #undef catenate_messages
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index a29f1ad..63fe845 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1124,7 +1124,8 @@ extern void builtin_define_with_int_value (const char *, HOST_WIDE_INT);
 extern void builtin_define_type_sizeof (const char *, tree);
 extern void c_stddef_cpp_builtins (void);
 extern void fe_file_change (const line_map_ordinary *);
-extern void c_parse_error (const char *, enum cpp_ttype, tree, unsigned char);
+extern void c_parse_error (const char *, enum cpp_ttype, tree, unsigned char,
+			   rich_location *richloc);
 
 /* In c-ppoutput.c  */
 extern void init_pp_output (FILE *);
diff --git a/gcc/testsuite/c-c++-common/missing-close-symbol.c b/gcc/testsuite/c-c++-common/missing-close-symbol.c
new file mode 100644
index 0000000..85b96f28
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/missing-close-symbol.c
@@ -0,0 +1,33 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+/* Verify that the C/C++ frontends show the pertinent opening symbol when
+   a closing symbol is missing.  */
+
+/* Verify that, when they are on the same line, that the opening symbol is
+   shown as a secondary range within the main diagnostic.  */
+
+void test_static_assert_same_line (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char), "msg"; /* { dg-error "expected '\\)' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char), "msg";
+                 ~                                  ^
+     { dg-end-multiline-output "" } */
+}
+
+/* Verify that, when they are on different lines, that the opening symbol is
+   shown via a secondary diagnostic.  */
+
+void test_static_assert_different_line (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char), /* { dg-message "to match this '\\('" } */
+		 "msg"; /* { dg-error "expected '\\)' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+    "msg";
+         ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char),
+                 ^
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/c-c++-common/missing-symbol.c b/gcc/testsuite/c-c++-common/missing-symbol.c
new file mode 100644
index 0000000..33a501b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/missing-symbol.c
@@ -0,0 +1,50 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+extern int foo (void);
+extern int bar (void);
+
+int missing_close_paren_in_switch (int i)
+{
+  switch (i /* { dg-message "10: to match this '\\('" } */
+    { /* { dg-error "5: expected '\\)' before '.' token" } */
+  /* { dg-begin-multiline-output "" }
+     {
+     ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   switch (i
+          ^
+     { dg-end-multiline-output "" } */
+
+    case 0:
+      return 5;
+    default:
+      return i;
+    }
+} /* { dg-error "1: expected" } */
+  /* { dg-begin-multiline-output "" }
+ }
+ ^
+     { dg-end-multiline-output "" } */
+
+void missing_close_paren_in_if (void)
+{
+  if (foo () /* { dg-line start_of_if } */
+      && bar () 
+    { /* { dg-error "5: expected '\\)' before '.' token" } */
+      /* { dg-begin-multiline-output "" }
+     {
+     ^
+         { dg-end-multiline-output "" } */
+      /* { dg-message "6: to match this '\\('" "" { target *-*-* } start_of_if } */
+      /* { dg-begin-multiline-output "" }
+   if (foo ()
+      ^
+      { dg-end-multiline-output "" } */
+    }
+
+} /* { dg-error "1: expected" } */
+  /* { dg-begin-multiline-output "" }
+ }
+ ^
+     { dg-end-multiline-output "" } */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token
  2017-07-12 13:13   ` Trevor Saunders
  2017-07-12 15:12     ` Martin Sebor
@ 2017-08-01 19:47     ` David Malcolm
  2017-08-01 19:47       ` [PATCH 2/3] Matching tokens: C parts (v2) David Malcolm
                         ` (4 more replies)
  1 sibling, 5 replies; 27+ messages in thread
From: David Malcolm @ 2017-08-01 19:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: Trevor Saunders, David Malcolm

On Wed, 2017-07-12 at 09:13 -0400, Trevor Saunders wrote:
> On Tue, Jul 11, 2017 at 11:24:45AM -0400, David Malcolm wrote:
> > +/* 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 token_pair_traits_t>
> 
> the style guide says template arguments should be in mixed case, so
> TokenPairTraits, and the _t looks odd to my eyes.

My thinking here was to have a way to refer to the template arg
in other locations, but, yes, it turns out not to be necessary.

> > +class token_pair
> > +{
> > + private:
> > +  typedef token_pair_traits_t traits_t;
> 
> I'm not really sure what this is about, you can name it whatever you
> like as a template argument, and this name seems less descriptive of
> what its about.

Fixed by renaming the arg to just "traits_t".

> > + public:
> > +  /* token_pair's ctor.  */
> > +  token_pair () : m_open_loc (UNKNOWN_LOCATION) {}
> 
> What do you think of passing the parser to the ctor, and dropping it
> from the other arguments?  It doesn't seem to make sense to support
> passing in different parsers?

I'm in two minds about this:

(a) yes: the parser ptr is always going to unchanged, and could be
    passed in to the ctor, which would avoid needing to pass it in
    everywhere the token_pair<T> instancd is used

(b) is the optimizer good enough to realize this, and avoid storing
    a second copy of the parser ptr on the stack?  (presumably to
    optimize away the 2nd copy of the parser ptr, every call to methods
    of the class would need to be inlined, and then the two on-stack
    member fields be split up into individual fields, and then copy
    propagation could eliminate it).

In this version of the kit I opted to keep passing in the parser ptr
at all the usage sites, but I'm open to making it a field of the
class.

I'm hoping for input on this from the C/C++ frontend maintainers.

> > +  /* 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
> > +     corrsponding opening token, and return NULL.  */
> 
> typo.

Thanks; fixed (in both C and C++ frontends).

> > +/* A subclass of token_pair for tracking matching pairs of
> > parentheses.  */
> > +
> > +class matching_parens : public token_pair<matching_parens>
> 
> It seems a little strange for this class to both subclass and be the
> traits, given that the token_pair class doesn't need objeects of the
> template argument type.  I'd consider writing this as
> 
> struct matching_paren_traits
> {
>   static const cpp_ttype open_token_type = CPP_OPEN_PAREN;
>   ...
> };
> 
> typedef token_pair<matching_paren_traits> matching_parens;

Done (for both C and C++ frontends).

> > +{
> > + public:
> > +  static const enum cpp_ttype open_token_type;
> > +  static const enum required_token required_token_open;
> > +  static const enum cpp_ttype close_token_type;
> > +  static const enum required_token required_token_close;
> 
> Given that these are static consts of integer type I think its fine
> to
> define them inline in the class.

Done, though in the C frontend there are some const char * static
consts which have to be defined outside (since constexpr is guaranteed
to be available to us).

> > +class matching_braces : public token_pair<matching_braces>
> 
> same comments here.
> 
> thanks
> 
> Trev

Thanks.

Updated patch kit follows.

Successfully bootstrapped&regrtested the combination of the patches
on x86_64-pc-linux-gnu (as before I've split up the patches for
ease-of-review; they're not independent of each other).

Patch 1 is already approved; are patches 2 and 3 OK for trunk?

Thanks
Dave


David Malcolm (3):
  matching tokens: c-family parts
  Matching tokens: C parts (v2)
  matching tokens: C++ parts (v2)

 gcc/c-family/c-common.c                            |  17 +-
 gcc/c-family/c-common.h                            |   3 +-
 gcc/c/c-parser.c                                   | 644 ++++++++++------
 gcc/c/c-parser.h                                   |   8 +-
 gcc/cp/parser.c                                    | 811 +++++++++++++--------
 gcc/testsuite/c-c++-common/missing-close-symbol.c  |  33 +
 gcc/testsuite/c-c++-common/missing-symbol.c        |  50 ++
 .../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 +-
 gcc/testsuite/gcc.dg/unclosed-init.c               |   3 +
 13 files changed, 1071 insertions(+), 513 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/missing-close-symbol.c
 create mode 100644 gcc/testsuite/c-c++-common/missing-symbol.c
 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
 create mode 100644 gcc/testsuite/gcc.dg/unclosed-init.c

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token
  2017-08-01 19:47     ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token David Malcolm
                         ` (2 preceding siblings ...)
  2017-08-01 19:47       ` [PATCH 1/3] matching tokens: c-family parts David Malcolm
@ 2017-08-02  3:03       ` Trevor Saunders
  2017-08-10 13:39       ` [committed, v3] " David Malcolm
  4 siblings, 0 replies; 27+ messages in thread
From: Trevor Saunders @ 2017-08-02  3:03 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On Tue, Aug 01, 2017 at 04:21:41PM -0400, David Malcolm wrote:
> On Wed, 2017-07-12 at 09:13 -0400, Trevor Saunders wrote:
> > On Tue, Jul 11, 2017 at 11:24:45AM -0400, David Malcolm wrote:
> > > + public:
> > > +  /* token_pair's ctor.  */
> > > +  token_pair () : m_open_loc (UNKNOWN_LOCATION) {}
> > 
> > What do you think of passing the parser to the ctor, and dropping it
> > from the other arguments?  It doesn't seem to make sense to support
> > passing in different parsers?
> 
> I'm in two minds about this:
> 
> (a) yes: the parser ptr is always going to unchanged, and could be
>     passed in to the ctor, which would avoid needing to pass it in
>     everywhere the token_pair<T> instancd is used
> 
> (b) is the optimizer good enough to realize this, and avoid storing
>     a second copy of the parser ptr on the stack?  (presumably to
>     optimize away the 2nd copy of the parser ptr, every call to methods
>     of the class would need to be inlined, and then the two on-stack
>     member fields be split up into individual fields, and then copy
>     propagation could eliminate it).

I think storing an extra copy of the parser ptr is the wrong thing to
worry about, its only going to be 8 bytes or so, and you aren't going to
have *that* many of these objects on the stack at any one point.  On the
other hand it might be worth considering which is faster, but I'd be
pretty shocked if these methods are called nearly enough to move this
out of the noise.  I'm not really sure what would be faster in theory,
and can believe it depends on the host arch.  On x86 I could see the
instructions to deal with the parser arg on the stack are slower than
copying the parser pointer into the object.

> In this version of the kit I opted to keep passing in the parser ptr
> at all the usage sites, but I'm open to making it a field of the
> class.
> 
> I'm hoping for input on this from the C/C++ frontend maintainers.

Fair enough, part of me thinks if there is a performance problem its
something we should fix by making gcc optimize better.

So I'd tend to think its better to go for clearer code, but we already
have a ton of explicit parser arguments, so no big deal either way.

Trev

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/3] matching tokens: c-family parts
  2017-08-01 19:47       ` [PATCH 1/3] matching tokens: c-family parts David Malcolm
@ 2017-08-03 17:22         ` Jeff Law
  0 siblings, 0 replies; 27+ messages in thread
From: Jeff Law @ 2017-08-03 17:22 UTC (permalink / raw)
  To: David Malcolm, gcc-patches; +Cc: Trevor Saunders

On 08/01/2017 02:21 PM, David Malcolm wrote:
> (Unchanged since v1; already approved by Marek, assuming rest is approved)
> 
> gcc/c-family/ChangeLog:
> 	* c-common.c (c_parse_error): Add rich_location * param, using it
> 	rather implicitly using input_location.
> 	* c-common.h (c_parse_error): Add rich_location * param.
> 
> gcc/testsuite/ChangeLog:
> 	* c-c++-common/missing-close-symbol.c: New test case.
> 	* c-c++-common/missing-symbol.c: New test case.
LGTM.
jeff

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] Matching tokens: C parts (v2)
  2017-08-01 19:47       ` [PATCH 2/3] Matching tokens: C parts (v2) David Malcolm
@ 2017-08-03 17:34         ` Jeff Law
  2017-08-04 14:32           ` David Malcolm
  0 siblings, 1 reply; 27+ messages in thread
From: Jeff Law @ 2017-08-03 17:34 UTC (permalink / raw)
  To: David Malcolm, gcc-patches; +Cc: Trevor Saunders

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

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] Matching tokens: C parts (v2)
  2017-08-03 17:34         ` Jeff Law
@ 2017-08-04 14:32           ` David Malcolm
  2017-08-04 18:09             ` Jeff Law
  0 siblings, 1 reply; 27+ messages in thread
From: David Malcolm @ 2017-08-04 14:32 UTC (permalink / raw)
  To: Jeff Law, gcc-patches; +Cc: Trevor Saunders

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

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] Matching tokens: C parts (v2)
  2017-08-04 14:32           ` David Malcolm
@ 2017-08-04 18:09             ` Jeff Law
  2017-08-08 20:37               ` David Malcolm
  0 siblings, 1 reply; 27+ messages in thread
From: Jeff Law @ 2017-08-04 18:09 UTC (permalink / raw)
  To: David Malcolm, gcc-patches; +Cc: Trevor Saunders

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

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/3] matching tokens: C++ parts (v2)
  2017-08-01 19:47       ` [PATCH 3/3] matching tokens: C++ " David Malcolm
@ 2017-08-07 18:25         ` Jason Merrill
  2017-08-08 20:26           ` [PATCH] matching tokens: C++ parts (v3) David Malcolm
  0 siblings, 1 reply; 27+ messages in thread
From: Jason Merrill @ 2017-08-07 18:25 UTC (permalink / raw)
  To: David Malcolm, gcc-patches; +Cc: Trevor Saunders

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.

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.

Jason

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH] matching tokens: C++ parts (v3)
  2017-08-07 18:25         ` Jason Merrill
@ 2017-08-08 20:26           ` David Malcolm
  2017-08-08 20:49             ` [PATCH] Changes for v3 of the C++ patch David Malcolm
  2017-08-09 19:26             ` [PATCH] matching tokens: C++ parts (v3) Jason Merrill
  0 siblings, 2 replies; 27+ messages in thread
From: David Malcolm @ 2017-08-08 20:26 UTC (permalink / raw)
  To: Jason Merrill, gcc-patches; +Cc: Trevor Saunders, David Malcolm

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

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);
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] Matching tokens: C parts (v2)
  2017-08-04 18:09             ` Jeff Law
@ 2017-08-08 20:37               ` David Malcolm
  2017-08-09  6:50                 ` Marek Polacek
  0 siblings, 1 reply; 27+ messages in thread
From: David Malcolm @ 2017-08-08 20:37 UTC (permalink / raw)
  To: Jeff Law, gcc-patches; +Cc: Trevor Saunders

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

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH] Changes for v3 of the C++ patch
  2017-08-08 20:26           ` [PATCH] matching tokens: C++ parts (v3) David Malcolm
@ 2017-08-08 20:49             ` David Malcolm
  2017-08-09 19:26             ` [PATCH] matching tokens: C++ parts (v3) Jason Merrill
  1 sibling, 0 replies; 27+ messages in thread
From: David Malcolm @ 2017-08-08 20:49 UTC (permalink / raw)
  To: Jason Merrill, gcc-patches; +Cc: Trevor Saunders, David Malcolm

For reference, here's the diff of the v3 C++ patch to the v2 patch,
in case it's useful:

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.
---
 gcc/cp/parser.c | 32 +++++++++++---------------------
 1 file changed, 11 insertions(+), 21 deletions(-)

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 3037ac7..0da92ab 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -4527,7 +4527,7 @@ class token_pair
   {
     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);
+			      traits_t::required_token_open);
   }
 
   /* Consume the next token from PARSER, recording its location as
@@ -4541,16 +4541,6 @@ class token_pair
     return tok;
   }
 
-  /* Peek the next token from PARSER, recording its location as
-     that of the opening token within the pair.  */
-
-  void peek_open (cp_parser *parser)
-  {
-    cp_token *tok = cp_lexer_peek_token (parser->lexer);
-    gcc_assert (tok->type == traits_t::open_token_type);
-    m_open_loc = tok->location;
-  }
-
   /* 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
@@ -4559,8 +4549,8 @@ class token_pair
   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);
+			      traits_t::required_token_close,
+			      m_open_loc);
   }
 
  private:
@@ -6443,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.
@@ -6857,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))
@@ -6868,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
@@ -27773,12 +27762,13 @@ cp_parser_sizeof_operand (cp_parser* parser, enum rid keyword)
     {
       tree type = NULL_TREE;
 
-      matching_parens parens;
-      parens.peek_open (parser);
-
       /* 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
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/3] Matching tokens: C parts (v2)
  2017-08-08 20:37               ` David Malcolm
@ 2017-08-09  6:50                 ` Marek Polacek
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Polacek @ 2017-08-09  6:50 UTC (permalink / raw)
  To: David Malcolm; +Cc: Jeff Law, gcc-patches, Trevor Saunders

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

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH] matching tokens: C++ parts (v3)
  2017-08-08 20:26           ` [PATCH] matching tokens: C++ parts (v3) David Malcolm
  2017-08-08 20:49             ` [PATCH] Changes for v3 of the C++ patch David Malcolm
@ 2017-08-09 19:26             ` Jason Merrill
  1 sibling, 0 replies; 27+ messages in thread
From: Jason Merrill @ 2017-08-09 19:26 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches List, Trevor Saunders

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

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [committed, v3] C/C++: show pertinent open token when missing a close token
  2017-08-01 19:47     ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token David Malcolm
                         ` (3 preceding siblings ...)
  2017-08-02  3:03       ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token Trevor Saunders
@ 2017-08-10 13:39       ` David Malcolm
  4 siblings, 0 replies; 27+ messages in thread
From: David Malcolm @ 2017-08-10 13:39 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

For reference, here's the combined patch I committed just now (as r251026),
after double-checking bootstrap&regrtest on x86_64-pc-linux-gnu.

Thanks Marek, Trevor, Jason, Jeff, and Martin for review.

Dave

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/c-family/ChangeLog:
	* c-common.c (c_parse_error): Add rich_location * param, using it
	rather implicitly using input_location.
	* c-common.h (c_parse_error): Add rich_location * param.

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:
	* c-c++-common/missing-close-symbol.c: New test case.
	* c-c++-common/missing-symbol.c: New test case.
	* gcc.dg/unclosed-init.c: New test case.
	* 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/c-family/c-common.c                            |  17 +-
 gcc/c-family/c-common.h                            |   3 +-
 gcc/c/c-parser.c                                   | 644 ++++++++++------
 gcc/c/c-parser.h                                   |   8 +-
 gcc/cp/parser.c                                    | 811 +++++++++++++--------
 gcc/testsuite/c-c++-common/missing-close-symbol.c  |  33 +
 gcc/testsuite/c-c++-common/missing-symbol.c        |  50 ++
 .../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 +-
 gcc/testsuite/gcc.dg/unclosed-init.c               |   3 +
 13 files changed, 1066 insertions(+), 518 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/missing-close-symbol.c
 create mode 100644 gcc/testsuite/c-c++-common/missing-symbol.c
 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
 create mode 100644 gcc/testsuite/gcc.dg/unclosed-init.c

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index feb0904..30b0603 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5949,12 +5949,13 @@ catenate_strings (const char *lhs, const char *rhs_start, int rhs_size)
   return result;
 }
 
-/* Issue the error given by GMSGID, indicating that it occurred before
-   TOKEN, which had the associated VALUE.  */
+/* Issue the error given by GMSGID at RICHLOC, indicating that it occurred
+   before TOKEN, which had the associated VALUE.  */
 
 void
 c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
-	       tree value, unsigned char token_flags)
+	       tree value, unsigned char token_flags,
+	       rich_location *richloc)
 {
 #define catenate_messages(M1, M2) catenate_strings ((M1), (M2), sizeof (M2))
 
@@ -5995,7 +5996,7 @@ c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
       else
 	message = catenate_messages (gmsgid, " before %s'\\x%x'");
 
-      error (message, prefix, val);
+      error_at_rich_loc (richloc, message, prefix, val);
       free (message);
       message = NULL;
     }
@@ -6023,7 +6024,7 @@ c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
   else if (token_type == CPP_NAME)
     {
       message = catenate_messages (gmsgid, " before %qE");
-      error (message, value);
+      error_at_rich_loc (richloc, message, value);
       free (message);
       message = NULL;
     }
@@ -6036,16 +6037,16 @@ c_parse_error (const char *gmsgid, enum cpp_ttype token_type,
   else if (token_type < N_TTYPES)
     {
       message = catenate_messages (gmsgid, " before %qs token");
-      error (message, cpp_type2name (token_type, token_flags));
+      error_at_rich_loc (richloc, message, cpp_type2name (token_type, token_flags));
       free (message);
       message = NULL;
     }
   else
-    error (gmsgid);
+    error_at_rich_loc (richloc, gmsgid);
 
   if (message)
     {
-      error (message);
+      error_at_rich_loc (richloc, message);
       free (message);
     }
 #undef catenate_messages
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index a29f1ad..63fe845 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1124,7 +1124,8 @@ extern void builtin_define_with_int_value (const char *, HOST_WIDE_INT);
 extern void builtin_define_type_sizeof (const char *, tree);
 extern void c_stddef_cpp_builtins (void);
 extern void fe_file_change (const line_map_ordinary *);
-extern void c_parse_error (const char *, enum cpp_ttype, tree, unsigned char);
+extern void c_parse_error (const char *, enum cpp_ttype, tree, unsigned char,
+			   rich_location *richloc);
 
 /* In c-ppoutput.c  */
 extern void init_pp_output (FILE *);
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 606c07c..e13bbdc 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;
@@ -4398,7 +4566,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);
@@ -4436,7 +4605,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;
@@ -5459,10 +5628,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;
 }
 
@@ -5731,7 +5901,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)
@@ -5746,7 +5917,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
     {
@@ -5956,7 +6127,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;
@@ -6103,7 +6275,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;
@@ -6197,7 +6369,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);
@@ -6255,7 +6428,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;
@@ -6320,7 +6493,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;
@@ -6328,7 +6502,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;
@@ -6937,9 +7111,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;
@@ -7166,10 +7341,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)
 	{
@@ -7248,11 +7424,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;
@@ -7411,7 +7588,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++;
@@ -7553,7 +7731,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;
@@ -7799,7 +7977,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:
@@ -7836,7 +8014,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;
@@ -7876,25 +8055,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)
@@ -8011,34 +8190,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);
@@ -8204,67 +8383,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);
@@ -8857,7 +9036,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))
@@ -8877,7 +9057,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);
@@ -9411,9 +9591,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
@@ -9619,7 +9800,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))
 	{
@@ -9642,7 +9824,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
@@ -9691,13 +9873,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;
@@ -9991,9 +10174,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;
 
@@ -10081,7 +10266,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);
@@ -10741,7 +10926,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);
@@ -10778,7 +10964,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;
 }
 
@@ -10936,10 +11122,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;
 }
@@ -11076,10 +11263,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;
@@ -11131,7 +11319,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))
     {
@@ -11171,7 +11360,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;
@@ -11234,7 +11423,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))
@@ -11315,7 +11505,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)
@@ -11414,7 +11604,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);
@@ -11422,7 +11613,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)))
 	{
@@ -11459,7 +11650,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);
@@ -11467,7 +11659,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)))
 	{
@@ -11504,7 +11696,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);
@@ -11512,7 +11705,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)))
 	{
@@ -11549,7 +11742,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);
@@ -11557,7 +11751,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)))
 	{
@@ -11595,7 +11789,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);
@@ -11603,7 +11798,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)))
 	{
@@ -11632,7 +11827,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))
     {
@@ -11660,14 +11856,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;
 }
 
@@ -11704,7 +11900,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;
@@ -11713,7 +11910,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;
@@ -12043,9 +12240,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;
@@ -12099,7 +12297,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;
@@ -12197,7 +12396,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;
 }
@@ -12225,7 +12424,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);
@@ -12336,7 +12536,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,
@@ -12449,7 +12649,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);
@@ -12457,7 +12658,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)))
 	{
@@ -12493,7 +12694,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);
@@ -12501,7 +12703,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)))
 	{
@@ -12541,7 +12743,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,
@@ -12568,7 +12771,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;
 }
 
@@ -12587,7 +12790,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
@@ -12610,7 +12814,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))
     {
@@ -12642,7 +12846,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;
 }
 
@@ -12655,7 +12859,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;
@@ -12672,7 +12877,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;
 
@@ -12693,7 +12898,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;
@@ -12710,7 +12916,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;
 
@@ -12822,7 +13028,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))
@@ -12852,7 +13059,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;
     }
 
@@ -12870,13 +13077,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;
 }
 
@@ -12904,7 +13111,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))
@@ -12971,7 +13179,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;
 	}
 
@@ -12991,7 +13199,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;
 	}
     }
@@ -13001,7 +13209,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;
 }
 
@@ -13012,7 +13220,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);
@@ -13020,7 +13229,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)))
 	{
@@ -13049,7 +13258,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))
@@ -13070,7 +13280,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,
@@ -13099,7 +13309,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))
@@ -13118,7 +13329,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;
@@ -13126,7 +13337,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;
 }
 
@@ -13157,11 +13368,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;
 }
@@ -14954,7 +15166,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.  */
@@ -15061,7 +15274,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;
@@ -17568,7 +17781,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;
@@ -17604,7 +17818,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;
 }
@@ -17626,7 +17840,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;
@@ -17688,7 +17903,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;
 }
@@ -18052,7 +18267,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);
@@ -18061,7 +18277,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/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/c-c++-common/missing-close-symbol.c b/gcc/testsuite/c-c++-common/missing-close-symbol.c
new file mode 100644
index 0000000..85b96f28
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/missing-close-symbol.c
@@ -0,0 +1,33 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+/* Verify that the C/C++ frontends show the pertinent opening symbol when
+   a closing symbol is missing.  */
+
+/* Verify that, when they are on the same line, that the opening symbol is
+   shown as a secondary range within the main diagnostic.  */
+
+void test_static_assert_same_line (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char), "msg"; /* { dg-error "expected '\\)' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char), "msg";
+                 ~                                  ^
+     { dg-end-multiline-output "" } */
+}
+
+/* Verify that, when they are on different lines, that the opening symbol is
+   shown via a secondary diagnostic.  */
+
+void test_static_assert_different_line (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char), /* { dg-message "to match this '\\('" } */
+		 "msg"; /* { dg-error "expected '\\)' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+    "msg";
+         ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char),
+                 ^
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/c-c++-common/missing-symbol.c b/gcc/testsuite/c-c++-common/missing-symbol.c
new file mode 100644
index 0000000..33a501b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/missing-symbol.c
@@ -0,0 +1,50 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+extern int foo (void);
+extern int bar (void);
+
+int missing_close_paren_in_switch (int i)
+{
+  switch (i /* { dg-message "10: to match this '\\('" } */
+    { /* { dg-error "5: expected '\\)' before '.' token" } */
+  /* { dg-begin-multiline-output "" }
+     {
+     ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   switch (i
+          ^
+     { dg-end-multiline-output "" } */
+
+    case 0:
+      return 5;
+    default:
+      return i;
+    }
+} /* { dg-error "1: expected" } */
+  /* { dg-begin-multiline-output "" }
+ }
+ ^
+     { dg-end-multiline-output "" } */
+
+void missing_close_paren_in_if (void)
+{
+  if (foo () /* { dg-line start_of_if } */
+      && bar () 
+    { /* { dg-error "5: expected '\\)' before '.' token" } */
+      /* { dg-begin-multiline-output "" }
+     {
+     ^
+         { dg-end-multiline-output "" } */
+      /* { dg-message "6: to match this '\\('" "" { target *-*-* } start_of_if } */
+      /* { dg-begin-multiline-output "" }
+   if (foo ()
+      ^
+      { dg-end-multiline-output "" } */
+    }
+
+} /* { dg-error "1: expected" } */
+  /* { dg-begin-multiline-output "" }
+ }
+ ^
+     { dg-end-multiline-output "" } */
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);
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" } */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2017-08-10 13:35 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-11 14:51 [PATCH 0/3] C/C++: show pertinent open token when missing a close token David Malcolm
2017-07-11 14:51 ` [PATCH 2/3] matching tokens: C parts David Malcolm
2017-07-11 14:51 ` [PATCH 3/3] matching tokens: C++ parts David Malcolm
2017-07-12 13:13   ` Trevor Saunders
2017-07-12 15:12     ` Martin Sebor
2017-07-16 17:55       ` Trevor Saunders
2017-08-01 19:47     ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token David Malcolm
2017-08-01 19:47       ` [PATCH 2/3] Matching tokens: C parts (v2) David Malcolm
2017-08-03 17:34         ` Jeff Law
2017-08-04 14:32           ` David Malcolm
2017-08-04 18:09             ` Jeff Law
2017-08-08 20:37               ` David Malcolm
2017-08-09  6:50                 ` Marek Polacek
2017-08-01 19:47       ` [PATCH 3/3] matching tokens: C++ " David Malcolm
2017-08-07 18:25         ` Jason Merrill
2017-08-08 20:26           ` [PATCH] matching tokens: C++ parts (v3) David Malcolm
2017-08-08 20:49             ` [PATCH] Changes for v3 of the C++ patch David Malcolm
2017-08-09 19:26             ` [PATCH] matching tokens: C++ parts (v3) Jason Merrill
2017-08-01 19:47       ` [PATCH 1/3] matching tokens: c-family parts David Malcolm
2017-08-03 17:22         ` Jeff Law
2017-08-02  3:03       ` [PATCH 0/3 v2] C/C++: show pertinent open token when missing a close token Trevor Saunders
2017-08-10 13:39       ` [committed, v3] " David Malcolm
2017-07-11 14:51 ` [PATCH 1/3] matching tokens: c-family parts David Malcolm
2017-07-18 17:23   ` Marek Polacek
2017-07-11 17:28 ` [PATCH 0/3] C/C++: show pertinent open token when missing a close token Martin Sebor
2017-07-11 18:32   ` David Malcolm
2017-07-11 19:30     ` Martin Sebor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).