C/C++: show pertinent open token when missing a close token
authorDavid Malcolm <dmalcolm@redhat.com>
Thu, 10 Aug 2017 13:22:27 +0000 (13:22 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Thu, 10 Aug 2017 13:22:27 +0000 (13:22 +0000)
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.

From-SVN: r251026

17 files changed:
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c/ChangeLog
gcc/c/c-parser.c
gcc/c/c-parser.h
gcc/cp/ChangeLog
gcc/cp/parser.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/missing-close-symbol.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/missing-symbol.c [new file with mode: 0644]
gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C [new file with mode: 0644]
gcc/testsuite/g++.dg/diagnostic/unclosed-function.C [new file with mode: 0644]
gcc/testsuite/g++.dg/diagnostic/unclosed-namespace.C [new file with mode: 0644]
gcc/testsuite/g++.dg/diagnostic/unclosed-struct.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/pragma2.C
gcc/testsuite/gcc.dg/unclosed-init.c [new file with mode: 0644]

index 5c7454b806909c562feff59547644caf1abe6271..fcb4ea5169b1b19a8ec9c4d44d815ef854962002 100644 (file)
@@ -1,3 +1,9 @@
+2017-08-10  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2017-08-09  Marek Polacek  <polacek@redhat.com>
 
        * c-common.c (pointer_int_sum): Use true/false instead of 1/0.
index a9c0614b4366b5ef241ab44aa4118f749ae67412..4dc3b338e5e99236daace7c7a69ab2dcb2f5bc77 100644 (file)
@@ -5950,12 +5950,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))
 
@@ -5996,7 +5997,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;
     }
@@ -6024,7 +6025,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;
     }
@@ -6037,16 +6038,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
index b44e1bd78d6046eeccd62df98eb76000abb1c9e6..980d39662fc936030fc4753cbbd29cc19fc775c9 100644 (file)
@@ -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 *);
index a7cc74658d56d123febb46a9cc86202b659c7b02..6e293345e9961c7166093b3c53a06bffdf65fafe 100644 (file)
@@ -1,3 +1,87 @@
+2017-08-10  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2017-08-09  Marek Polacek  <polacek@redhat.com>
 
        * c-decl.c (build_enumerator): Use true/false instead of 1/0.
index 6d40a97d5f569fe3f81c4abe43109732376b257f..1402ba67204601576b630ce149ff71be4cd0fddf 100644 (file)
@@ -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;
@@ -6940,9 +7114,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;
@@ -7169,10 +7344,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)
        {
@@ -7251,11 +7427,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;
@@ -7414,7 +7591,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++;
@@ -7556,7 +7734,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;
@@ -7802,7 +7980,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:
@@ -7839,7 +8017,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;
@@ -7879,25 +8058,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)
@@ -8014,34 +8193,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);
@@ -8207,67 +8386,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);
@@ -8860,7 +9039,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))
@@ -8880,7 +9060,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);
@@ -9414,9 +9594,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
@@ -9622,7 +9803,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))
        {
@@ -9645,7 +9827,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
@@ -9694,13 +9876,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;
@@ -9994,9 +10177,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;
 
@@ -10084,7 +10269,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);
@@ -10744,7 +10929,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);
@@ -10781,7 +10967,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;
 }
 
@@ -10939,10 +11125,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;
 }
@@ -11079,10 +11266,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;
@@ -11134,7 +11322,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))
     {
@@ -11174,7 +11363,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;
@@ -11237,7 +11426,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))
@@ -11318,7 +11508,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)
@@ -11417,7 +11607,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);
@@ -11425,7 +11616,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)))
        {
@@ -11462,7 +11653,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);
@@ -11470,7 +11662,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)))
        {
@@ -11507,7 +11699,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);
@@ -11515,7 +11708,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)))
        {
@@ -11552,7 +11745,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);
@@ -11560,7 +11754,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)))
        {
@@ -11598,7 +11792,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);
@@ -11606,7 +11801,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)))
        {
@@ -11635,7 +11830,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))
     {
@@ -11663,14 +11859,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;
 }
 
@@ -11707,7 +11903,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;
@@ -11716,7 +11913,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;
@@ -12046,9 +12243,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;
@@ -12102,7 +12300,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;
@@ -12200,7 +12399,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;
 }
@@ -12228,7 +12427,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);
@@ -12339,7 +12539,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,
@@ -12452,7 +12652,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);
@@ -12460,7 +12661,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)))
        {
@@ -12496,7 +12697,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);
@@ -12504,7 +12706,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)))
        {
@@ -12544,7 +12746,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,
@@ -12571,7 +12774,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;
 }
 
@@ -12590,7 +12793,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
@@ -12613,7 +12817,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))
     {
@@ -12645,7 +12849,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;
 }
 
@@ -12658,7 +12862,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;
@@ -12675,7 +12880,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;
 
@@ -12696,7 +12901,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;
@@ -12713,7 +12919,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;
 
@@ -12825,7 +13031,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))
@@ -12855,7 +13062,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;
     }
 
@@ -12873,13 +13080,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;
 }
 
@@ -12907,7 +13114,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))
@@ -12974,7 +13182,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;
        }
 
@@ -12994,7 +13202,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;
        }
     }
@@ -13004,7 +13212,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;
 }
 
@@ -13015,7 +13223,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);
@@ -13023,7 +13232,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)))
        {
@@ -13052,7 +13261,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))
@@ -13073,7 +13283,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,
@@ -13102,7 +13312,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))
@@ -13121,7 +13332,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;
@@ -13129,7 +13340,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;
 }
 
@@ -13160,11 +13371,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;
 }
@@ -14957,7 +15169,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.  */
@@ -15064,7 +15277,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;
@@ -17571,7 +17784,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;
@@ -17607,7 +17821,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;
 }
@@ -17629,7 +17843,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;
@@ -17691,7 +17906,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;
 }
@@ -18055,7 +18270,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);
@@ -18064,7 +18280,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;
index 1e344c40e27d9d30d2e58f0e50c544b8efa36897..01a7b724081ab7d3f815eb543fe76f078efc51db 100644 (file)
@@ -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);
index d3510cf1965797d9b79d6248edc67987c8a08870..bc0959cfef041f404440f65e54374805e40c3da1 100644 (file)
@@ -1,3 +1,114 @@
+2017-08-10  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2017-08-09  Jason Merrill  <jason@redhat.com>
 
        PR c++/81525 - wrong constant value with generic lambda
index 9d989a7f84fb984d11ea03bf38fb07c1691eed82..b849824fcd0fb441f4fe7a26557bb76756230097 100644 (file)
@@ -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;
     }
 
@@ -29046,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)
     {
@@ -29091,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);
 }
@@ -29112,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)
@@ -29152,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
@@ -29199,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:
@@ -29406,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
@@ -29432,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);
     }
 
@@ -30046,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.  */
@@ -30058,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);
     }
 }
 
@@ -30246,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))
        {
@@ -30274,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
@@ -30328,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.  */
@@ -30571,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)
        {
@@ -30659,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,
@@ -31350,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,
@@ -31403,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
        {
@@ -31477,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;
     }
 
@@ -31623,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);
@@ -31667,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))
     {
@@ -31710,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);
@@ -31734,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);
@@ -31771,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))
@@ -31854,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);
@@ -31949,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);
@@ -31979,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);
@@ -32009,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);
@@ -32039,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);
@@ -32069,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);
@@ -32099,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))
@@ -32131,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",
@@ -32166,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);
@@ -32327,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);
@@ -32421,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))
@@ -32527,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);
@@ -32557,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);
@@ -32588,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,
@@ -32598,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);
@@ -32629,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
@@ -32659,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);
@@ -32697,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);
@@ -32724,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);
@@ -32753,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);
@@ -32875,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))
@@ -32907,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);
@@ -33038,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);
@@ -33069,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);
@@ -33088,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))
@@ -33174,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);
@@ -34228,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);
@@ -34731,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;
@@ -34863,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);
@@ -35222,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 ();
@@ -35259,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);
 
@@ -37121,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 = "";
@@ -37212,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;
 }
@@ -37645,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
@@ -37662,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;
@@ -38094,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
     {
@@ -38687,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);
@@ -38724,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;
 }
index a1e72369cd34ae4509702c9d21f220a7624da9e1..869e7b020984028074857e6d14899e99d0c0be84 100644 (file)
@@ -1,3 +1,15 @@
+2017-08-10  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2017-08-10  Fritz Reese <fritzoreese@gmail.com>
 
        * gfortran.dg/dec_d_lines_1.f: New test.
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 (file)
index 0000000..85b96f2
--- /dev/null
@@ -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 (file)
index 0000000..33a501b
--- /dev/null
@@ -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 (file)
index 0000000..fda3532
--- /dev/null
@@ -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 (file)
index 0000000..e1e1550
--- /dev/null
@@ -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 (file)
index 0000000..ff11322
--- /dev/null
@@ -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 (file)
index 0000000..8c206bb
--- /dev/null
@@ -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 }
index c5616ff74f53e49d2141e8a71a7eda4a32fae2c2..3dc5fc177886327e18ba0b414034f0d5053dbedf 100644 (file)
@@ -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 (file)
index 0000000..c0e4dd8
--- /dev/null
@@ -0,0 +1,3 @@
+int unclosed[] = { /* { dg-message "18: to match this '.'" } */
+  42
+ /* { dg-error "0: expected '.' at end of input" } */