public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] C/C++: add fix-it hints for various missing symbols
@ 2017-07-03 18:04 David Malcolm
  2017-07-03 18:57 ` Richard Sandiford
                   ` (3 more replies)
  0 siblings, 4 replies; 18+ messages in thread
From: David Malcolm @ 2017-07-03 18:04 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch improves our C/C++ frontends' handling of missing
symbols, by making c_parser_require and cp_parser_require use
"better" locations for the diagnostic, and insert fix-it hints,
under certain circumstances (see the comments in the patch for
full details).

For example, for this code with a missing semicolon:

  $ cat test.c
  int missing_semicolon (void)
  {
    return 42
  }

trunk currently emits:

  test.c:4:1: error: expected ‘;’ before ‘}’ token
   }
   ^

This patch adds a fix-it hint for the missing semicolon, and puts
the error at the location of the missing semicolon, printing the
followup token as a secondary location:

  test.c:3:12: error: expected ‘;’ before ‘}’ token
     return 42
              ^
              ;
   }
   ~

More examples can be seen in the test cases.

For reference, clang prints the following:

  test.c:3:12: error: expected ';' after return statement
    return 42
             ^
             ;

i.e. describing what syntactic thing came before, which
I think is likely to be more meaningful to the user.

clang can also print notes about matching opening symbols
e.g. the note here:

  missing-symbol-2.c:25:22: error: expected ']'
    const char test [42;
                       ^
  missing-symbol-2.c:25:19: note: to match this '['
    const char test [42;
                    ^
which, although somewhat redundant for this example, seems much more
useful if there's non-trivial nesting of constructs, or more than a few
lines separating the open/close symbols (e.g. showing a stray "namespace {"
that the user forgot to close).

I'd like to implement both of these ideas as followups, but in
the meantime, is the fix-it hint patch OK for trunk?
(successfully bootstrapped & regrtested on x86_64-pc-linux-gnu)

gcc/c-family/ChangeLog:
	* c-common.c (c_parse_error): Add RICHLOC param, and use it rather
	than implicitly using input_location.
	(enum missing_token_insertion_kind): New enum.
	(get_missing_token_insertion_kind): New function.
	(maybe_suggest_missing_token_insertion): New function.
	* c-common.h (c_parse_error): Add RICHLOC param.
	(maybe_suggest_missing_token_insertion): New decl.

gcc/c/ChangeLog:
	* c-parser.c (struct c_parser): Add "previous_token_loc" field.
	(c_parser_consume_token): Set parser->previous_token_loc.
	(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.
	(c_parser_require): Add "type_is_unique" param.  Use
	c_parser_error_richloc rather than c_parser_error, calling
	maybe_suggest_missing_token_insertion.
	(c_parser_parms_list_declarator): Override default value of new
	"type_is_unique" param to c_parser_require.
	(c_parser_asm_statement): Likewise.
	* c-parser.h (c_parser_require): Add "type_is_unique" param,
	defaulting to true.

gcc/cp/ChangeLog:
	* parser.c (cp_parser_error): Add rich_location to call to
	c_parse_error.
	(get_required_cpp_ttype): New function.
	(cp_parser_required_error): 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 fix-it hint.

gcc/testsuite/ChangeLog:
	* c-c++-common/cilk-plus/AN/parser_errors.c: Update expected
	output to reflect changes to reported locations of missing
	symbols.
	* c-c++-common/cilk-plus/AN/parser_errors2.c: Likewise.
	* c-c++-common/cilk-plus/AN/parser_errors3.c: Likewise.
	* c-c++-common/cilk-plus/AN/pr61191.c: Likewise.
	* c-c++-common/gomp/pr63326.c: Likewise.
	* c-c++-common/missing-symbol.c: New test case.
	* g++.dg/cpp1y/digit-sep-neg.C: Update expected output to reflect
	changes to reported locations of missing symbols.
	* g++.dg/cpp1y/pr65202.C: Likewise.
	* g++.dg/other/do1.C: Likewise.
	* g++.dg/missing-symbol-2.C: New test case.
	* g++.dg/parse/error11.C: Update expected output to reflect
	changes to reported locations of missing symbols.
	* g++.dg/parse/pragma2.C: Likewise.
	* g++.dg/template/error11.C: Likewise.
	* gcc.dg/missing-symbol-2.c: New test case.
	* gcc.dg/missing-symbol-3.c: New test case.
	* gcc.dg/noncompile/940112-1.c: Update expected output to reflect
	changes to reported locations of missing symbols.
	* gcc.dg/noncompile/971104-1.c: Likewise.
	* obj-c++.dg/exceptions-6.mm: Likewise.
	* obj-c++.dg/pr48187.mm: Likewise.
	* objc.dg/exceptions-6.m: Likewise.
---
 gcc/c-family/c-common.c                            | 176 +++++++++++++-
 gcc/c-family/c-common.h                            |   7 +-
 gcc/c/c-parser.c                                   |  55 ++++-
 gcc/c/c-parser.h                                   |   2 +-
 gcc/cp/parser.c                                    | 254 +++++++++++++--------
 .../c-c++-common/cilk-plus/AN/parser_errors.c      |   4 +-
 .../c-c++-common/cilk-plus/AN/parser_errors2.c     |   3 +-
 .../c-c++-common/cilk-plus/AN/parser_errors3.c     |   3 +-
 gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c  |   3 +-
 gcc/testsuite/c-c++-common/gomp/pr63326.c          |  22 +-
 gcc/testsuite/c-c++-common/missing-symbol.c        |  34 +++
 gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C         |   4 +-
 gcc/testsuite/g++.dg/cpp1y/pr65202.C               |   4 +-
 gcc/testsuite/g++.dg/missing-symbol-2.C            |  58 +++++
 gcc/testsuite/g++.dg/other/do1.C                   |   4 +-
 gcc/testsuite/g++.dg/parse/error11.C               |   2 +-
 gcc/testsuite/g++.dg/parse/pragma2.C               |   4 +-
 gcc/testsuite/g++.dg/template/error11.C            |   2 +-
 gcc/testsuite/gcc.dg/missing-symbol-2.c            |  71 ++++++
 gcc/testsuite/gcc.dg/missing-symbol-3.c            |  50 ++++
 gcc/testsuite/gcc.dg/noncompile/940112-1.c         |   4 +-
 gcc/testsuite/gcc.dg/noncompile/971104-1.c         |   4 +-
 gcc/testsuite/obj-c++.dg/exceptions-6.mm           |   6 +-
 gcc/testsuite/obj-c++.dg/pr48187.mm                |   8 +-
 gcc/testsuite/objc.dg/exceptions-6.m               |   4 +-
 25 files changed, 629 insertions(+), 159 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/missing-symbol.c
 create mode 100644 gcc/testsuite/g++.dg/missing-symbol-2.C
 create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-2.c
 create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-3.c

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 4395e51..f75a869 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5945,12 +5945,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))
 
@@ -5991,7 +5992,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;
     }
@@ -6019,7 +6020,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;
     }
@@ -6032,16 +6033,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
@@ -7996,4 +7997,163 @@ c_flt_eval_method (bool maybe_c11_only_p)
     return c_ts18661_flt_eval_method ();
 }
 
+/* An enum for get_missing_token_insertion_kind for describing the best
+   place to insert a missing token, if there is one.  */
+
+enum missing_token_insertion_kind
+{
+  MTIK_IMPOSSIBLE,
+  MTIK_INSERT_BEFORE_NEXT,
+  MTIK_INSERT_AFTER_PREV
+};
+
+/* Given a missing token of TYPE, determine if it is reasonable to
+   emit a fix-it hint suggesting the insertion of the token, and,
+   if so, where the token should be inserted relative to other tokens.
+
+   For example, it only makes sense to do this for values of TYPE that
+   are symbols.
+
+   Some symbols should go before the next token, e.g. in:
+     if flag)
+   we want to insert the missing '(' immediately before "flag",
+   giving:
+     if (flag)
+   rather than:
+     if( flag)
+   These use MTIK_INSERT_BEFORE_NEXT.
+
+   Other symbols should go after the previous token, e.g. in:
+     if (flag
+       do_something ();
+   we want to insert the missing ')' immediately after the "flag",
+   giving:
+     if (flag)
+       do_something ();
+   rather than:
+     if (flag
+       )do_something ();
+   These use MTIK_INSERT_AFTER_PREV.  */
+
+static enum missing_token_insertion_kind
+get_missing_token_insertion_kind (enum cpp_ttype type)
+{
+  switch (type)
+    {
+      /* Insert missing "opening" brackets immediately
+	 before the next token.  */
+    case CPP_OPEN_SQUARE:
+    case CPP_OPEN_PAREN:
+      return MTIK_INSERT_BEFORE_NEXT;
+
+      /* Insert other missing symbols immediately after
+	 the previous token.  */
+    case CPP_CLOSE_PAREN:
+    case CPP_CLOSE_SQUARE:
+    case CPP_SEMICOLON:
+    case CPP_COMMA:
+    case CPP_COLON:
+      return MTIK_INSERT_AFTER_PREV;
+
+      /* Other kinds of token don't get fix-it hints.  */
+    default:
+      return MTIK_IMPOSSIBLE;
+    }
+}
+
+/* Given RICHLOC, a location for a diagnostic describing a missing token
+   of kind TOKEN_TYPE, potentially add a fix-it hint suggesting the
+   insertion of the token.
+
+   The location of the attemped fix-it hint depends on TOKEN_TYPE:
+   it will either be:
+     (a) immediately after PREV_TOKEN_LOC, or
+
+     (b) immediately before the primary location within RICHLOC (taken to
+	 be that of the token following where the token was expected).
+
+   If we manage to add a fix-it hint, then the location of the
+   fix-it hint is likely to be more useful as the primary location
+   of the diagnostic than that of the following token, so we swap
+   these locations.
+
+   For example, given this bogus code:
+       123456789012345678901234567890
+   1 | int missing_semicolon (void)
+   2 | {
+   3 |   return 42
+   4 | }
+
+   we will emit:
+
+     "expected ';' before '}'"
+
+   RICHLOC's primary location is at the closing brace, so before "swapping"
+   we would emit the error at line 4 column 1:
+
+       123456789012345678901234567890
+   3 |   return 42  |< fix-it hint emitted for this line
+     |            ; |
+   4 | }            |< "expected ';' before '}'" emitted at this line
+     | ^            |
+
+   It's more useful for the location of the diagnostic to be at the
+   fix-it hint, so we swap the locations, so the primary location
+   is at the fix-it hint, with the old primary location inserted
+   as a secondary location, giving this, with the error at line 3
+   column 12:
+
+       123456789012345678901234567890
+   3 |   return 42   |< "expected ';' before '}'" emitted at this line,
+     |            ^  |   with fix-it hint
+   4 |            ;  |
+     | }             |< secondary range emitted here
+     | ~             |.  */
+
+void
+maybe_suggest_missing_token_insertion (rich_location *richloc,
+				       enum cpp_ttype token_type,
+				       location_t prev_token_loc)
+{
+  gcc_assert (richloc);
+
+  enum missing_token_insertion_kind mtik
+    = get_missing_token_insertion_kind (token_type);
+
+  switch (mtik)
+    {
+    default:
+      gcc_unreachable ();
+      break;
+
+    case MTIK_IMPOSSIBLE:
+      return;
+
+    case MTIK_INSERT_BEFORE_NEXT:
+      /* Attempt to add the fix-it hint before the primary location
+	 of RICHLOC.  */
+      richloc->add_fixit_insert_before (cpp_type2name (token_type, 0));
+      break;
+
+    case MTIK_INSERT_AFTER_PREV:
+      /* Attempt to add the fix-it hint after PREV_TOKEN_LOC.  */
+      richloc->add_fixit_insert_after (prev_token_loc,
+				       cpp_type2name (token_type, 0));
+      break;
+    }
+
+  /* If we were successful, use the fix-it hint's location as the
+     primary location within RICHLOC, adding the old primary location
+     back as a secondary location.  */
+  if (!richloc->seen_impossible_fixit_p ())
+    {
+      fixit_hint *hint = richloc->get_last_fixit_hint ();
+      location_t hint_loc = hint->get_start_loc ();
+      location_t old_loc = richloc->get_loc ();
+
+      richloc->set_range (line_table, 0, hint_loc, true);
+      richloc->add_range (old_loc, false);
+    }
+}
+
 #include "gt-c-family-c-common.h"
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 1748c19..f5fd6ae 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1124,7 +1124,8 @@ extern void builtin_define_with_int_value (const char *, HOST_WIDE_INT);
 extern void builtin_define_type_sizeof (const char *, tree);
 extern void c_stddef_cpp_builtins (void);
 extern void fe_file_change (const line_map_ordinary *);
-extern void c_parse_error (const char *, enum cpp_ttype, tree, unsigned char);
+extern void c_parse_error (const char *, enum cpp_ttype, tree, unsigned char,
+			   rich_location *richloc);
 
 /* In c-ppoutput.c  */
 extern void init_pp_output (FILE *);
@@ -1554,6 +1555,10 @@ excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
 extern int c_flt_eval_method (bool ts18661_p);
 extern void add_no_sanitize_value (tree node, unsigned int flags);
 
+extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
+						   enum cpp_ttype token_type,
+						   location_t prev_token_loc);
+
 #if CHECKING_P
 namespace selftest {
   extern void c_format_c_tests (void);
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 6f954f2..11f061f 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -206,6 +206,9 @@ struct GTY(()) c_parser {
   /* Buffer to hold all the tokens from parsing the vector attribute for the
      SIMD-enabled functions (formerly known as elemental functions).  */
   vec <c_token, va_gc> *cilk_simd_fn_tokens;
+
+  /* Location of the most-recently consumed token.  */
+  location_t previous_token_loc;
 };
 
 /* Return a pointer to the Nth token in PARSERs tokens_buf.  */
@@ -770,6 +773,7 @@ c_parser_consume_token (c_parser *parser)
   gcc_assert (parser->tokens[0].type != CPP_EOF);
   gcc_assert (!parser->in_pragma || parser->tokens[0].type != CPP_PRAGMA_EOL);
   gcc_assert (parser->error || parser->tokens[0].type != CPP_PRAGMA);
+  parser->previous_token_loc = parser->tokens[0].location;
   if (parser->tokens != &parser->tokens_buf[0])
     parser->tokens++;
   else if (parser->tokens_avail == 2)
@@ -850,14 +854,17 @@ 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.
 
    ??? 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 void
+c_parser_error_richloc (c_parser *parser, const char *gmsgid,
+			rich_location *richloc)
 {
   c_token *token = c_parser_peek_token (parser);
   if (parser->error)
@@ -879,9 +886,6 @@ c_parser_error (c_parser *parser, const char *gmsgid)
 	}
     }
 
-  /* 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 +895,39 @@ 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);
+}
+
+/* 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.  */
+
+void
+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);
+  c_parser_error_richloc (parser, gmsgid, &richloc);
 }
 
 /* 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 TYPE_IS_UNIQUE is true (the default) then msgid describes exactly
+   one type (e.g. "expected %<)%>") and thus it may be reasonable to
+   attempt to generate a fix-it hint for the problem.
+   Otherwise msgid describes multiple token types (e.g.
+   "expected %<;%>, %<,%> or %<)%>"), and thus we shouldn't attempt to
+   generate a fix-it hint.  */
 
 bool
 c_parser_require (c_parser *parser,
 		  enum cpp_ttype type,
-		  const char *msgid)
+		  const char *msgid,
+		  bool type_is_unique)
 {
   if (c_parser_next_token_is (parser, type))
     {
@@ -911,7 +936,14 @@ c_parser_require (c_parser *parser,
     }
   else
     {
-      c_parser_error (parser, msgid);
+      rich_location richloc (line_table,
+			     c_parser_peek_token (parser)->location);
+      /* Potentially supply a fix-it hint, suggesting to add the
+	 missing token immediately after the *previous* token.  */
+      if (!parser->error && type_is_unique)
+	maybe_suggest_missing_token_insertion (&richloc, type,
+					       parser->previous_token_loc);
+      c_parser_error_richloc (parser, msgid, &richloc);
       return false;
     }
 }
@@ -3798,7 +3830,7 @@ c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr)
 	    return get_parm_info (false, expr);
 	}
       if (!c_parser_require (parser, CPP_COMMA,
-			     "expected %<;%>, %<,%> or %<)%>"))
+			     "expected %<;%>, %<,%> or %<)%>", false))
 	{
 	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
 	  return NULL;
@@ -6177,7 +6209,8 @@ c_parser_asm_statement (c_parser *parser)
       if (!c_parser_require (parser, CPP_COLON,
 			     is_goto
 			     ? G_("expected %<:%>")
-			     : G_("expected %<:%> or %<)%>")))
+			     : G_("expected %<:%> or %<)%>"),
+			     is_goto))
 	goto error_close_paren;
 
       /* Once past any colon, we're no longer a simple asm.  */
diff --git a/gcc/c/c-parser.h b/gcc/c/c-parser.h
index 1e344c4..70b318f 100644
--- a/gcc/c/c-parser.h
+++ b/gcc/c/c-parser.h
@@ -136,7 +136,7 @@ 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);
+			      const char *msgid, bool type_is_unique=true);
 extern void 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,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 1ee3ffe..d605aef 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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);
     }
 }
 
@@ -27758,6 +27759,39 @@ cp_parser_friend_p (const cp_decl_specifier_seq *decl_specifiers)
   return decl_spec_seq_has_spec_p (decl_specifiers, ds_friend);
 }
 
+/* Attempt to convert TOKEN_DESC from a required_token to an
+   enum cpp_ttype, returning CPP_EOF if there is no good conversion.  */
+
+static enum cpp_ttype
+get_required_cpp_ttype (required_token token_desc)
+{
+  switch (token_desc)
+    {
+    case RT_SEMICOLON:
+      return CPP_SEMICOLON;
+    case RT_OPEN_PAREN:
+      return CPP_OPEN_PAREN;
+    case RT_CLOSE_BRACE:
+      return CPP_CLOSE_BRACE;
+    case RT_OPEN_BRACE:
+      return CPP_OPEN_BRACE;
+    case RT_CLOSE_SQUARE:
+      return CPP_CLOSE_SQUARE;
+    case RT_OPEN_SQUARE:
+      return CPP_OPEN_SQUARE;
+    case RT_COMMA:
+      return CPP_COMMA;
+    case RT_COLON:
+      return CPP_COLON;
+    case RT_CLOSE_PAREN:
+      return CPP_CLOSE_PAREN;
+
+    default:
+      /* Use CPP_EOF as a "no completions possible" code.  */
+      return CPP_EOF;
+    }
+}
+
 /* 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
@@ -27768,163 +27802,185 @@ cp_parser_required_error (cp_parser *parser,
 			  required_token token_desc,
 			  bool keyword)
 {
+  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);
+
+      rich_location richloc (line_table, input_location);
+
+      /* Potentially supply a fix-it hint, suggesting to add the
+	 missing token immediately after the *previous* token.  */
+      enum cpp_ttype ttype = get_required_cpp_ttype (token_desc);
+      location_t prev_token_loc
+	= cp_lexer_previous_token (parser->lexer)->location;
+      maybe_suggest_missing_token_insertion (&richloc, ttype, prev_token_loc);
+
+      c_parse_error (gmsgid,
+		     (token->type == CPP_KEYWORD ? CPP_NAME : token->type),
+		     token->u.value, token->flags, &richloc);
+    }
 }
 
 
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
index 18816e0..fd4fe54 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
@@ -7,5 +7,5 @@ int main (void)
   
   array2[:] = array2[: ;  /* { dg-error "expected ']'" } */
 
-  return 0;
-} /* { dg-error "expected ';' before" "" { target c } } */
+  return 0; /* { dg-error "expected ';' before" "" { target c } } */
+}
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
index 2bb9134..d003d7c 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
@@ -7,6 +7,7 @@ int main (void)
   
   array2[:] = array2[1:2:] ;  /* { dg-error "expected expression before" "" { target c } } */ 
   /* { dg-error  "expected primary-expression before" "" { target c++ } .-1 } */
+  /* { dg-error "expected ';' before" "" { target c } .-2 } */
 
-  return 0; /* { dg-error "expected ';' before" "" { target c }  } */
+  return 0;
 }
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
index 9270007..14256e9 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
@@ -7,6 +7,7 @@ int main (void)
   
   array2[:] = array2[1: :] ;  /* { dg-error "expected expression before" "" { target c }  } */ 
   /* { dg-error "expected primary-expression before" "" { target c++ }  .-1 } */
+  /* { dg-error "expected ';' before" "" { target c } .-2 } */
 
-  return 0; /* { dg-error "expected ';' before" "" { target c } } */
+  return 0;
 }
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
index a9a9d66..8c32ad9 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
@@ -7,4 +7,5 @@ double f(double * A, double * B)
   return __sec_reduce_add((B[0:500])(; /* { dg-error "called object" "" { target c } } */
 /* { dg-error "expected expression before ';' token" "" { target c } .-1 } */
 /* { dg-error "expected primary-expression before ';' token" "" { target c++ } .-2 } */
-} /* { dg-error "expected" "" { target c } } */
+/* { dg-error "expected" "" { target c } .-3 } */
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/pr63326.c b/gcc/testsuite/c-c++-common/gomp/pr63326.c
index e319f49..3e62723 100644
--- a/gcc/testsuite/c-c++-common/gomp/pr63326.c
+++ b/gcc/testsuite/c-c++-common/gomp/pr63326.c
@@ -156,34 +156,34 @@ f4 (int x)
   {
     do
       #pragma omp barrier			/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp flush				/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp taskwait			/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp taskyield			/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   #pragma omp parallel
   {
     do
       #pragma omp cancel parallel		/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   #pragma omp parallel
   {
     do
       #pragma omp cancellation point parallel	/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   #pragma omp for ordered(1)
   for (i = 0; i < 16; i++)
@@ -191,28 +191,28 @@ f4 (int x)
       {
 	do
 	  #pragma omp ordered depend(source)	/* { dg-error "may only be used in compound statements" } */
-	while (0);
+	while (0); /* { dg-error "before" "" { target c++ } } */
       } /* { dg-error "before" "" { target c++ } } */
       {
 	do
 	  #pragma omp ordered depend(sink: i-1)	/* { dg-error "may only be used in compound statements" } */
-	while (0);
+	while (0); /* { dg-error "before" "" { target c++ } } */
       } /* { dg-error "before" "" { target c++ } } */
     }
   {
     do
       #pragma omp target enter data map(to:i)	/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp target update to(i)		/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp target exit data map(from:i)	/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
 }
 
diff --git a/gcc/testsuite/c-c++-common/missing-symbol.c b/gcc/testsuite/c-c++-common/missing-symbol.c
new file mode 100644
index 0000000..d056516
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/missing-symbol.c
@@ -0,0 +1,34 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+extern int foo (void);
+extern int bar (void);
+
+void missing_close_paren (void)
+{
+  if (foo ()
+      && bar () /* { dg-error "16: expected '\\)' before '.' token" } */
+    {
+      /* { dg-begin-multiline-output "" }
+       && bar ()
+                ^
+                )
+     {
+     ~           
+         { dg-end-multiline-output "" } */
+    }
+} /* { dg-error "1: expected" } */
+  /* { dg-begin-multiline-output "" }
+ }
+ ^
+     { dg-end-multiline-output "" } */
+
+
+int missing_colon_in_ternary (int flag)
+{
+  return flag ? 42 0; /* { dg-error "expected ':' before numeric constant" } */
+  /* { dg-begin-multiline-output "" }
+   return flag ? 42 0;
+                   ^~
+                   :
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C b/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
index 833fab7..727e74e 100644
--- a/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
+++ b/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
@@ -26,5 +26,5 @@ main()
 }
 
 // { dg-error "exponent has no digits" "exponent has no digits" { target *-*-* } 21 }
-// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 14 }
-// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 25 }
+// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 13 }
+// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 24 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr65202.C b/gcc/testsuite/g++.dg/cpp1y/pr65202.C
index 602b264..7ce4895 100644
--- a/gcc/testsuite/g++.dg/cpp1y/pr65202.C
+++ b/gcc/testsuite/g++.dg/cpp1y/pr65202.C
@@ -22,5 +22,5 @@ struct bar;
 int main()
 {
     foo<ns::bar> f;
-    adl::swap(f, f)
-} // { dg-error "" }
+    adl::swap(f, f) // { dg-error "expected ';'" }
+} // { dg-error "expected '.'" "expected end of namespace" }
diff --git a/gcc/testsuite/g++.dg/missing-symbol-2.C b/gcc/testsuite/g++.dg/missing-symbol-2.C
new file mode 100644
index 0000000..4a119f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/missing-symbol-2.C
@@ -0,0 +1,58 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+extern int foo (void);
+
+void missing_open_paren (void)
+{
+  if foo ()) /* { dg-error "expected '\\(' before 'foo'" } */
+    {
+    }
+  /* { dg-begin-multiline-output "" }
+   if foo ())
+      ^~~
+      (
+     { dg-end-multiline-output "" } */
+}
+
+
+void missing_close_square (void)
+{
+  const char test [42;  /* { dg-error "22: expected ']' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   const char test [42;
+                      ^
+                      ]
+     { dg-end-multiline-output "" } */
+}
+
+int missing_semicolon (void)
+{
+  return 42 /* { dg-error "expected ';'" } */
+}
+/* { dg-begin-multiline-output "" }
+   return 42
+            ^
+            ;
+ }
+ ~           
+   { dg-end-multiline-output "" } */
+
+
+int missing_colon_in_switch (int val)
+{
+  switch (val)
+    {
+    case 42 /* { dg-error "expected ':' before 'return'" } */
+      return 42;
+    /* { dg-begin-multiline-output "" }
+     case 42
+            ^
+            :
+       return 42;
+       ~~~~~~
+       { dg-end-multiline-output "" } */
+
+    default:
+      return val;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/other/do1.C b/gcc/testsuite/g++.dg/other/do1.C
index b3a9daf..db65e7d 100644
--- a/gcc/testsuite/g++.dg/other/do1.C
+++ b/gcc/testsuite/g++.dg/other/do1.C
@@ -7,7 +7,7 @@
 
 void init ()
 {
-  do {  } while (0)
-	    obj = 0; // { dg-error "expected|not declared" }
+  do {  } while (0) // { dg-error "expected ';'" }
+	    obj = 0; // { dg-error "not declared" }
      
 }
diff --git a/gcc/testsuite/g++.dg/parse/error11.C b/gcc/testsuite/g++.dg/parse/error11.C
index d118c19..1a49d6e 100644
--- a/gcc/testsuite/g++.dg/parse/error11.C
+++ b/gcc/testsuite/g++.dg/parse/error11.C
@@ -52,7 +52,7 @@ void func(void)
   Foo[:B> k1;       // { dg-bogus "cannot begin|alternate spelling" "smart error should not be triggered here" } 
 // { dg-error "6:missing template arguments before" "template" { target *-*-* } 51 }
 // { dg-error "9:expected primary-expression before ':' token" "primary" { target *-*-* } 51 }
-// { dg-error "9:expected '\]' before ':' token" "backslash" { target *-*-* } 51 }
+// { dg-error "8:expected '\]' before ':' token" "backslash" { target *-*-* } 51 }
 // { dg-error "6:missing template arguments before" "template" { target *-*-* } 52 }
 // { dg-error "7:expected primary-expression before ':' token" "primary" { target *-*-* } 52 }
 // { dg-error "7:expected '\]' before ':' token" "backslash" { target *-*-* } 52 }
diff --git a/gcc/testsuite/g++.dg/parse/pragma2.C b/gcc/testsuite/g++.dg/parse/pragma2.C
index c5616ff..eb0cf95 100644
--- a/gcc/testsuite/g++.dg/parse/pragma2.C
+++ b/gcc/testsuite/g++.dg/parse/pragma2.C
@@ -4,5 +4,5 @@
 // does not.
 int f(int x,
 #pragma interface  // { dg-error "not allowed here" }
-      // The parser gets confused and issues an error on the next line.
-      int y); // { dg-bogus "" "" { xfail *-*-* } } 
+      // { dg-bogus "expected identifier" "" { xfail *-*-* } .-1 }
+      int y); 
diff --git a/gcc/testsuite/g++.dg/template/error11.C b/gcc/testsuite/g++.dg/template/error11.C
index 3a469fd..1640298 100644
--- a/gcc/testsuite/g++.dg/template/error11.C
+++ b/gcc/testsuite/g++.dg/template/error11.C
@@ -1,4 +1,4 @@
 // PR c++/12132
 
 inline template <int> void foo () {} // { dg-error "<" }
-void abort (); // { dg-error ";" }
+void abort (); // { dg-error ";" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/gcc.dg/missing-symbol-2.c b/gcc/testsuite/gcc.dg/missing-symbol-2.c
new file mode 100644
index 0000000..7ee795d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/missing-symbol-2.c
@@ -0,0 +1,71 @@
+/* { dg-options "-fdiagnostics-show-caret -Wno-switch-unreachable" } */
+
+extern int foo (void);
+
+void missing_open_paren (void)
+{
+  if foo ()) /* { dg-line missing_open_paren } */
+    {
+    }
+  /* { dg-error "expected '\\(' before 'foo'" "" { target c } missing_open_paren } */
+  /* { dg-begin-multiline-output "" }
+   if foo ())
+      ^~~
+      (
+     { dg-end-multiline-output "" } */
+  /* { dg-error "expected statement before '\\)' token"  "" { target c } missing_open_paren } */
+  /* { dg-begin-multiline-output "" }
+   if foo ())
+            ^
+     { dg-end-multiline-output "" } */
+}
+
+void missing_close_square (void)
+{
+  const char test [42;  /* { dg-error "22: expected ']' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   const char test [42;
+                      ^
+                      ]
+     { dg-end-multiline-output "" } */
+}
+
+int missing_semicolon (void)
+{
+  return 42 /* { dg-error "expected ';'" } */
+}
+/* { dg-begin-multiline-output "" }
+   return 42
+            ^
+            ;
+ }
+ ~           
+   { dg-end-multiline-output "" } */
+
+
+/* We don't offer a fix-it hint for this case in C, as it could be
+   colon or ellipsis.
+   TODO: we could be smarter about error-recovery here; given the
+   return perhaps we could assume a missing colon.  */
+
+int missing_colon_in_switch (int val)
+{
+  switch (val)
+    {
+    case 42
+      return 42; /* { dg-error "expected ':' or '...' before 'return'" } */
+    /* { dg-begin-multiline-output "" }
+       return 42;
+       ^~~~~~
+       { dg-end-multiline-output "" } */
+
+    default:
+      return val;
+    }
+}
+
+/* { dg-begin-multiline-output "" }
+ int dummy;
+ ^~~
+   { dg-end-multiline-output "" } */
+int dummy;/* { dg-error "expected declaration or statement at end of input" "" { target c } } */
diff --git a/gcc/testsuite/gcc.dg/missing-symbol-3.c b/gcc/testsuite/gcc.dg/missing-symbol-3.c
new file mode 100644
index 0000000..5449665
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/missing-symbol-3.c
@@ -0,0 +1,50 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+/* A sequence of bogus _Static_assert.
+   We can offer fix-it hints for some of these, but not all.  */
+
+void test_static_assert_1 (void)
+{
+  _Static_assert sizeof(int) >= sizeof(char); /* { dg-error "expected '\\(' before 'sizeof'" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert sizeof(int) >= sizeof(char);
+                  ^~~~~~
+                  (
+     { dg-end-multiline-output "" } */
+}
+
+void test_static_assert_2 (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char); /* { dg-error "expected ',' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char);
+                                             ^
+                                             ,
+     { dg-end-multiline-output "" } */
+}
+
+void test_static_assert_3 (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char),; /* { dg-error "expected string literal before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char),;
+                                              ^
+     { dg-end-multiline-output "" } */
+}
+
+void test_static_assert_4 (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 "" } */
+}
+
+/* The final one is correct.  */
+
+void test_static_assert_5 (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char), "msg");
+}
diff --git a/gcc/testsuite/gcc.dg/noncompile/940112-1.c b/gcc/testsuite/gcc.dg/noncompile/940112-1.c
index bb5e0f6..0a9e07d 100644
--- a/gcc/testsuite/gcc.dg/noncompile/940112-1.c
+++ b/gcc/testsuite/gcc.dg/noncompile/940112-1.c
@@ -3,5 +3,5 @@ f (int x)
 {
   double e = 1;
   e = 1;
-  return (e)
-}	/* { dg-error "parse error|syntax error|expected" } */
+  return (e) /* { dg-error "parse error|syntax error|expected" } */
+}	
diff --git a/gcc/testsuite/gcc.dg/noncompile/971104-1.c b/gcc/testsuite/gcc.dg/noncompile/971104-1.c
index 39e00c6..4a04dad 100644
--- a/gcc/testsuite/gcc.dg/noncompile/971104-1.c
+++ b/gcc/testsuite/gcc.dg/noncompile/971104-1.c
@@ -27,6 +27,6 @@ static void up(int sem){
     printf("%s had processes sleeping on it!\n",
     ({ "MUTEX     ", "BARB_SEM 1", "BARB_SEM 2", "CUST_SEM 1",
        "CUST_SEM 2", "WAIT_SEM 1", "WAIT_SEM 2", "WAIT_SEM 3",
-       "WAIT_SEM 4"}	 /* { dg-error "parse error|syntax error|expected" } */
-	[( sb.sem_num )]) ); /* { dg-error "expected" } */
+       "WAIT_SEM 4"}	 /* { dg-error "expected" } */
+	[( sb.sem_num )]) );
 }
diff --git a/gcc/testsuite/obj-c++.dg/exceptions-6.mm b/gcc/testsuite/obj-c++.dg/exceptions-6.mm
index 58882fe..6f6ba78 100644
--- a/gcc/testsuite/obj-c++.dg/exceptions-6.mm
+++ b/gcc/testsuite/obj-c++.dg/exceptions-6.mm
@@ -11,15 +11,15 @@ void test (id object)
   @throw object;   /* Ok */
   @throw;          /* { dg-error ".@throw. .rethrow. used outside of a @catch block" } */
   @throw (object); /* Ok.  */
-  @throw (id)0
-}                  /* { dg-error "expected" } */
+  @throw (id)0     /* { dg-error "expected" } */
+}
 
 void test2 (id object)
 {
   @throw object);  /* { dg-error "expected" } */
   @throw (...);    /* { dg-error "expected" } */
   @throw ();       /* { dg-error "expected" } */
-  @throw           
+  @throw           /* { dg-error "expected" } */
 }                  /* { dg-error "expected" } */
 
 void test3 (id object1, id object2)
diff --git a/gcc/testsuite/obj-c++.dg/pr48187.mm b/gcc/testsuite/obj-c++.dg/pr48187.mm
index 750710b..99677a5 100644
--- a/gcc/testsuite/obj-c++.dg/pr48187.mm
+++ b/gcc/testsuite/obj-c++.dg/pr48187.mm
@@ -1,19 +1,19 @@
 /* { dg-do compile } */
 
 @interface A
-{
+{    /* { dg-error "xpected" } */
   ]  /* { dg-error "xpected" } */
 }
 @end
 
 @interface B
-{
+{     /* { dg-error "xpected" } */
   ];  /* { dg-error "xpected" } */
 }
 @end
 
 @interface C
-{
+{     /* { dg-error "xpected" } */
   ];  /* { dg-error "xpected" } */
   int x;
 }
@@ -21,7 +21,7 @@
 
 @interface D
 {
-  (
+  (  /* { dg-error "xpected" } */
 }  /* { dg-error "xpected" } */
 @end
 
diff --git a/gcc/testsuite/objc.dg/exceptions-6.m b/gcc/testsuite/objc.dg/exceptions-6.m
index 58882fe..74be98d 100644
--- a/gcc/testsuite/objc.dg/exceptions-6.m
+++ b/gcc/testsuite/objc.dg/exceptions-6.m
@@ -11,8 +11,8 @@ void test (id object)
   @throw object;   /* Ok */
   @throw;          /* { dg-error ".@throw. .rethrow. used outside of a @catch block" } */
   @throw (object); /* Ok.  */
-  @throw (id)0
-}                  /* { dg-error "expected" } */
+  @throw (id)0     /* { dg-error "expected" } */
+}
 
 void test2 (id object)
 {
-- 
1.8.5.3

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

* Re: [PATCH] C/C++: add fix-it hints for various missing symbols
  2017-07-03 18:04 [PATCH] C/C++: add fix-it hints for various missing symbols David Malcolm
@ 2017-07-03 18:57 ` Richard Sandiford
  2017-07-03 19:34   ` David Malcolm
  2017-07-11 14:09   ` [committed] diagnostics: support compact printing of secondary locations David Malcolm
  2017-07-03 23:01 ` [PATCH] C/C++: add fix-it hints for various missing symbols Joseph Myers
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 18+ messages in thread
From: Richard Sandiford @ 2017-07-03 18:57 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

[Thanks for all your diagnostic work btw.]

David Malcolm <dmalcolm@redhat.com> writes:
> clang can also print notes about matching opening symbols
> e.g. the note here:
>
>   missing-symbol-2.c:25:22: error: expected ']'
>     const char test [42;
>                        ^
>   missing-symbol-2.c:25:19: note: to match this '['
>     const char test [42;
>                     ^
> which, although somewhat redundant for this example, seems much more
> useful if there's non-trivial nesting of constructs, or more than a few
> lines separating the open/close symbols (e.g. showing a stray "namespace {"
> that the user forgot to close).
>
> I'd like to implement both of these ideas as followups, but in
> the meantime, is the fix-it hint patch OK for trunk?
> (successfully bootstrapped & regrtested on x86_64-pc-linux-gnu)

Just wondering: how easy would it be to restrict the note to the kinds
of cases you mention?  TBH I think clang goes in for extra notes too
much, and it's not always that case that an "expected 'foo'" message
really is caused by a missing 'foo'.  It'd be great if there was some
way of making the notes a bit more discerning. :-)

Or maybe do something like restrict the extra note to cases in which the
opening character is on a different line and use an underlined range
when the opening character is on the same line?

Thanks,
Richard

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

* Re: [PATCH] C/C++: add fix-it hints for various missing symbols
  2017-07-03 18:57 ` Richard Sandiford
@ 2017-07-03 19:34   ` David Malcolm
  2017-07-11 14:09   ` [committed] diagnostics: support compact printing of secondary locations David Malcolm
  1 sibling, 0 replies; 18+ messages in thread
From: David Malcolm @ 2017-07-03 19:34 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: gcc-patches

On Mon, 2017-07-03 at 19:57 +0100, Richard Sandiford wrote:
> [Thanks for all your diagnostic work btw.]
> 
> David Malcolm <dmalcolm@redhat.com> writes:
> > clang can also print notes about matching opening symbols
> > e.g. the note here:
> > 
> >   missing-symbol-2.c:25:22: error: expected ']'
> >     const char test [42;
> >                        ^
> >   missing-symbol-2.c:25:19: note: to match this '['
> >     const char test [42;
> >                     ^
> > which, although somewhat redundant for this example, seems much
> > more
> > useful if there's non-trivial nesting of constructs, or more than a
> > few
> > lines separating the open/close symbols (e.g. showing a stray
> > "namespace {"
> > that the user forgot to close).
> > 
> > I'd like to implement both of these ideas as followups, but in
> > the meantime, is the fix-it hint patch OK for trunk?
> > (successfully bootstrapped & regrtested on x86_64-pc-linux-gnu)
> 
> Just wondering: how easy would it be to restrict the note to the
> kinds
> of cases you mention?  TBH I think clang goes in for extra notes too
> much, and it's not always that case that an "expected 'foo'" message
> really is caused by a missing 'foo'.  It'd be great if there was some
> way of making the notes a bit more discerning. :-)

My plan was to only do it for open/close punctuation, i.e.:
  * '(' and ')'
  * '{' and '}'
  * '[' and ']'
  * maybe '<' and '>' in C++

> Or maybe do something like restrict the extra note to cases in which
> the
> opening character is on a different line and use an underlined range
> when the opening character is on the same line?

Good idea: if it's on the same line, use a secondary range; if it's on
a different line, use a note.

The above example would look something like this (with the '[' as a
secondary range):

  missing-symbol-2.c:25:22: error: expected ']'
  const char test [42;
                  ~  ^
                     ]

which is more compact than the "separate note" approach, whilst (IMHO)
being just as readable.

FWIW diagnostic-show-locus.c can handle widely-separated secondary
ranges within one rich_location, provided they're in the same source
file (see calculate_line_spans, and the start_span callback within
diagnostic_context).

Consider the unclosed namespace here:

$ cat -n test.cc
     1	namespace ns {
     2	
     3	void test ()
     4	{
     5	}

for which we currently emit the rather unhelpful:

$ gcc test.cc
test.cc:5:1: error: expected ‘}’ at end of input
 }
 ^

Printing it via a secondary range using a single rich_location with
just an "error_at_rich_loc" call would print something like:

test.cc:5:1: error: expected ‘}’ at end of input
test.cc:1:14:
 namespace ns {
              ^
test.cc:5:1:
 }
  ^
  }

which works, but I'm not a fan of.

In constrast, with the "if it's on a different line, use a note" approach, we would print:

test.cc:5:1: error: expected ‘}’ at end of input
 }
  ^
  }
test.cc:1:14: note: to match this '{'
 namespace ns {
              ^

which I think is better (and supports the cases where they're in different files (e.g. you have a stray unclosed namespace in a header file, somewhere...), or macros are involved, etc)

So I'll have a go at implementing the "is it on a different line" logic you suggest.

For reference, clang prints the following for the above case:

test.cc:5:2: error: expected '}'
}
 ^
test.cc:1:14: note: to match this '{'
namespace ns {
             ^

Thinking aloud, maybe it would be better for the fix-it hint to suggest putting the '}' on a whole new line.  Might even be good to suggest adding

} // namespace ns

or similar (for this specific case), giving this output:

test.cc:5:1: error: expected ‘}’ at end of input
 }
+} // namespace ns
test.cc:1:14: note: to match this '{'
 namespace ns {
              ^

(only works if the proposed insertion point is on the end of a line, given the current restrictions on what our fix-it machinery is capable of - we don't currently support splitting a pre-existing line via a fix-it hint)

Thanks.
Dave


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

* Re: [PATCH] C/C++: add fix-it hints for various missing symbols
  2017-07-03 18:04 [PATCH] C/C++: add fix-it hints for various missing symbols David Malcolm
  2017-07-03 18:57 ` Richard Sandiford
@ 2017-07-03 23:01 ` Joseph Myers
  2017-07-05 15:32   ` David Malcolm
  2017-08-28 16:11 ` Jeff Law
  2017-09-26 13:56 ` [PATCH 0/2] " David Malcolm
  3 siblings, 1 reply; 18+ messages in thread
From: Joseph Myers @ 2017-07-03 23:01 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

Does the changed location fix bug 7356?

-- 
Joseph S. Myers
joseph@codesourcery.com

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

* Re: [PATCH] C/C++: add fix-it hints for various missing symbols
  2017-07-03 23:01 ` [PATCH] C/C++: add fix-it hints for various missing symbols Joseph Myers
@ 2017-07-05 15:32   ` David Malcolm
  2017-07-05 16:17     ` Joseph Myers
  0 siblings, 1 reply; 18+ messages in thread
From: David Malcolm @ 2017-07-05 15:32 UTC (permalink / raw)
  To: Joseph Myers; +Cc: gcc-patches

On Mon, 2017-07-03 at 23:01 +0000, Joseph Myers wrote:
> Does the changed location fix bug 7356?

The patch as-written doesn't affect that bug, since the patch only
affects sites that use c_parser_require and cp_parser_require with
certain token types, and the diagnostic in PR 7356 is emitted by the C
FE here:

2174		  /* This can appear in many cases looking nothing like a
2175		     function definition, so we don't give a more specific
2176		     error suggesting there was one.  */
2177		  c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, %<asm%> "
2178				  "or %<__attribute__%>");

(the C++ FE handles it, emitting:

pr7356.c:1:1: error: ‘a’ does not name a type
 a//sample
 ^
)

c_parser_error currently uses the location of the next token, and
concats as description of the next token.

I tried hacking up c_parser_error to unconditionally attempt to use the
location immediately after the previous token.  This "fixes" PR 7356,
giving:

pr7356.c:1:2: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘typedef’
 a//sample
  ^

This error message might be better to be worded in terms of the
syntactic thing that came before, which would yield:

pr7356.c:1:2: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’
after declaration
 a//sample
  ^

or somesuch.  Doing so would presumably require adding an extra param to 
c_parser_error, e.g. an enum describing the syntactic elements that go before.

Does this sound worth pursuing as a followup?


Thanks
Dave

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

* Re: [PATCH] C/C++: add fix-it hints for various missing symbols
  2017-07-05 15:32   ` David Malcolm
@ 2017-07-05 16:17     ` Joseph Myers
  0 siblings, 0 replies; 18+ messages in thread
From: Joseph Myers @ 2017-07-05 16:17 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 875 bytes --]

On Wed, 5 Jul 2017, David Malcolm wrote:

> This error message might be better to be worded in terms of the
> syntactic thing that came before, which would yield:
> 
> pr7356.c:1:2: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’
> after declaration
>  a//sample
>   ^
> 
> or somesuch.  Doing so would presumably require adding an extra param to 
> c_parser_error, e.g. an enum describing the syntactic elements that go before.
> 
> Does this sound worth pursuing as a followup?

Yes.  When you're wording things in terms of what the syntax error comes 
after rather than saying it comes before some automatically-generated 
description of a token, it would be best if the caller passes the complete 
message in an i18n-friendly way, rather than using concat (bug 18248).

-- 
Joseph S. Myers
joseph@codesourcery.com

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

* [committed] diagnostics: support compact printing of secondary locations
  2017-07-03 18:57 ` Richard Sandiford
  2017-07-03 19:34   ` David Malcolm
@ 2017-07-11 14:09   ` David Malcolm
  2017-07-11 17:34     ` Richard Sandiford
  1 sibling, 1 reply; 18+ messages in thread
From: David Malcolm @ 2017-07-11 14:09 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: gcc-patches, David Malcolm

On Mon, 2017-07-03 at 19:57 +0100, Richard Sandiford wrote:
> [Thanks for all your diagnostic work btw.]
> 
> David Malcolm <dmalcolm@redhat.com> writes:
> > clang can also print notes about matching opening symbols
> > e.g. the note here:
> > 
> >   missing-symbol-2.c:25:22: error: expected ']'
> >     const char test [42;
> >                        ^
> >   missing-symbol-2.c:25:19: note: to match this '['
> >     const char test [42;
> >                     ^
> > which, although somewhat redundant for this example, seems much
> > more
> > useful if there's non-trivial nesting of constructs, or more than a
> > few
> > lines separating the open/close symbols (e.g. showing a stray
> > "namespace {"
> > that the user forgot to close).
> > 
> > I'd like to implement both of these ideas as followups, but in
> > the meantime, is the fix-it hint patch OK for trunk?
> > (successfully bootstrapped & regrtested on x86_64-pc-linux-gnu)
> 
> Just wondering: how easy would it be to restrict the note to the
> kinds
> of cases you mention?  TBH I think clang goes in for extra notes too
> much, and it's not always that case that an "expected 'foo'" message
> really is caused by a missing 'foo'.  It'd be great if there was some
> way of making the notes a bit more discerning. :-)
> 
> Or maybe do something like restrict the extra note to cases in which
> the
> opening character is on a different line and use an underlined range
> when the opening character is on the same line?
> 
> Thanks,
> Richard

Thanks.

This patch implements a new method:

   bool gcc_rich_location::add_location_if_nearby (location_t);

to make it easy for a diagnostic to compactly print secondary locations
for these kinds of cases, falling back to printing them via a note
otherwise.

Usage example (adapted from the one in the header):

  gcc_rich_location richloc (primary_loc);
  bool added secondary = richloc.add_location_if_nearby (secondary_loc);
  error_at_rich_loc (&richloc, "missing %qs", "}");
  if (!added secondary)
    inform (secondary_loc, "here's the associated %qs", "{");

When primary_loc and secondary_loc are on the same line this will print:

  test.c:1:39: error: missing '}'
   struct same_line { double x; double y; ;
                    ~                    ^

When they are on different lines, this will print:

  test.c:6:1: error: missing '}'
   ;
   ^
  test.c:3:1: note: here's the associated '{'
   {
   ^

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu;
takes -fself-test from 39233 passes to 39328 passes.

Committed to trunk as r250133 (and r250134 to fix a ChangeLog snafu).

gcc/ChangeLog:
	* diagnostic-show-locus.c: Include "gcc-rich-location.h".
	(layout::m_primary_loc): New field.
	(layout::layout): Initialize new field.  Move location filtering
	logic from here to...
	(layout::maybe_add_location_range): ...this new method.  Add
	support for filtering to just the lines already specified by other
	locations.
	(layout::will_show_line_p): New method.
	(gcc_rich_location::add_location_if_nearby): New method.
	(selftest::test_add_location_if_nearby): New test function.
	(selftest::diagnostic_show_locus_c_tests): Call it.
	* gcc-rich-location.h (gcc_rich_location::add_location_if_nearby):
	New method.
---
 gcc/diagnostic-show-locus.c | 273 +++++++++++++++++++++++++++++++++-----------
 gcc/gcc-rich-location.h     |  21 ++++
 2 files changed, 228 insertions(+), 66 deletions(-)

diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 8a4fd5f..5227400 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "backtrace.h"
 #include "diagnostic.h"
 #include "diagnostic-color.h"
+#include "gcc-rich-location.h"
 #include "selftest.h"
 
 #ifdef HAVE_TERMIOS_H
@@ -196,6 +197,9 @@ class layout
 	  rich_location *richloc,
 	  diagnostic_t diagnostic_kind);
 
+  bool maybe_add_location_range (const location_range *loc_range,
+				 bool restrict_to_current_line_spans);
+
   int get_num_line_spans () const { return m_line_spans.length (); }
   const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
 
@@ -206,6 +210,7 @@ class layout
   void print_line (int row);
 
  private:
+  bool will_show_line_p (int row) const;
   void print_leading_fixits (int row);
   void print_source_line (int row, const char *line, int line_width,
 			  line_bounds *lbounds_out);
@@ -241,6 +246,7 @@ class layout
   diagnostic_context *m_context;
   pretty_printer *m_pp;
   diagnostic_t m_diagnostic_kind;
+  location_t m_primary_loc;
   expanded_location m_exploc;
   colorizer m_colorizer;
   bool m_colorize_source_p;
@@ -767,6 +773,7 @@ layout::layout (diagnostic_context * context,
 : m_context (context),
   m_pp (context->printer),
   m_diagnostic_kind (diagnostic_kind),
+  m_primary_loc (richloc->get_range (0)->m_loc),
   m_exploc (richloc->get_expanded_location (0)),
   m_colorizer (context, diagnostic_kind),
   m_colorize_source_p (context->colorize_source_p),
@@ -775,77 +782,12 @@ layout::layout (diagnostic_context * context,
   m_line_spans (1 + richloc->get_num_locations ()),
   m_x_offset (0)
 {
-  source_location primary_loc = richloc->get_range (0)->m_loc;
-
   for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
     {
       /* This diagnostic printer can only cope with "sufficiently sane" ranges.
 	 Ignore any ranges that are awkward to handle.  */
       const location_range *loc_range = richloc->get_range (idx);
-
-      /* Split the "range" into caret and range information.  */
-      source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
-
-      /* Expand the various locations.  */
-      expanded_location start
-	= linemap_client_expand_location_to_spelling_point
-	    (src_range.m_start, LOCATION_ASPECT_START);
-      expanded_location finish
-	= linemap_client_expand_location_to_spelling_point
-	    (src_range.m_finish, LOCATION_ASPECT_FINISH);
-      expanded_location caret
-	= linemap_client_expand_location_to_spelling_point
-	    (loc_range->m_loc, LOCATION_ASPECT_CARET);
-
-      /* If any part of the range isn't in the same file as the primary
-	 location of this diagnostic, ignore the range.  */
-      if (start.file != m_exploc.file)
-	continue;
-      if (finish.file != m_exploc.file)
-	continue;
-      if (loc_range->m_show_caret_p)
-	if (caret.file != m_exploc.file)
-	  continue;
-
-      /* Sanitize the caret location for non-primary ranges.  */
-      if (m_layout_ranges.length () > 0)
-	if (loc_range->m_show_caret_p)
-	  if (!compatible_locations_p (loc_range->m_loc, primary_loc))
-	    /* Discard any non-primary ranges that can't be printed
-	       sanely relative to the primary location.  */
-	    continue;
-
-      /* Everything is now known to be in the correct source file,
-	 but it may require further sanitization.  */
-      layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
-
-      /* If we have a range that finishes before it starts (perhaps
-	 from something built via macro expansion), printing the
-	 range is likely to be nonsensical.  Also, attempting to do so
-	 breaks assumptions within the printing code  (PR c/68473).
-	 Similarly, don't attempt to print ranges if one or both ends
-	 of the range aren't sane to print relative to the
-	 primary location (PR c++/70105).  */
-      if (start.line > finish.line
-	  || !compatible_locations_p (src_range.m_start, primary_loc)
-	  || !compatible_locations_p (src_range.m_finish, primary_loc))
-	{
-	  /* Is this the primary location?  */
-	  if (m_layout_ranges.length () == 0)
-	    {
-	      /* We want to print the caret for the primary location, but
-		 we must sanitize away m_start and m_finish.  */
-	      ri.m_start = ri.m_caret;
-	      ri.m_finish = ri.m_caret;
-	    }
-	  else
-	    /* This is a non-primary range; ignore it.  */
-	    continue;
-	}
-
-      /* Passed all the tests; add the range to m_layout_ranges so that
-	 it will be printed.  */
-      m_layout_ranges.safe_push (ri);
+      maybe_add_location_range (loc_range, false);
     }
 
   /* Populate m_fixit_hints, filtering to only those that are in the
@@ -882,6 +824,118 @@ layout::layout (diagnostic_context * context,
     show_ruler (m_x_offset + max_width);
 }
 
+/* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
+   those that we can sanely print.
+
+   If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
+   filtered against this layout instance's current line spans: it
+   will only be added if the location is fully within the lines
+   already specified by other locations.
+
+   Return true iff LOC_RANGE was added.  */
+
+bool
+layout::maybe_add_location_range (const location_range *loc_range,
+				  bool restrict_to_current_line_spans)
+{
+  gcc_assert (loc_range);
+
+  /* Split the "range" into caret and range information.  */
+  source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
+
+  /* Expand the various locations.  */
+  expanded_location start
+    = linemap_client_expand_location_to_spelling_point
+    (src_range.m_start, LOCATION_ASPECT_START);
+  expanded_location finish
+    = linemap_client_expand_location_to_spelling_point
+    (src_range.m_finish, LOCATION_ASPECT_FINISH);
+  expanded_location caret
+    = linemap_client_expand_location_to_spelling_point
+    (loc_range->m_loc, LOCATION_ASPECT_CARET);
+
+  /* If any part of the range isn't in the same file as the primary
+     location of this diagnostic, ignore the range.  */
+  if (start.file != m_exploc.file)
+    return false;
+  if (finish.file != m_exploc.file)
+    return false;
+  if (loc_range->m_show_caret_p)
+    if (caret.file != m_exploc.file)
+      return false;
+
+  /* Sanitize the caret location for non-primary ranges.  */
+  if (m_layout_ranges.length () > 0)
+    if (loc_range->m_show_caret_p)
+      if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
+	/* Discard any non-primary ranges that can't be printed
+	   sanely relative to the primary location.  */
+	return false;
+
+  /* Everything is now known to be in the correct source file,
+     but it may require further sanitization.  */
+  layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
+
+  /* If we have a range that finishes before it starts (perhaps
+     from something built via macro expansion), printing the
+     range is likely to be nonsensical.  Also, attempting to do so
+     breaks assumptions within the printing code  (PR c/68473).
+     Similarly, don't attempt to print ranges if one or both ends
+     of the range aren't sane to print relative to the
+     primary location (PR c++/70105).  */
+  if (start.line > finish.line
+      || !compatible_locations_p (src_range.m_start, m_primary_loc)
+      || !compatible_locations_p (src_range.m_finish, m_primary_loc))
+    {
+      /* Is this the primary location?  */
+      if (m_layout_ranges.length () == 0)
+	{
+	  /* We want to print the caret for the primary location, but
+	     we must sanitize away m_start and m_finish.  */
+	  ri.m_start = ri.m_caret;
+	  ri.m_finish = ri.m_caret;
+	}
+      else
+	/* This is a non-primary range; ignore it.  */
+	return false;
+    }
+
+  /* Potentially filter to just the lines already specified by other
+     locations.  This is for use by gcc_rich_location::add_location_if_nearby.
+     The layout ctor doesn't use it, and can't because m_line_spans
+     hasn't been set up at that point.  */
+  if (restrict_to_current_line_spans)
+    {
+      if (!will_show_line_p (start.line))
+	return false;
+      if (!will_show_line_p (finish.line))
+	return false;
+      if (loc_range->m_show_caret_p)
+	if (!will_show_line_p (caret.line))
+	  return false;
+    }
+
+  /* Passed all the tests; add the range to m_layout_ranges so that
+     it will be printed.  */
+  m_layout_ranges.safe_push (ri);
+  return true;
+}
+
+/* Return true iff ROW is within one of the line spans for this layout.  */
+
+bool
+layout::will_show_line_p (int row) const
+{
+  for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
+       line_span_idx++)
+    {
+      const line_span *line_span = get_line_span (line_span_idx);
+      if (line_span->contains_line_p (row))
+	return true;
+    }
+  return false;
+}
+
 /* Return true iff we should print a heading when starting the
    line span with the given index.  */
 
@@ -1782,6 +1836,28 @@ layout::print_line (int row)
 
 } /* End of anonymous namespace.  */
 
+/* If LOC is within the spans of lines that will already be printed for
+   this gcc_rich_location, then add it as a secondary location and return true.
+
+   Otherwise return false.  */
+
+bool
+gcc_rich_location::add_location_if_nearby (location_t loc)
+{
+  /* Use the layout location-handling logic to sanitize LOC,
+     filtering it to the current line spans within a temporary
+     layout instance.  */
+  layout layout (global_dc, this, DK_ERROR);
+  location_range loc_range;
+  loc_range.m_loc = loc;
+  loc_range.m_show_caret_p = false;
+  if (!layout.maybe_add_location_range (&loc_range, true))
+    return false;
+
+  add_range (loc, false);
+  return true;
+}
+
 /* Print the physical source code corresponding to the location of
    this diagnostic, with additional annotations.  */
 
@@ -2226,6 +2302,70 @@ test_diagnostic_show_locus_one_liner (const line_table_case &case_)
   test_one_liner_many_fixits_2 ();
 }
 
+/* Verify that gcc_rich_location::add_location_if_nearby works.  */
+
+static void
+test_add_location_if_nearby (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+     ...000000000111111111122222222223333333333.
+     ...123456789012345678901234567890123456789.  */
+  const char *content
+    = ("struct same_line { double x; double y; ;\n" /* line 1.  */
+       "struct different_line\n"                    /* line 2.  */
+       "{\n"                                        /* line 3.  */
+       "  double x;\n"                              /* line 4.  */
+       "  double y;\n"                              /* line 5.  */
+       ";\n");                                      /* line 6.  */
+  temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
+  line_table_test ltt (case_);
+
+  const line_map_ordinary *ord_map
+    = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
+					   tmp.get_filename (), 0));
+
+  linemap_line_start (line_table, 1, 100);
+
+  const location_t final_line_end
+    = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
+
+  /* Don't attempt to run the tests if column data might be unavailable.  */
+  if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  /* Test of add_location_if_nearby on the same line as the
+     primary location.  */
+  {
+    const location_t missing_close_brace_1_39
+      = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
+    const location_t matching_open_brace_1_18
+      = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
+    gcc_rich_location richloc (missing_close_brace_1_39);
+    bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
+    ASSERT_TRUE (added);
+    ASSERT_EQ (2, richloc.get_num_locations ());
+    test_diagnostic_context dc;
+    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+    ASSERT_STREQ ("\n"
+		  " struct same_line { double x; double y; ;\n"
+		  "                  ~                    ^\n",
+		  pp_formatted_text (dc.printer));
+  }
+
+  /* Test of add_location_if_nearby on a different line to the
+     primary location.  */
+  {
+    const location_t missing_close_brace_6_1
+      = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
+    const location_t matching_open_brace_3_1
+      = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
+    gcc_rich_location richloc (missing_close_brace_6_1);
+    bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
+    ASSERT_FALSE (added);
+    ASSERT_EQ (1, richloc.get_num_locations ());
+  }
+}
+
 /* Verify that we print fixits even if they only affect lines
    outside those covered by the ranges in the rich_location.  */
 
@@ -2857,6 +2997,7 @@ diagnostic_show_locus_c_tests ()
   test_diagnostic_show_locus_unknown_location ();
 
   for_each_line_table_case (test_diagnostic_show_locus_one_liner);
+  for_each_line_table_case (test_add_location_if_nearby);
   for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
   for_each_line_table_case (test_fixit_consolidation);
   for_each_line_table_case (test_overlapped_fixit_printing);
diff --git a/gcc/gcc-rich-location.h b/gcc/gcc-rich-location.h
index 49708ca..2720f38 100644
--- a/gcc/gcc-rich-location.h
+++ b/gcc/gcc-rich-location.h
@@ -40,6 +40,27 @@ class gcc_rich_location : public rich_location
 
   void add_fixit_misspelled_id (location_t misspelled_token_loc,
 				tree hint_id);
+
+  /* If LOC is within the spans of lines that will already be printed for
+     this gcc_rich_location, then add it as a secondary location
+     and return true.
+
+     Otherwise return false.
+
+     This allows for a diagnostic to compactly print secondary locations
+     in one diagnostic when these are near enough the primary locations for
+     diagnostics-show-locus.c to cope with them, and to fall back to
+     printing them via a note otherwise e.g.:
+
+	gcc_rich_location richloc (primary_loc);
+	bool added secondary = richloc.add_location_if_nearby (secondary_loc);
+	error_at_rich_loc (&richloc, "main message");
+	if (!added secondary)
+	  inform (secondary_loc, "message for secondary");
+
+     Implemented in diagnostic-show-locus.c.  */
+
+  bool add_location_if_nearby (location_t loc);
 };
 
 #endif /* GCC_RICH_LOCATION_H */
-- 
1.8.5.3

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

* Re: [committed] diagnostics: support compact printing of secondary locations
  2017-07-11 14:09   ` [committed] diagnostics: support compact printing of secondary locations David Malcolm
@ 2017-07-11 17:34     ` Richard Sandiford
  0 siblings, 0 replies; 18+ messages in thread
From: Richard Sandiford @ 2017-07-11 17:34 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

David Malcolm <dmalcolm@redhat.com> writes:
> On Mon, 2017-07-03 at 19:57 +0100, Richard Sandiford wrote:
>> [Thanks for all your diagnostic work btw.]
>> 
>> David Malcolm <dmalcolm@redhat.com> writes:
>> > clang can also print notes about matching opening symbols
>> > e.g. the note here:
>> > 
>> >   missing-symbol-2.c:25:22: error: expected ']'
>> >     const char test [42;
>> >                        ^
>> >   missing-symbol-2.c:25:19: note: to match this '['
>> >     const char test [42;
>> >                     ^
>> > which, although somewhat redundant for this example, seems much
>> > more
>> > useful if there's non-trivial nesting of constructs, or more than a
>> > few
>> > lines separating the open/close symbols (e.g. showing a stray
>> > "namespace {"
>> > that the user forgot to close).
>> > 
>> > I'd like to implement both of these ideas as followups, but in
>> > the meantime, is the fix-it hint patch OK for trunk?
>> > (successfully bootstrapped & regrtested on x86_64-pc-linux-gnu)
>> 
>> Just wondering: how easy would it be to restrict the note to the
>> kinds
>> of cases you mention?  TBH I think clang goes in for extra notes too
>> much, and it's not always that case that an "expected 'foo'" message
>> really is caused by a missing 'foo'.  It'd be great if there was some
>> way of making the notes a bit more discerning. :-)
>> 
>> Or maybe do something like restrict the extra note to cases in which
>> the
>> opening character is on a different line and use an underlined range
>> when the opening character is on the same line?
>> 
>> Thanks,
>> Richard
>
> Thanks.
>
> This patch implements a new method:
>
>    bool gcc_rich_location::add_location_if_nearby (location_t);
>
> to make it easy for a diagnostic to compactly print secondary locations
> for these kinds of cases, falling back to printing them via a note
> otherwise.
>
> Usage example (adapted from the one in the header):
>
>   gcc_rich_location richloc (primary_loc);
>   bool added secondary = richloc.add_location_if_nearby (secondary_loc);
>   error_at_rich_loc (&richloc, "missing %qs", "}");
>   if (!added secondary)
>     inform (secondary_loc, "here's the associated %qs", "{");
>
> When primary_loc and secondary_loc are on the same line this will print:
>
>   test.c:1:39: error: missing '}'
>    struct same_line { double x; double y; ;
>                     ~                    ^
>
> When they are on different lines, this will print:
>
>   test.c:6:1: error: missing '}'
>    ;
>    ^
>   test.c:3:1: note: here's the associated '{'
>    {
>    ^

Thanks, this looks great!

Richard

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

* Re: [PATCH] C/C++: add fix-it hints for various missing symbols
  2017-07-03 18:04 [PATCH] C/C++: add fix-it hints for various missing symbols David Malcolm
  2017-07-03 18:57 ` Richard Sandiford
  2017-07-03 23:01 ` [PATCH] C/C++: add fix-it hints for various missing symbols Joseph Myers
@ 2017-08-28 16:11 ` Jeff Law
  2017-09-26 13:56 ` [PATCH 0/2] " David Malcolm
  3 siblings, 0 replies; 18+ messages in thread
From: Jeff Law @ 2017-08-28 16:11 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 07/03/2017 12:37 PM, David Malcolm wrote:
> This patch improves our C/C++ frontends' handling of missing
> symbols, by making c_parser_require and cp_parser_require use
> "better" locations for the diagnostic, and insert fix-it hints,
> under certain circumstances (see the comments in the patch for
> full details).
> 
> For example, for this code with a missing semicolon:
> 
>   $ cat test.c
>   int missing_semicolon (void)
>   {
>     return 42
>   }
> 
> trunk currently emits:
> 
>   test.c:4:1: error: expected ‘;’ before ‘}’ token
>    }
>    ^
> 
> This patch adds a fix-it hint for the missing semicolon, and puts
> the error at the location of the missing semicolon, printing the
> followup token as a secondary location:
> 
>   test.c:3:12: error: expected ‘;’ before ‘}’ token
>      return 42
>               ^
>               ;
>    }
>    ~
> 
> More examples can be seen in the test cases.
> 
> For reference, clang prints the following:
> 
>   test.c:3:12: error: expected ';' after return statement
>     return 42
>              ^
>              ;
> 
> i.e. describing what syntactic thing came before, which
> I think is likely to be more meaningful to the user.
> 
> clang can also print notes about matching opening symbols
> e.g. the note here:
> 
>   missing-symbol-2.c:25:22: error: expected ']'
>     const char test [42;
>                        ^
>   missing-symbol-2.c:25:19: note: to match this '['
>     const char test [42;
>                     ^
> which, although somewhat redundant for this example, seems much more
> useful if there's non-trivial nesting of constructs, or more than a few
> lines separating the open/close symbols (e.g. showing a stray "namespace {"
> that the user forgot to close).
> 
> I'd like to implement both of these ideas as followups, but in
> the meantime, is the fix-it hint patch OK for trunk?
> (successfully bootstrapped & regrtested on x86_64-pc-linux-gnu)
> 
> gcc/c-family/ChangeLog:
> 	* c-common.c (c_parse_error): Add RICHLOC param, and use it rather
> 	than implicitly using input_location.
> 	(enum missing_token_insertion_kind): New enum.
> 	(get_missing_token_insertion_kind): New function.
> 	(maybe_suggest_missing_token_insertion): New function.
> 	* c-common.h (c_parse_error): Add RICHLOC param.
> 	(maybe_suggest_missing_token_insertion): New decl.
> 
> gcc/c/ChangeLog:
> 	* c-parser.c (struct c_parser): Add "previous_token_loc" field.
> 	(c_parser_consume_token): Set parser->previous_token_loc.
> 	(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.
> 	(c_parser_require): Add "type_is_unique" param.  Use
> 	c_parser_error_richloc rather than c_parser_error, calling
> 	maybe_suggest_missing_token_insertion.
> 	(c_parser_parms_list_declarator): Override default value of new
> 	"type_is_unique" param to c_parser_require.
> 	(c_parser_asm_statement): Likewise.
> 	* c-parser.h (c_parser_require): Add "type_is_unique" param,
> 	defaulting to true.
> 
> gcc/cp/ChangeLog:
> 	* parser.c (cp_parser_error): Add rich_location to call to
> 	c_parse_error.
> 	(get_required_cpp_ttype): New function.
> 	(cp_parser_required_error): 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 fix-it hint.
> 
> gcc/testsuite/ChangeLog:
> 	* c-c++-common/cilk-plus/AN/parser_errors.c: Update expected
> 	output to reflect changes to reported locations of missing
> 	symbols.
> 	* c-c++-common/cilk-plus/AN/parser_errors2.c: Likewise.
> 	* c-c++-common/cilk-plus/AN/parser_errors3.c: Likewise.
> 	* c-c++-common/cilk-plus/AN/pr61191.c: Likewise.
> 	* c-c++-common/gomp/pr63326.c: Likewise.
> 	* c-c++-common/missing-symbol.c: New test case.
> 	* g++.dg/cpp1y/digit-sep-neg.C: Update expected output to reflect
> 	changes to reported locations of missing symbols.
> 	* g++.dg/cpp1y/pr65202.C: Likewise.
> 	* g++.dg/other/do1.C: Likewise.
> 	* g++.dg/missing-symbol-2.C: New test case.
> 	* g++.dg/parse/error11.C: Update expected output to reflect
> 	changes to reported locations of missing symbols.
> 	* g++.dg/parse/pragma2.C: Likewise.
> 	* g++.dg/template/error11.C: Likewise.
> 	* gcc.dg/missing-symbol-2.c: New test case.
> 	* gcc.dg/missing-symbol-3.c: New test case.
> 	* gcc.dg/noncompile/940112-1.c: Update expected output to reflect
> 	changes to reported locations of missing symbols.
> 	* gcc.dg/noncompile/971104-1.c: Likewise.
> 	* obj-c++.dg/exceptions-6.mm: Likewise.
> 	* obj-c++.dg/pr48187.mm: Likewise.
> 	* objc.dg/exceptions-6.m: Likewise.
AFAICT, this never got moved forward after the comments from Richard and
Joseph.




> +}
> +
> +/* Given RICHLOC, a location for a diagnostic describing a missing token
> +   of kind TOKEN_TYPE, potentially add a fix-it hint suggesting the
> +   insertion of the token.
> +
> +   The location of the attemped fix-it hint depends on TOKEN_TYPE:
s/attemped/attempted/


> +
> +  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);
> +
> +      rich_location richloc (line_table, input_location);
So is it worth trying to factor the bits you want to emulate from
cp_parser_error so that they're shared?  Or just a comment in
cp_parser_error in the hopes that if someone changes it in a meaningful
way they'll know to come back here and potentially update this routine?

In general though it looks really good.  I think we just want to make a
decision whether or not there's some way to avoid a long term
maintenance headache noted immediately above.

jeff
Jeff

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

* [PATCH 0/2] Re: [PATCH] C/C++: add fix-it hints for various missing symbols
  2017-07-03 18:04 [PATCH] C/C++: add fix-it hints for various missing symbols David Malcolm
                   ` (2 preceding siblings ...)
  2017-08-28 16:11 ` Jeff Law
@ 2017-09-26 13:56 ` David Malcolm
  2017-09-26 13:56   ` [PATCH 1/2] C++: avoid partial duplicate implementation of cp_parser_error David Malcolm
  2017-09-26 13:56   ` [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v2) David Malcolm
  3 siblings, 2 replies; 18+ messages in thread
From: David Malcolm @ 2017-09-26 13:56 UTC (permalink / raw)
  To: Jeff Law, gcc-patches; +Cc: David Malcolm

On Mon, 2017-08-28 at 09:22 -0600, Jeff Law wrote:
> On 07/03/2017 12:37 PM, David Malcolm wrote:
> > This patch improves our C/C++ frontends' handling of missing
> > symbols, by making c_parser_require and cp_parser_require use
> > "better" locations for the diagnostic, and insert fix-it hints,
> > under certain circumstances (see the comments in the patch for
> > full details).
> > 
> > For example, for this code with a missing semicolon:
> > 
> >   $ cat test.c
> >   int missing_semicolon (void)
> >   {
> >     return 42
> >   }
> > 
> > trunk currently emits:
> > 
> >   test.c:4:1: error: expected ‘;’ before ‘}’ token
> >    }
> >    ^
> > 
> > This patch adds a fix-it hint for the missing semicolon, and puts
> > the error at the location of the missing semicolon, printing the
> > followup token as a secondary location:
> > 
> >   test.c:3:12: error: expected ‘;’ before ‘}’ token
> >      return 42
> >               ^
> >               ;
> >    }
> >    ~
> > 
> > More examples can be seen in the test cases.
> > 
> > For reference, clang prints the following:
> > 
> >   test.c:3:12: error: expected ';' after return statement
> >     return 42
> >              ^
> >              ;
> > 
> > i.e. describing what syntactic thing came before, which
> > I think is likely to be more meaningful to the user.
> > 
> > clang can also print notes about matching opening symbols
> > e.g. the note here:
> > 
> >   missing-symbol-2.c:25:22: error: expected ']'
> >     const char test [42;
> >                        ^
> >   missing-symbol-2.c:25:19: note: to match this '['
> >     const char test [42;
> >                     ^
> > which, although somewhat redundant for this example, seems much
> > more
> > useful if there's non-trivial nesting of constructs, or more than a
> > few
> > lines separating the open/close symbols (e.g. showing a stray
> > "namespace {"
> > that the user forgot to close).
> > 
> > I'd like to implement both of these ideas as followups, but in
> > the meantime, is the fix-it hint patch OK for trunk?
> > (successfully bootstrapped & regrtested on x86_64-pc-linux-gnu)
> > 
> > gcc/c-family/ChangeLog:
> > 	* c-common.c (c_parse_error): Add RICHLOC param, and use it
> > rather
> > 	than implicitly using input_location.
> > 	(enum missing_token_insertion_kind): New enum.
> > 	(get_missing_token_insertion_kind): New function.
> > 	(maybe_suggest_missing_token_insertion): New function.
> > 	* c-common.h (c_parse_error): Add RICHLOC param.
> > 	(maybe_suggest_missing_token_insertion): New decl.
> > 
> > gcc/c/ChangeLog:
> > 	* c-parser.c (struct c_parser): Add "previous_token_loc" field.
> > 	(c_parser_consume_token): Set parser->previous_token_loc.
> > 	(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.
> > 	(c_parser_require): Add "type_is_unique" param.  Use
> > 	c_parser_error_richloc rather than c_parser_error, calling
> > 	maybe_suggest_missing_token_insertion.
> > 	(c_parser_parms_list_declarator): Override default value of new
> > 	"type_is_unique" param to c_parser_require.
> > 	(c_parser_asm_statement): Likewise.
> > 	* c-parser.h (c_parser_require): Add "type_is_unique" param,
> > 	defaulting to true.
> > 
> > gcc/cp/ChangeLog:
> > 	* parser.c (cp_parser_error): Add rich_location to call to
> > 	c_parse_error.
> > 	(get_required_cpp_ttype): New function.
> > 	(cp_parser_required_error): 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 fix-it hint.
> > 
> > gcc/testsuite/ChangeLog:
> > 	* c-c++-common/cilk-plus/AN/parser_errors.c: Update expected
> > 	output to reflect changes to reported locations of missing
> > 	symbols.
> > 	* c-c++-common/cilk-plus/AN/parser_errors2.c: Likewise.
> > 	* c-c++-common/cilk-plus/AN/parser_errors3.c: Likewise.
> > 	* c-c++-common/cilk-plus/AN/pr61191.c: Likewise.
> > 	* c-c++-common/gomp/pr63326.c: Likewise.
> > 	* c-c++-common/missing-symbol.c: New test case.
> > 	* g++.dg/cpp1y/digit-sep-neg.C: Update expected output to
> > reflect
> > 	changes to reported locations of missing symbols.
> > 	* g++.dg/cpp1y/pr65202.C: Likewise.
> > 	* g++.dg/other/do1.C: Likewise.
> > 	* g++.dg/missing-symbol-2.C: New test case.
> > 	* g++.dg/parse/error11.C: Update expected output to reflect
> > 	changes to reported locations of missing symbols.
> > 	* g++.dg/parse/pragma2.C: Likewise.
> > 	* g++.dg/template/error11.C: Likewise.
> > 	* gcc.dg/missing-symbol-2.c: New test case.
> > 	* gcc.dg/missing-symbol-3.c: New test case.
> > 	* gcc.dg/noncompile/940112-1.c: Update expected output to
> > reflect
> > 	changes to reported locations of missing symbols.
> > 	* gcc.dg/noncompile/971104-1.c: Likewise.
> > 	* obj-c++.dg/exceptions-6.mm: Likewise.
> > 	* obj-c++.dg/pr48187.mm: Likewise.
> > 	* objc.dg/exceptions-6.m: Likewise.
> 
> AFAICT, this never got moved forward after the comments from Richard
> and
> Joseph.


Here's an updated version of the patch, addressing the various issues
raised.


> > +}
> > +
> > +/* Given RICHLOC, a location for a diagnostic describing a missing
> > token
> > +   of kind TOKEN_TYPE, potentially add a fix-it hint suggesting
> > the
> > +   insertion of the token.
> > +
> > +   The location of the attemped fix-it hint depends on TOKEN_TYPE:
> 
> s/attemped/attempted/

Fixed.

> > +
> > +  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);
> > +
> > +      rich_location richloc (line_table, input_location);
> 
> So is it worth trying to factor the bits you want to emulate from
> cp_parser_error so that they're shared?  Or just a comment in
> cp_parser_error in the hopes that if someone changes it in a
> meaningful
> way they'll know to come back here and potentially update this
> routine?

I ended up factoring out the shared bits into a new subroutine
(patch 1 of the kit).

> In general though it looks really good.  I think we just want to make
> a
> decision whether or not there's some way to avoid a long term
> maintenance headache noted immediately above.
> 
> jeff
> Jeff

Thanks.  Here's an updated version of the patch, split out as a 
two patch kit.

Patch 1 implements the cleanup of the duplicated code.
Patch 2 implements the fix-it hints.

Are they OK for trunk?

Dave

David Malcolm (2):
  C++: avoid partial duplicate implementation of cp_parser_error
  C/C++: add fix-it hints for various missing symbols (v2)

 gcc/c-family/c-common.c                            | 158 +++++++++++++++
 gcc/c-family/c-common.h                            |   3 +
 gcc/c/c-parser.c                                   |  29 ++-
 gcc/c/c-parser.h                                   |   3 +-
 gcc/cp/parser.c                                    | 214 ++++++++++++++-------
 .../c-c++-common/cilk-plus/AN/parser_errors.c      |   4 +-
 .../c-c++-common/cilk-plus/AN/parser_errors2.c     |   3 +-
 .../c-c++-common/cilk-plus/AN/parser_errors3.c     |   3 +-
 gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c  |   3 +-
 gcc/testsuite/c-c++-common/gomp/pr63326.c          |  22 +--
 gcc/testsuite/c-c++-common/missing-close-symbol.c  |   2 +
 gcc/testsuite/c-c++-common/missing-symbol.c        |  35 ++--
 gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C         |   4 +-
 gcc/testsuite/g++.dg/cpp1y/pr65202.C               |   4 +-
 gcc/testsuite/g++.dg/missing-symbol-2.C            |  58 ++++++
 gcc/testsuite/g++.dg/other/do1.C                   |   4 +-
 gcc/testsuite/g++.dg/parse/error11.C               |   2 +-
 gcc/testsuite/g++.dg/parse/pragma2.C               |   4 +-
 gcc/testsuite/g++.dg/template/error11.C            |   2 +-
 gcc/testsuite/gcc.dg/missing-symbol-2.c            |  71 +++++++
 gcc/testsuite/gcc.dg/missing-symbol-3.c            |  50 +++++
 gcc/testsuite/gcc.dg/noncompile/940112-1.c         |   4 +-
 gcc/testsuite/gcc.dg/noncompile/971104-1.c         |   4 +-
 gcc/testsuite/obj-c++.dg/exceptions-6.mm           |   6 +-
 gcc/testsuite/obj-c++.dg/pr48187.mm                |   8 +-
 gcc/testsuite/objc.dg/exceptions-6.m               |   4 +-
 26 files changed, 574 insertions(+), 130 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/missing-symbol-2.C
 create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-2.c
 create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-3.c

-- 
1.8.5.3

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

* [PATCH 1/2] C++: avoid partial duplicate implementation of cp_parser_error
  2017-09-26 13:56 ` [PATCH 0/2] " David Malcolm
@ 2017-09-26 13:56   ` David Malcolm
  2017-10-11 17:26     ` PING: " David Malcolm
  2017-10-11 21:21     ` Jason Merrill
  2017-09-26 13:56   ` [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v2) David Malcolm
  1 sibling, 2 replies; 18+ messages in thread
From: David Malcolm @ 2017-09-26 13:56 UTC (permalink / raw)
  To: Jeff Law, gcc-patches; +Cc: David Malcolm

In r251026 (aka 3fe34694f0990d1d649711ede0326497f8a849dc,
"C/C++: show pertinent open token when missing a close token")
I copied part of cp_parser_error into cp_parser_required_error,
leading to duplication of code.

This patch eliminates this duplication by merging the two copies of the
code into a new cp_parser_error_1 subroutine.

Doing so removes an indentation level, making the patch appear to have
more churn than it really does.

The patch also undoes the change to g++.dg/parse/pragma2.C, as the
old behavior is restored.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/cp/ChangeLog:
	* parser.c (get_matching_symbol): Move to before...
	(cp_parser_error): Split out into...
	(cp_parser_error_1): ...this new function, merging in content
	from...
	(cp_parser_required_error): ...here.  Eliminate partial duplicate
	of body of cp_parser_error in favor of a call to the new
	cp_parser_error_1 helper function.

gcc/testsuite/ChangeLog:
	* g++.dg/parse/pragma2.C: Update to reflect reinstatement of the
	"#pragma is not allowed here" error.
---
 gcc/cp/parser.c                      | 169 ++++++++++++++++++++---------------
 gcc/testsuite/g++.dg/parse/pragma2.C |   4 +-
 2 files changed, 97 insertions(+), 76 deletions(-)

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 25b91df..56d9442 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2767,53 +2767,116 @@ cp_lexer_peek_conflict_marker (cp_lexer *lexer, enum cpp_ttype tok1_kind,
   return true;
 }
 
-/* If not parsing tentatively, issue a diagnostic of the form
+/* 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 "(";
+    }
+}
+
+/* Subroutine of cp_parser_error and cp_parser_required_error.
+
+   Issue a diagnostic of the form
       FILE:LINE: MESSAGE before TOKEN
    where TOKEN is the next token in the input stream.  MESSAGE
    (specified by the caller) is usually of the form "expected
-   OTHER-TOKEN".  */
+   OTHER-TOKEN".
+
+   This bypasses the check for tentative passing, and potentially
+   adds material needed by cp_parser_required_error.
+
+   If MISSING_TOKEN_DESC is not RT_NONE, and MATCHING_LOCATION is not
+   UNKNOWN_LOCATION, then we have an unmatched symbol at
+   MATCHING_LOCATION; highlight this secondary location.  */
 
 static void
-cp_parser_error (cp_parser* parser, const char* gmsgid)
+cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
+		   required_token missing_token_desc,
+		   location_t matching_location)
 {
-  if (!cp_parser_simulate_error (parser))
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  /* This diagnostic makes more sense if it is tagged to the line
+     of the token we just peeked at.  */
+  cp_lexer_set_source_position_from_token (token);
+
+  if (token->type == CPP_PRAGMA)
     {
-      cp_token *token = cp_lexer_peek_token (parser->lexer);
-      /* This diagnostic makes more sense if it is tagged to the line
-	 of the token we just peeked at.  */
-      cp_lexer_set_source_position_from_token (token);
+      error_at (token->location,
+		"%<#pragma%> is not allowed here");
+      cp_parser_skip_to_pragma_eol (parser, token);
+      return;
+    }
 
-      if (token->type == CPP_PRAGMA)
+  /* If this is actually a conflict marker, report it as such.  */
+  if (token->type == CPP_LSHIFT
+      || token->type == CPP_RSHIFT
+      || token->type == CPP_EQ_EQ)
+    {
+      location_t loc;
+      if (cp_lexer_peek_conflict_marker (parser->lexer, token->type, &loc))
 	{
-	  error_at (token->location,
-		    "%<#pragma%> is not allowed here");
-	  cp_parser_skip_to_pragma_eol (parser, token);
+	  error_at (loc, "version control conflict marker in file");
 	  return;
 	}
+    }
 
-      /* If this is actually a conflict marker, report it as such.  */
-      if (token->type == CPP_LSHIFT
-	  || token->type == CPP_RSHIFT
-	  || token->type == CPP_EQ_EQ)
-	{
-	  location_t loc;
-	  if (cp_lexer_peek_conflict_marker (parser->lexer, token->type, &loc))
-	    {
-	      error_at (loc, "version control conflict marker in file");
-	      return;
-	    }
-	}
+  gcc_rich_location richloc (input_location);
+
+  bool added_matching_location = false;
+
+  if (missing_token_desc != RT_NONE)
+    {
+      /* If matching_location != UNKNOWN_LOCATION, highlight it.
+	 Attempt to consolidate diagnostics by printing it as a
+	secondary range within the main diagnostic.  */
+      if (matching_location != UNKNOWN_LOCATION)
+	added_matching_location
+	  = richloc.add_location_if_nearby (matching_location);
+    }
+
+  /* Actually emit the error.  */
+  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, &richloc);
 
-      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, &richloc);
+  if (missing_token_desc != RT_NONE)
+    {
+      /* 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 (missing_token_desc));
     }
 }
 
+/* If not parsing tentatively, issue a diagnostic of the form
+      FILE:LINE: MESSAGE before TOKEN
+   where TOKEN is the next token in the input stream.  MESSAGE
+   (specified by the caller) is usually of the form "expected
+   OTHER-TOKEN".  */
+
+static void
+cp_parser_error (cp_parser* parser, const char* gmsgid)
+{
+  if (!cp_parser_simulate_error (parser))
+    cp_parser_error_1 (parser, gmsgid, RT_NONE, UNKNOWN_LOCATION);
+}
+
 /* Issue an error about name-lookup failing.  NAME is the
    IDENTIFIER_NODE DECL is the result of
    the lookup (as returned from cp_parser_lookup_name).  DESIRED is
@@ -27960,24 +28023,6 @@ 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
@@ -28155,31 +28200,7 @@ cp_parser_required_error (cp_parser *parser,
     }
 
   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));
-    }
+    cp_parser_error_1 (parser, gmsgid, token_desc, matching_location);
 }
 
 
diff --git a/gcc/testsuite/g++.dg/parse/pragma2.C b/gcc/testsuite/g++.dg/parse/pragma2.C
index 3dc5fc1..c5616ff 100644
--- a/gcc/testsuite/g++.dg/parse/pragma2.C
+++ b/gcc/testsuite/g++.dg/parse/pragma2.C
@@ -4,5 +4,5 @@
 // does not.
 int f(int x,
 #pragma interface  // { dg-error "not allowed here" }
-      // { dg-bogus "expected identifier" "" { xfail *-*-* } .-1 }
-      int y);
+      // The parser gets confused and issues an error on the next line.
+      int y); // { dg-bogus "" "" { xfail *-*-* } } 
-- 
1.8.5.3

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

* [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v2)
  2017-09-26 13:56 ` [PATCH 0/2] " David Malcolm
  2017-09-26 13:56   ` [PATCH 1/2] C++: avoid partial duplicate implementation of cp_parser_error David Malcolm
@ 2017-09-26 13:56   ` David Malcolm
  2017-10-05 16:08     ` [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v3) David Malcolm
  1 sibling, 1 reply; 18+ messages in thread
From: David Malcolm @ 2017-09-26 13:56 UTC (permalink / raw)
  To: Jeff Law, gcc-patches; +Cc: David Malcolm

The patch improves our C/C++ frontends' handling of missing
symbols, by making c_parser_require and cp_parser_require use
"better" locations for the diagnostic, and insert fix-it hints,
under certain circumstances (see the comments in the patch for
full details).

For example, for this code with a missing semicolon:

  $ cat test.c
  int missing_semicolon (void)
  {
    return 42
  }

  trunk currently emits:

  test.c:4:1: error: expected ';' before '}' token
   }
   ^

This patch adds a fix-it hint for the missing semicolon, and puts
the error at the location of the missing semicolon, printing the
followup token as a secondary location:

  test.c:3:12: error: expected ';' before '}' token
     return 42
              ^
              ;
   }
   ~

More examples can be seen in the test cases.

This is a revised version of the patch I posted here:
  https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00135.html

Some of the changes in that patch landed in trunk in r251026
(aka 3fe34694f0990d1d649711ede0326497f8a849dc
"C/C++: show pertinent open token when missing a close token"),
so this patch contains the remaining part, updated also for the
previous patch that reunifies the cloned copiesc of cp_parser_error
introduced in r251026.

It also:
- fixes the typo seen by Jeff
- eliminated some unnecessary changes to c-c++-common/missing-symbol.c
- fixes some bugs

r250133, r250134, and r251026 already incorporated the suggestion from
Richard Sandiford to consolidate note-printing when the matching location
is near the primary location of the diagnostic.

This patch doesn't address Joseph's requests to tackle PR 7356 and
PR 18248, but he said that it was OK to leave these for followups.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu
in conjunction with patch 1 of the kit.

OK for trunk?

gcc/c-family/ChangeLog:
	* c-common.c (enum missing_token_insertion_kind): New enum.
	(get_missing_token_insertion_kind): New function.
	(maybe_suggest_missing_token_insertion): New function.
	* c-common.h (maybe_suggest_missing_token_insertion): New decl.

gcc/c/ChangeLog:
	* c-parser.c (struct c_parser): Add "previous_token_loc" field.
	(c_parser_consume_token): Set parser->previous_token_loc.
	(get_matching_symbol): Likewise.
	(c_parser_require): Add "type_is_unique" param and use it
	to guard calls to maybe_suggest_missing_token_insertion.
	(c_parser_parms_list_declarator): Override default value of new
	"type_is_unique" param to c_parser_require.
	(c_parser_asm_statement): Likewise.
	* c-parser.h (c_parser_require): Add "type_is_unique" param,
	defaulting to true.

gcc/cp/ChangeLog:
	* parser.c (get_required_cpp_ttype): New function.
	(cp_parser_error_1): Call it, using the result to call
	maybe_suggest_missing_token_insertion.

gcc/testsuite/ChangeLog:
	* c-c++-common/cilk-plus/AN/parser_errors.c: Update expected
	output to reflect changes to reported locations of missing
	symbols.
	* c-c++-common/cilk-plus/AN/parser_errors2.c: Likewise.
	* c-c++-common/cilk-plus/AN/parser_errors3.c: Likewise.
	* c-c++-common/cilk-plus/AN/pr61191.c: Likewise.
	* c-c++-common/gomp/pr63326.c: Likewise.
	* c-c++-common/missing-close-symbol.c: Likewise, also update for
	new fix-it hints.
	* c-c++-common/missing-symbol.c: Likewise, also add test coverage
	for missing colon in ternary operator.
	* g++.dg/cpp1y/digit-sep-neg.C: Likewise.
	* g++.dg/cpp1y/pr65202.C: Likewise.
	* g++.dg/missing-symbol-2.C: New test case.
	* g++.dg/other/do1.C: Update expected output to reflect
	changes to reported locations of missing symbols.
	* g++.dg/parse/error11.C: Likewise.
	* g++.dg/template/error11.C: Likewise.
	* gcc.dg/missing-symbol-2.c: New test case.
	* gcc.dg/missing-symbol-3.c: New test case.
	* gcc.dg/noncompile/940112-1.c: Update expected output to reflect
	changes to reported locations of missing symbols.
	* gcc.dg/noncompile/971104-1.c: Likewise.
	* obj-c++.dg/exceptions-6.mm: Likewise.
	* obj-c++.dg/pr48187.mm: Likewise.
	* objc.dg/exceptions-6.m: Likewise.
---
 gcc/c-family/c-common.c                            | 158 +++++++++++++++++++++
 gcc/c-family/c-common.h                            |   3 +
 gcc/c/c-parser.c                                   |  29 +++-
 gcc/c/c-parser.h                                   |   3 +-
 gcc/cp/parser.c                                    |  51 ++++++-
 .../c-c++-common/cilk-plus/AN/parser_errors.c      |   4 +-
 .../c-c++-common/cilk-plus/AN/parser_errors2.c     |   3 +-
 .../c-c++-common/cilk-plus/AN/parser_errors3.c     |   3 +-
 gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c  |   3 +-
 gcc/testsuite/c-c++-common/gomp/pr63326.c          |  22 +--
 gcc/testsuite/c-c++-common/missing-close-symbol.c  |   2 +
 gcc/testsuite/c-c++-common/missing-symbol.c        |  35 +++--
 gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C         |   4 +-
 gcc/testsuite/g++.dg/cpp1y/pr65202.C               |   4 +-
 gcc/testsuite/g++.dg/missing-symbol-2.C            |  58 ++++++++
 gcc/testsuite/g++.dg/other/do1.C                   |   4 +-
 gcc/testsuite/g++.dg/parse/error11.C               |   2 +-
 gcc/testsuite/g++.dg/template/error11.C            |   2 +-
 gcc/testsuite/gcc.dg/missing-symbol-2.c            |  71 +++++++++
 gcc/testsuite/gcc.dg/missing-symbol-3.c            |  50 +++++++
 gcc/testsuite/gcc.dg/noncompile/940112-1.c         |   4 +-
 gcc/testsuite/gcc.dg/noncompile/971104-1.c         |   4 +-
 gcc/testsuite/obj-c++.dg/exceptions-6.mm           |   6 +-
 gcc/testsuite/obj-c++.dg/pr48187.mm                |   8 +-
 gcc/testsuite/objc.dg/exceptions-6.m               |   4 +-
 25 files changed, 480 insertions(+), 57 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/missing-symbol-2.C
 create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-2.c
 create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-3.c

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index b3ec3a0..a23fa5f 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -7946,6 +7946,164 @@ c_flt_eval_method (bool maybe_c11_only_p)
     return c_ts18661_flt_eval_method ();
 }
 
+/* An enum for get_missing_token_insertion_kind for describing the best
+   place to insert a missing token, if there is one.  */
+
+enum missing_token_insertion_kind
+{
+  MTIK_IMPOSSIBLE,
+  MTIK_INSERT_BEFORE_NEXT,
+  MTIK_INSERT_AFTER_PREV
+};
+
+/* Given a missing token of TYPE, determine if it is reasonable to
+   emit a fix-it hint suggesting the insertion of the token, and,
+   if so, where the token should be inserted relative to other tokens.
+
+   It only makes sense to do this for values of TYPE that are symbols.
+
+   Some symbols should go before the next token, e.g. in:
+     if flag)
+   we want to insert the missing '(' immediately before "flag",
+   giving:
+     if (flag)
+   rather than:
+     if( flag)
+   These use MTIK_INSERT_BEFORE_NEXT.
+
+   Other symbols should go after the previous token, e.g. in:
+     if (flag
+       do_something ();
+   we want to insert the missing ')' immediately after the "flag",
+   giving:
+     if (flag)
+       do_something ();
+   rather than:
+     if (flag
+       )do_something ();
+   These use MTIK_INSERT_AFTER_PREV.  */
+
+static enum missing_token_insertion_kind
+get_missing_token_insertion_kind (enum cpp_ttype type)
+{
+  switch (type)
+    {
+      /* Insert missing "opening" brackets immediately
+	 before the next token.  */
+    case CPP_OPEN_SQUARE:
+    case CPP_OPEN_PAREN:
+      return MTIK_INSERT_BEFORE_NEXT;
+
+      /* Insert other missing symbols immediately after
+	 the previous token.  */
+    case CPP_CLOSE_PAREN:
+    case CPP_CLOSE_SQUARE:
+    case CPP_SEMICOLON:
+    case CPP_COMMA:
+    case CPP_COLON:
+      return MTIK_INSERT_AFTER_PREV;
+
+      /* Other kinds of token don't get fix-it hints.  */
+    default:
+      return MTIK_IMPOSSIBLE;
+    }
+}
+
+/* Given RICHLOC, a location for a diagnostic describing a missing token
+   of kind TOKEN_TYPE, potentially add a fix-it hint suggesting the
+   insertion of the token.
+
+   The location of the attempted fix-it hint depends on TOKEN_TYPE:
+   it will either be:
+     (a) immediately after PREV_TOKEN_LOC, or
+
+     (b) immediately before the primary location within RICHLOC (taken to
+	 be that of the token following where the token was expected).
+
+   If we manage to add a fix-it hint, then the location of the
+   fix-it hint is likely to be more useful as the primary location
+   of the diagnostic than that of the following token, so we swap
+   these locations.
+
+   For example, given this bogus code:
+       123456789012345678901234567890
+   1 | int missing_semicolon (void)
+   2 | {
+   3 |   return 42
+   4 | }
+
+   we will emit:
+
+     "expected ';' before '}'"
+
+   RICHLOC's primary location is at the closing brace, so before "swapping"
+   we would emit the error at line 4 column 1:
+
+       123456789012345678901234567890
+   3 |   return 42  |< fix-it hint emitted for this line
+     |            ; |
+   4 | }            |< "expected ';' before '}'" emitted at this line
+     | ^            |
+
+   It's more useful for the location of the diagnostic to be at the
+   fix-it hint, so we swap the locations, so the primary location
+   is at the fix-it hint, with the old primary location inserted
+   as a secondary location, giving this, with the error at line 3
+   column 12:
+
+       123456789012345678901234567890
+   3 |   return 42   |< "expected ';' before '}'" emitted at this line,
+     |            ^  |   with fix-it hint
+   4 |            ;  |
+     | }             |< secondary range emitted here
+     | ~             |.  */
+
+void
+maybe_suggest_missing_token_insertion (rich_location *richloc,
+				       enum cpp_ttype token_type,
+				       location_t prev_token_loc)
+{
+  gcc_assert (richloc);
+
+  enum missing_token_insertion_kind mtik
+    = get_missing_token_insertion_kind (token_type);
+
+  switch (mtik)
+    {
+    default:
+      gcc_unreachable ();
+      break;
+
+    case MTIK_IMPOSSIBLE:
+      return;
+
+    case MTIK_INSERT_BEFORE_NEXT:
+      /* Attempt to add the fix-it hint before the primary location
+	 of RICHLOC.  */
+      richloc->add_fixit_insert_before (cpp_type2name (token_type, 0));
+      break;
+
+    case MTIK_INSERT_AFTER_PREV:
+      /* Attempt to add the fix-it hint after PREV_TOKEN_LOC.  */
+      richloc->add_fixit_insert_after (prev_token_loc,
+				       cpp_type2name (token_type, 0));
+      break;
+    }
+
+  /* If we were successful, use the fix-it hint's location as the
+     primary location within RICHLOC, adding the old primary location
+     back as a secondary location.  */
+  if (!richloc->seen_impossible_fixit_p ())
+    {
+      fixit_hint *hint = richloc->get_last_fixit_hint ();
+      location_t hint_loc = hint->get_start_loc ();
+      location_t old_loc = richloc->get_loc ();
+
+      richloc->set_range (line_table, 0, hint_loc, true);
+      richloc->add_range (old_loc, false);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index da6a0be..7e1877e 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1550,6 +1550,9 @@ extern int c_flt_eval_method (bool ts18661_p);
 extern void add_no_sanitize_value (tree node, unsigned int flags);
 
 extern void maybe_add_include_fixit (rich_location *, const char *);
+extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
+						   enum cpp_ttype token_type,
+						   location_t prev_token_loc);
 
 #if CHECKING_P
 namespace selftest {
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index a36397b..20f9ed7 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -206,6 +206,9 @@ struct GTY(()) c_parser {
   /* Buffer to hold all the tokens from parsing the vector attribute for the
      SIMD-enabled functions (formerly known as elemental functions).  */
   vec <c_token, va_gc> *cilk_simd_fn_tokens;
+
+  /* Location of the most-recently consumed token.  */
+  location_t previous_token_loc;
 };
 
 /* Return a pointer to the Nth token in PARSERs tokens_buf.  */
@@ -770,6 +773,7 @@ c_parser_consume_token (c_parser *parser)
   gcc_assert (parser->tokens[0].type != CPP_EOF);
   gcc_assert (!parser->in_pragma || parser->tokens[0].type != CPP_PRAGMA_EOL);
   gcc_assert (parser->error || parser->tokens[0].type != CPP_PRAGMA);
+  parser->previous_token_loc = parser->tokens[0].location;
   if (parser->tokens != &parser->tokens_buf[0])
     parser->tokens++;
   else if (parser->tokens_avail == 2)
@@ -1037,13 +1041,21 @@ get_matching_symbol (enum cpp_ttype type)
    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).  */
+   CPP_CLOSE_PAREN).
+
+   If TYPE_IS_UNIQUE is true (the default) then msgid describes exactly
+   one type (e.g. "expected %<)%>") and thus it may be reasonable to
+   attempt to generate a fix-it hint for the problem.
+   Otherwise msgid describes multiple token types (e.g.
+   "expected %<;%>, %<,%> or %<)%>"), and thus we shouldn't attempt to
+   generate a fix-it hint.  */
 
 bool
 c_parser_require (c_parser *parser,
 		  enum cpp_ttype type,
 		  const char *msgid,
-		  location_t matching_location)
+		  location_t matching_location,
+		  bool type_is_unique)
 {
   if (c_parser_next_token_is (parser, type))
     {
@@ -1055,6 +1067,13 @@ c_parser_require (c_parser *parser,
       location_t next_token_loc = c_parser_peek_token (parser)->location;
       gcc_rich_location richloc (next_token_loc);
 
+      /* Potentially supply a fix-it hint, suggesting to add the
+	 missing token immediately after the *previous* token.
+	 This may move the primary location within richloc.  */
+      if (!parser->error && type_is_unique)
+	maybe_suggest_missing_token_insertion (&richloc, type,
+					       parser->previous_token_loc);
+
       /* If matching_location != UNKNOWN_LOCATION, highlight it.
 	 Attempt to consolidate diagnostics by printing it as a
 	 secondary range within the main diagnostic.  */
@@ -3967,7 +3986,8 @@ c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr)
 	    return get_parm_info (false, expr);
 	}
       if (!c_parser_require (parser, CPP_COMMA,
-			     "expected %<;%>, %<,%> or %<)%>"))
+			     "expected %<;%>, %<,%> or %<)%>",
+			     UNKNOWN_LOCATION, false))
 	{
 	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
 	  return NULL;
@@ -6393,7 +6413,8 @@ c_parser_asm_statement (c_parser *parser)
       if (!c_parser_require (parser, CPP_COLON,
 			     is_goto
 			     ? G_("expected %<:%>")
-			     : G_("expected %<:%> or %<)%>")))
+			     : G_("expected %<:%> or %<)%>"),
+			     UNKNOWN_LOCATION, is_goto))
 	goto error_close_paren;
 
       /* Once past any colon, we're no longer a simple asm.  */
diff --git a/gcc/c/c-parser.h b/gcc/c/c-parser.h
index 01a7b72..21e4054 100644
--- a/gcc/c/c-parser.h
+++ b/gcc/c/c-parser.h
@@ -137,7 +137,8 @@ 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,
-			      location_t matching_location = UNKNOWN_LOCATION);
+			      location_t matching_location = UNKNOWN_LOCATION,
+			      bool type_is_unique=true);
 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,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 56d9442..d831d66 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2785,6 +2785,40 @@ get_matching_symbol (required_token token_desc)
     }
 }
 
+/* Attempt to convert TOKEN_DESC from a required_token to an
+   enum cpp_ttype, returning CPP_EOF if there is no good conversion.  */
+
+static enum cpp_ttype
+get_required_cpp_ttype (required_token token_desc)
+{
+  switch (token_desc)
+    {
+    case RT_SEMICOLON:
+      return CPP_SEMICOLON;
+    case RT_OPEN_PAREN:
+      return CPP_OPEN_PAREN;
+    case RT_CLOSE_BRACE:
+      return CPP_CLOSE_BRACE;
+    case RT_OPEN_BRACE:
+      return CPP_OPEN_BRACE;
+    case RT_CLOSE_SQUARE:
+      return CPP_CLOSE_SQUARE;
+    case RT_OPEN_SQUARE:
+      return CPP_OPEN_SQUARE;
+    case RT_COMMA:
+      return CPP_COMMA;
+    case RT_COLON:
+      return CPP_COLON;
+    case RT_CLOSE_PAREN:
+      return CPP_CLOSE_PAREN;
+
+    default:
+      /* Use CPP_EOF as a "no completions possible" code.  */
+      return CPP_EOF;
+    }
+}
+
+
 /* Subroutine of cp_parser_error and cp_parser_required_error.
 
    Issue a diagnostic of the form
@@ -2796,9 +2830,12 @@ get_matching_symbol (required_token token_desc)
    This bypasses the check for tentative passing, and potentially
    adds material needed by cp_parser_required_error.
 
-   If MISSING_TOKEN_DESC is not RT_NONE, and MATCHING_LOCATION is not
-   UNKNOWN_LOCATION, then we have an unmatched symbol at
-   MATCHING_LOCATION; highlight this secondary location.  */
+   If MISSING_TOKEN_DESC is not RT_NONE, then potentially add fix-it hints
+   suggesting insertion of the missing token.
+
+   Additionally, if MATCHING_LOCATION is not UNKNOWN_LOCATION, then we
+   have an unmatched symbol at MATCHING_LOCATION; highlight this secondary
+   location.  */
 
 static void
 cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
@@ -2837,6 +2874,14 @@ cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
 
   if (missing_token_desc != RT_NONE)
     {
+      /* Potentially supply a fix-it hint, suggesting to add the
+	 missing token immediately after the *previous* token.
+	 This may move the primary location within richloc.  */
+      enum cpp_ttype ttype = get_required_cpp_ttype (missing_token_desc);
+      location_t prev_token_loc
+	= cp_lexer_previous_token (parser->lexer)->location;
+      maybe_suggest_missing_token_insertion (&richloc, ttype, prev_token_loc);
+
       /* If matching_location != UNKNOWN_LOCATION, highlight it.
 	 Attempt to consolidate diagnostics by printing it as a
 	secondary range within the main diagnostic.  */
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
index 18816e0..fd4fe54 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
@@ -7,5 +7,5 @@ int main (void)
   
   array2[:] = array2[: ;  /* { dg-error "expected ']'" } */
 
-  return 0;
-} /* { dg-error "expected ';' before" "" { target c } } */
+  return 0; /* { dg-error "expected ';' before" "" { target c } } */
+}
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
index 2bb9134..d003d7c 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
@@ -7,6 +7,7 @@ int main (void)
   
   array2[:] = array2[1:2:] ;  /* { dg-error "expected expression before" "" { target c } } */ 
   /* { dg-error  "expected primary-expression before" "" { target c++ } .-1 } */
+  /* { dg-error "expected ';' before" "" { target c } .-2 } */
 
-  return 0; /* { dg-error "expected ';' before" "" { target c }  } */
+  return 0;
 }
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
index 9270007..14256e9 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
@@ -7,6 +7,7 @@ int main (void)
   
   array2[:] = array2[1: :] ;  /* { dg-error "expected expression before" "" { target c }  } */ 
   /* { dg-error "expected primary-expression before" "" { target c++ }  .-1 } */
+  /* { dg-error "expected ';' before" "" { target c } .-2 } */
 
-  return 0; /* { dg-error "expected ';' before" "" { target c } } */
+  return 0;
 }
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
index a9a9d66..8c32ad9 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
@@ -7,4 +7,5 @@ double f(double * A, double * B)
   return __sec_reduce_add((B[0:500])(; /* { dg-error "called object" "" { target c } } */
 /* { dg-error "expected expression before ';' token" "" { target c } .-1 } */
 /* { dg-error "expected primary-expression before ';' token" "" { target c++ } .-2 } */
-} /* { dg-error "expected" "" { target c } } */
+/* { dg-error "expected" "" { target c } .-3 } */
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/pr63326.c b/gcc/testsuite/c-c++-common/gomp/pr63326.c
index e319f49..3e62723 100644
--- a/gcc/testsuite/c-c++-common/gomp/pr63326.c
+++ b/gcc/testsuite/c-c++-common/gomp/pr63326.c
@@ -156,34 +156,34 @@ f4 (int x)
   {
     do
       #pragma omp barrier			/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp flush				/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp taskwait			/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp taskyield			/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   #pragma omp parallel
   {
     do
       #pragma omp cancel parallel		/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   #pragma omp parallel
   {
     do
       #pragma omp cancellation point parallel	/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   #pragma omp for ordered(1)
   for (i = 0; i < 16; i++)
@@ -191,28 +191,28 @@ f4 (int x)
       {
 	do
 	  #pragma omp ordered depend(source)	/* { dg-error "may only be used in compound statements" } */
-	while (0);
+	while (0); /* { dg-error "before" "" { target c++ } } */
       } /* { dg-error "before" "" { target c++ } } */
       {
 	do
 	  #pragma omp ordered depend(sink: i-1)	/* { dg-error "may only be used in compound statements" } */
-	while (0);
+	while (0); /* { dg-error "before" "" { target c++ } } */
       } /* { dg-error "before" "" { target c++ } } */
     }
   {
     do
       #pragma omp target enter data map(to:i)	/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp target update to(i)		/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp target exit data map(from:i)	/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
 }
 
diff --git a/gcc/testsuite/c-c++-common/missing-close-symbol.c b/gcc/testsuite/c-c++-common/missing-close-symbol.c
index 85b96f28..abeb837 100644
--- a/gcc/testsuite/c-c++-common/missing-close-symbol.c
+++ b/gcc/testsuite/c-c++-common/missing-close-symbol.c
@@ -12,6 +12,7 @@ void test_static_assert_same_line (void)
   /* { dg-begin-multiline-output "" }
    _Static_assert(sizeof(int) >= sizeof(char), "msg";
                  ~                                  ^
+                                                    )
      { dg-end-multiline-output "" } */
 }
 
@@ -25,6 +26,7 @@ void test_static_assert_different_line (void)
   /* { dg-begin-multiline-output "" }
     "msg";
          ^
+         )
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
    _Static_assert(sizeof(int) >= sizeof(char),
diff --git a/gcc/testsuite/c-c++-common/missing-symbol.c b/gcc/testsuite/c-c++-common/missing-symbol.c
index 33a501b..326b9fa 100644
--- a/gcc/testsuite/c-c++-common/missing-symbol.c
+++ b/gcc/testsuite/c-c++-common/missing-symbol.c
@@ -5,15 +5,14 @@ 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 "" } */
+  switch (i /* { dg-error "12: expected '\\)' before '.' token" } */
+    {
   /* { dg-begin-multiline-output "" }
    switch (i
-          ^
+          ~ ^
+            )
+     {
+     ~       
      { dg-end-multiline-output "" } */
 
     case 0:
@@ -30,21 +29,33 @@ int missing_close_paren_in_switch (int i)
 void missing_close_paren_in_if (void)
 {
   if (foo () /* { dg-line start_of_if } */
-      && bar () 
-    { /* { dg-error "5: expected '\\)' before '.' token" } */
+      && bar () /* { dg-error "16: expected '\\)' before '.' token" } */
+    {
       /* { dg-begin-multiline-output "" }
+       && bar ()
+                ^
+                )
      {
-     ^
+     ~           
          { 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-end-multiline-output "" } */
     }
-
 } /* { dg-error "1: expected" } */
   /* { dg-begin-multiline-output "" }
  }
  ^
      { dg-end-multiline-output "" } */
+
+int missing_colon_in_ternary (int flag)
+{
+  return flag ? 42 0; /* { dg-error "expected ':' before numeric constant" } */
+  /* { dg-begin-multiline-output "" }
+   return flag ? 42 0;
+                   ^~
+                   :
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C b/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
index 833fab7..727e74e 100644
--- a/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
+++ b/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
@@ -26,5 +26,5 @@ main()
 }
 
 // { dg-error "exponent has no digits" "exponent has no digits" { target *-*-* } 21 }
-// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 14 }
-// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 25 }
+// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 13 }
+// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 24 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr65202.C b/gcc/testsuite/g++.dg/cpp1y/pr65202.C
index 602b264..7ce4895 100644
--- a/gcc/testsuite/g++.dg/cpp1y/pr65202.C
+++ b/gcc/testsuite/g++.dg/cpp1y/pr65202.C
@@ -22,5 +22,5 @@ struct bar;
 int main()
 {
     foo<ns::bar> f;
-    adl::swap(f, f)
-} // { dg-error "" }
+    adl::swap(f, f) // { dg-error "expected ';'" }
+} // { dg-error "expected '.'" "expected end of namespace" }
diff --git a/gcc/testsuite/g++.dg/missing-symbol-2.C b/gcc/testsuite/g++.dg/missing-symbol-2.C
new file mode 100644
index 0000000..4a119f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/missing-symbol-2.C
@@ -0,0 +1,58 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+extern int foo (void);
+
+void missing_open_paren (void)
+{
+  if foo ()) /* { dg-error "expected '\\(' before 'foo'" } */
+    {
+    }
+  /* { dg-begin-multiline-output "" }
+   if foo ())
+      ^~~
+      (
+     { dg-end-multiline-output "" } */
+}
+
+
+void missing_close_square (void)
+{
+  const char test [42;  /* { dg-error "22: expected ']' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   const char test [42;
+                      ^
+                      ]
+     { dg-end-multiline-output "" } */
+}
+
+int missing_semicolon (void)
+{
+  return 42 /* { dg-error "expected ';'" } */
+}
+/* { dg-begin-multiline-output "" }
+   return 42
+            ^
+            ;
+ }
+ ~           
+   { dg-end-multiline-output "" } */
+
+
+int missing_colon_in_switch (int val)
+{
+  switch (val)
+    {
+    case 42 /* { dg-error "expected ':' before 'return'" } */
+      return 42;
+    /* { dg-begin-multiline-output "" }
+     case 42
+            ^
+            :
+       return 42;
+       ~~~~~~
+       { dg-end-multiline-output "" } */
+
+    default:
+      return val;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/other/do1.C b/gcc/testsuite/g++.dg/other/do1.C
index b3a9daf..db65e7d 100644
--- a/gcc/testsuite/g++.dg/other/do1.C
+++ b/gcc/testsuite/g++.dg/other/do1.C
@@ -7,7 +7,7 @@
 
 void init ()
 {
-  do {  } while (0)
-	    obj = 0; // { dg-error "expected|not declared" }
+  do {  } while (0) // { dg-error "expected ';'" }
+	    obj = 0; // { dg-error "not declared" }
      
 }
diff --git a/gcc/testsuite/g++.dg/parse/error11.C b/gcc/testsuite/g++.dg/parse/error11.C
index d118c19..1a49d6e 100644
--- a/gcc/testsuite/g++.dg/parse/error11.C
+++ b/gcc/testsuite/g++.dg/parse/error11.C
@@ -52,7 +52,7 @@ void func(void)
   Foo[:B> k1;       // { dg-bogus "cannot begin|alternate spelling" "smart error should not be triggered here" } 
 // { dg-error "6:missing template arguments before" "template" { target *-*-* } 51 }
 // { dg-error "9:expected primary-expression before ':' token" "primary" { target *-*-* } 51 }
-// { dg-error "9:expected '\]' before ':' token" "backslash" { target *-*-* } 51 }
+// { dg-error "8:expected '\]' before ':' token" "backslash" { target *-*-* } 51 }
 // { dg-error "6:missing template arguments before" "template" { target *-*-* } 52 }
 // { dg-error "7:expected primary-expression before ':' token" "primary" { target *-*-* } 52 }
 // { dg-error "7:expected '\]' before ':' token" "backslash" { target *-*-* } 52 }
diff --git a/gcc/testsuite/g++.dg/template/error11.C b/gcc/testsuite/g++.dg/template/error11.C
index 3a469fd..1640298 100644
--- a/gcc/testsuite/g++.dg/template/error11.C
+++ b/gcc/testsuite/g++.dg/template/error11.C
@@ -1,4 +1,4 @@
 // PR c++/12132
 
 inline template <int> void foo () {} // { dg-error "<" }
-void abort (); // { dg-error ";" }
+void abort (); // { dg-error ";" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/gcc.dg/missing-symbol-2.c b/gcc/testsuite/gcc.dg/missing-symbol-2.c
new file mode 100644
index 0000000..7ee795d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/missing-symbol-2.c
@@ -0,0 +1,71 @@
+/* { dg-options "-fdiagnostics-show-caret -Wno-switch-unreachable" } */
+
+extern int foo (void);
+
+void missing_open_paren (void)
+{
+  if foo ()) /* { dg-line missing_open_paren } */
+    {
+    }
+  /* { dg-error "expected '\\(' before 'foo'" "" { target c } missing_open_paren } */
+  /* { dg-begin-multiline-output "" }
+   if foo ())
+      ^~~
+      (
+     { dg-end-multiline-output "" } */
+  /* { dg-error "expected statement before '\\)' token"  "" { target c } missing_open_paren } */
+  /* { dg-begin-multiline-output "" }
+   if foo ())
+            ^
+     { dg-end-multiline-output "" } */
+}
+
+void missing_close_square (void)
+{
+  const char test [42;  /* { dg-error "22: expected ']' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   const char test [42;
+                      ^
+                      ]
+     { dg-end-multiline-output "" } */
+}
+
+int missing_semicolon (void)
+{
+  return 42 /* { dg-error "expected ';'" } */
+}
+/* { dg-begin-multiline-output "" }
+   return 42
+            ^
+            ;
+ }
+ ~           
+   { dg-end-multiline-output "" } */
+
+
+/* We don't offer a fix-it hint for this case in C, as it could be
+   colon or ellipsis.
+   TODO: we could be smarter about error-recovery here; given the
+   return perhaps we could assume a missing colon.  */
+
+int missing_colon_in_switch (int val)
+{
+  switch (val)
+    {
+    case 42
+      return 42; /* { dg-error "expected ':' or '...' before 'return'" } */
+    /* { dg-begin-multiline-output "" }
+       return 42;
+       ^~~~~~
+       { dg-end-multiline-output "" } */
+
+    default:
+      return val;
+    }
+}
+
+/* { dg-begin-multiline-output "" }
+ int dummy;
+ ^~~
+   { dg-end-multiline-output "" } */
+int dummy;/* { dg-error "expected declaration or statement at end of input" "" { target c } } */
diff --git a/gcc/testsuite/gcc.dg/missing-symbol-3.c b/gcc/testsuite/gcc.dg/missing-symbol-3.c
new file mode 100644
index 0000000..e2d00df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/missing-symbol-3.c
@@ -0,0 +1,50 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+/* A sequence of bogus _Static_assert.
+   We can offer fix-it hints for some of these, but not all.  */
+
+void test_static_assert_1 (void)
+{
+  _Static_assert sizeof(int) >= sizeof(char); /* { dg-error "expected '\\(' before 'sizeof'" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert sizeof(int) >= sizeof(char);
+                  ^~~~~~
+                  (
+     { dg-end-multiline-output "" } */
+}
+
+void test_static_assert_2 (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char); /* { dg-error "expected ',' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char);
+                                             ^
+                                             ,
+     { dg-end-multiline-output "" } */
+}
+
+void test_static_assert_3 (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char),; /* { dg-error "expected string literal before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char),;
+                                              ^
+     { dg-end-multiline-output "" } */
+}
+
+void test_static_assert_4 (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 "" } */
+}
+
+/* The final one is correct.  */
+
+void test_static_assert_5 (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char), "msg");
+}
diff --git a/gcc/testsuite/gcc.dg/noncompile/940112-1.c b/gcc/testsuite/gcc.dg/noncompile/940112-1.c
index bb5e0f6..0a9e07d 100644
--- a/gcc/testsuite/gcc.dg/noncompile/940112-1.c
+++ b/gcc/testsuite/gcc.dg/noncompile/940112-1.c
@@ -3,5 +3,5 @@ f (int x)
 {
   double e = 1;
   e = 1;
-  return (e)
-}	/* { dg-error "parse error|syntax error|expected" } */
+  return (e) /* { dg-error "parse error|syntax error|expected" } */
+}	
diff --git a/gcc/testsuite/gcc.dg/noncompile/971104-1.c b/gcc/testsuite/gcc.dg/noncompile/971104-1.c
index 39e00c6..4a04dad 100644
--- a/gcc/testsuite/gcc.dg/noncompile/971104-1.c
+++ b/gcc/testsuite/gcc.dg/noncompile/971104-1.c
@@ -27,6 +27,6 @@ static void up(int sem){
     printf("%s had processes sleeping on it!\n",
     ({ "MUTEX     ", "BARB_SEM 1", "BARB_SEM 2", "CUST_SEM 1",
        "CUST_SEM 2", "WAIT_SEM 1", "WAIT_SEM 2", "WAIT_SEM 3",
-       "WAIT_SEM 4"}	 /* { dg-error "parse error|syntax error|expected" } */
-	[( sb.sem_num )]) ); /* { dg-error "expected" } */
+       "WAIT_SEM 4"}	 /* { dg-error "expected" } */
+	[( sb.sem_num )]) );
 }
diff --git a/gcc/testsuite/obj-c++.dg/exceptions-6.mm b/gcc/testsuite/obj-c++.dg/exceptions-6.mm
index 58882fe..6f6ba78 100644
--- a/gcc/testsuite/obj-c++.dg/exceptions-6.mm
+++ b/gcc/testsuite/obj-c++.dg/exceptions-6.mm
@@ -11,15 +11,15 @@ void test (id object)
   @throw object;   /* Ok */
   @throw;          /* { dg-error ".@throw. .rethrow. used outside of a @catch block" } */
   @throw (object); /* Ok.  */
-  @throw (id)0
-}                  /* { dg-error "expected" } */
+  @throw (id)0     /* { dg-error "expected" } */
+}
 
 void test2 (id object)
 {
   @throw object);  /* { dg-error "expected" } */
   @throw (...);    /* { dg-error "expected" } */
   @throw ();       /* { dg-error "expected" } */
-  @throw           
+  @throw           /* { dg-error "expected" } */
 }                  /* { dg-error "expected" } */
 
 void test3 (id object1, id object2)
diff --git a/gcc/testsuite/obj-c++.dg/pr48187.mm b/gcc/testsuite/obj-c++.dg/pr48187.mm
index 750710b..99677a5 100644
--- a/gcc/testsuite/obj-c++.dg/pr48187.mm
+++ b/gcc/testsuite/obj-c++.dg/pr48187.mm
@@ -1,19 +1,19 @@
 /* { dg-do compile } */
 
 @interface A
-{
+{    /* { dg-error "xpected" } */
   ]  /* { dg-error "xpected" } */
 }
 @end
 
 @interface B
-{
+{     /* { dg-error "xpected" } */
   ];  /* { dg-error "xpected" } */
 }
 @end
 
 @interface C
-{
+{     /* { dg-error "xpected" } */
   ];  /* { dg-error "xpected" } */
   int x;
 }
@@ -21,7 +21,7 @@
 
 @interface D
 {
-  (
+  (  /* { dg-error "xpected" } */
 }  /* { dg-error "xpected" } */
 @end
 
diff --git a/gcc/testsuite/objc.dg/exceptions-6.m b/gcc/testsuite/objc.dg/exceptions-6.m
index 58882fe..74be98d 100644
--- a/gcc/testsuite/objc.dg/exceptions-6.m
+++ b/gcc/testsuite/objc.dg/exceptions-6.m
@@ -11,8 +11,8 @@ void test (id object)
   @throw object;   /* Ok */
   @throw;          /* { dg-error ".@throw. .rethrow. used outside of a @catch block" } */
   @throw (object); /* Ok.  */
-  @throw (id)0
-}                  /* { dg-error "expected" } */
+  @throw (id)0     /* { dg-error "expected" } */
+}
 
 void test2 (id object)
 {
-- 
1.8.5.3

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

* [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v3)
  2017-09-26 13:56   ` [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v2) David Malcolm
@ 2017-10-05 16:08     ` David Malcolm
  2017-10-11 17:27       ` PING " David Malcolm
  2017-10-12  5:23       ` Jason Merrill
  0 siblings, 2 replies; 18+ messages in thread
From: David Malcolm @ 2017-10-05 16:08 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

Here's a slight update to this patch, since v2 was made invalid by 
r253411 ("C: underline parameters in mismatching function calls").

Both v2 and r253411 added code to c-parser.c/h to track the location_t
of the last consumed token (although I somehow managed to name the new
field in c_parser differently between the two versions...)

This version (v3) is the same as v2, but removes the copy of the
above code, updating the usage sites to use the field name from
r253411.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu
in conjunction with patch 1 of the kit:
  https://gcc.gnu.org/ml/gcc-patches/2017-09/msg01745.html

OK for trunk?

Blurb from v2 follows:

The patch improves our C/C++ frontends' handling of missing
symbols, by making c_parser_require and cp_parser_require use
"better" locations for the diagnostic, and insert fix-it hints,
under certain circumstances (see the comments in the patch for
full details).

For example, for this code with a missing semicolon:

  $ cat test.c
  int missing_semicolon (void)
  {
    return 42
  }

  trunk currently emits:

  test.c:4:1: error: expected ';' before '}' token
   }
   ^

This patch adds a fix-it hint for the missing semicolon, and puts
the error at the location of the missing semicolon, printing the
followup token as a secondary location:

  test.c:3:12: error: expected ';' before '}' token
     return 42
              ^
              ;
   }
   ~

More examples can be seen in the test cases.

This is a revised version of the patch I posted here:
  https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00135.html

Some of the changes in that patch landed in trunk in r251026
(aka 3fe34694f0990d1d649711ede0326497f8a849dc
"C/C++: show pertinent open token when missing a close token"),
so this patch contains the remaining part, updated also for the
previous patch that reunifies the cloned copies of cp_parser_error
introduced in r251026.

It also:
- fixes the typo seen by Jeff
- eliminated some unnecessary changes to c-c++-common/missing-symbol.c
- fixes some bugs

r250133, r250134, and r251026 already incorporated the suggestion from
Richard Sandiford to consolidate note-printing when the matching location
is near the primary location of the diagnostic.

This patch doesn't address Joseph's requests to tackle PR 7356 and
PR 18248, but he said that it was OK to leave these for followups.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu
in conjunction with patch 1 of the kit.

OK for trunk?

gcc/c-family/ChangeLog:
	* c-common.c (enum missing_token_insertion_kind): New enum.
	(get_missing_token_insertion_kind): New function.
	(maybe_suggest_missing_token_insertion): New function.
	* c-common.h (maybe_suggest_missing_token_insertion): New decl.

gcc/c/ChangeLog:
	* c-parser.c (c_parser_require): Add "type_is_unique" param and
	use it to guard calls to maybe_suggest_missing_token_insertion.
	(c_parser_parms_list_declarator): Override default value of new
	"type_is_unique" param to c_parser_require.
	(c_parser_asm_statement): Likewise.
	* c-parser.h (c_parser_require): Add "type_is_unique" param,
	defaulting to true.

gcc/cp/ChangeLog:
	* parser.c (get_required_cpp_ttype): New function.
	(cp_parser_error_1): Call it, using the result to call
	maybe_suggest_missing_token_insertion.

gcc/testsuite/ChangeLog:
	* c-c++-common/cilk-plus/AN/parser_errors.c: Update expected
	output to reflect changes to reported locations of missing
	symbols.
	* c-c++-common/cilk-plus/AN/parser_errors2.c: Likewise.
	* c-c++-common/cilk-plus/AN/parser_errors3.c: Likewise.
	* c-c++-common/cilk-plus/AN/pr61191.c: Likewise.
	* c-c++-common/gomp/pr63326.c: Likewise.
	* c-c++-common/missing-close-symbol.c: Likewise, also update for
	new fix-it hints.
	* c-c++-common/missing-symbol.c: Likewise, also add test coverage
	for missing colon in ternary operator.
	* g++.dg/cpp1y/digit-sep-neg.C: Likewise.
	* g++.dg/cpp1y/pr65202.C: Likewise.
	* g++.dg/missing-symbol-2.C: New test case.
	* g++.dg/other/do1.C: Update expected output to reflect
	changes to reported locations of missing symbols.
	* g++.dg/parse/error11.C: Likewise.
	* g++.dg/template/error11.C: Likewise.
	* gcc.dg/missing-symbol-2.c: New test case.
	* gcc.dg/missing-symbol-3.c: New test case.
	* gcc.dg/noncompile/940112-1.c: Update expected output to reflect
	changes to reported locations of missing symbols.
	* gcc.dg/noncompile/971104-1.c: Likewise.
	* obj-c++.dg/exceptions-6.mm: Likewise.
	* obj-c++.dg/pr48187.mm: Likewise.
	* objc.dg/exceptions-6.m: Likewise.
---
 gcc/c-family/c-common.c                            | 158 +++++++++++++++++++++
 gcc/c-family/c-common.h                            |   3 +
 gcc/c/c-parser.c                                   |  25 +++-
 gcc/c/c-parser.h                                   |   3 +-
 gcc/cp/parser.c                                    |  51 ++++++-
 .../c-c++-common/cilk-plus/AN/parser_errors.c      |   4 +-
 .../c-c++-common/cilk-plus/AN/parser_errors2.c     |   3 +-
 .../c-c++-common/cilk-plus/AN/parser_errors3.c     |   3 +-
 gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c  |   3 +-
 gcc/testsuite/c-c++-common/gomp/pr63326.c          |  22 +--
 gcc/testsuite/c-c++-common/missing-close-symbol.c  |   2 +
 gcc/testsuite/c-c++-common/missing-symbol.c        |  35 +++--
 gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C         |   4 +-
 gcc/testsuite/g++.dg/cpp1y/pr65202.C               |   4 +-
 gcc/testsuite/g++.dg/missing-symbol-2.C            |  58 ++++++++
 gcc/testsuite/g++.dg/other/do1.C                   |   4 +-
 gcc/testsuite/g++.dg/parse/error11.C               |   2 +-
 gcc/testsuite/g++.dg/template/error11.C            |   2 +-
 gcc/testsuite/gcc.dg/missing-symbol-2.c            |  71 +++++++++
 gcc/testsuite/gcc.dg/missing-symbol-3.c            |  50 +++++++
 gcc/testsuite/gcc.dg/noncompile/940112-1.c         |   4 +-
 gcc/testsuite/gcc.dg/noncompile/971104-1.c         |   4 +-
 gcc/testsuite/obj-c++.dg/exceptions-6.mm           |   6 +-
 gcc/testsuite/obj-c++.dg/pr48187.mm                |   8 +-
 gcc/testsuite/objc.dg/exceptions-6.m               |   4 +-
 25 files changed, 476 insertions(+), 57 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/missing-symbol-2.C
 create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-2.c
 create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-3.c

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index b3ec3a0..a23fa5f 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -7946,6 +7946,164 @@ c_flt_eval_method (bool maybe_c11_only_p)
     return c_ts18661_flt_eval_method ();
 }
 
+/* An enum for get_missing_token_insertion_kind for describing the best
+   place to insert a missing token, if there is one.  */
+
+enum missing_token_insertion_kind
+{
+  MTIK_IMPOSSIBLE,
+  MTIK_INSERT_BEFORE_NEXT,
+  MTIK_INSERT_AFTER_PREV
+};
+
+/* Given a missing token of TYPE, determine if it is reasonable to
+   emit a fix-it hint suggesting the insertion of the token, and,
+   if so, where the token should be inserted relative to other tokens.
+
+   It only makes sense to do this for values of TYPE that are symbols.
+
+   Some symbols should go before the next token, e.g. in:
+     if flag)
+   we want to insert the missing '(' immediately before "flag",
+   giving:
+     if (flag)
+   rather than:
+     if( flag)
+   These use MTIK_INSERT_BEFORE_NEXT.
+
+   Other symbols should go after the previous token, e.g. in:
+     if (flag
+       do_something ();
+   we want to insert the missing ')' immediately after the "flag",
+   giving:
+     if (flag)
+       do_something ();
+   rather than:
+     if (flag
+       )do_something ();
+   These use MTIK_INSERT_AFTER_PREV.  */
+
+static enum missing_token_insertion_kind
+get_missing_token_insertion_kind (enum cpp_ttype type)
+{
+  switch (type)
+    {
+      /* Insert missing "opening" brackets immediately
+	 before the next token.  */
+    case CPP_OPEN_SQUARE:
+    case CPP_OPEN_PAREN:
+      return MTIK_INSERT_BEFORE_NEXT;
+
+      /* Insert other missing symbols immediately after
+	 the previous token.  */
+    case CPP_CLOSE_PAREN:
+    case CPP_CLOSE_SQUARE:
+    case CPP_SEMICOLON:
+    case CPP_COMMA:
+    case CPP_COLON:
+      return MTIK_INSERT_AFTER_PREV;
+
+      /* Other kinds of token don't get fix-it hints.  */
+    default:
+      return MTIK_IMPOSSIBLE;
+    }
+}
+
+/* Given RICHLOC, a location for a diagnostic describing a missing token
+   of kind TOKEN_TYPE, potentially add a fix-it hint suggesting the
+   insertion of the token.
+
+   The location of the attempted fix-it hint depends on TOKEN_TYPE:
+   it will either be:
+     (a) immediately after PREV_TOKEN_LOC, or
+
+     (b) immediately before the primary location within RICHLOC (taken to
+	 be that of the token following where the token was expected).
+
+   If we manage to add a fix-it hint, then the location of the
+   fix-it hint is likely to be more useful as the primary location
+   of the diagnostic than that of the following token, so we swap
+   these locations.
+
+   For example, given this bogus code:
+       123456789012345678901234567890
+   1 | int missing_semicolon (void)
+   2 | {
+   3 |   return 42
+   4 | }
+
+   we will emit:
+
+     "expected ';' before '}'"
+
+   RICHLOC's primary location is at the closing brace, so before "swapping"
+   we would emit the error at line 4 column 1:
+
+       123456789012345678901234567890
+   3 |   return 42  |< fix-it hint emitted for this line
+     |            ; |
+   4 | }            |< "expected ';' before '}'" emitted at this line
+     | ^            |
+
+   It's more useful for the location of the diagnostic to be at the
+   fix-it hint, so we swap the locations, so the primary location
+   is at the fix-it hint, with the old primary location inserted
+   as a secondary location, giving this, with the error at line 3
+   column 12:
+
+       123456789012345678901234567890
+   3 |   return 42   |< "expected ';' before '}'" emitted at this line,
+     |            ^  |   with fix-it hint
+   4 |            ;  |
+     | }             |< secondary range emitted here
+     | ~             |.  */
+
+void
+maybe_suggest_missing_token_insertion (rich_location *richloc,
+				       enum cpp_ttype token_type,
+				       location_t prev_token_loc)
+{
+  gcc_assert (richloc);
+
+  enum missing_token_insertion_kind mtik
+    = get_missing_token_insertion_kind (token_type);
+
+  switch (mtik)
+    {
+    default:
+      gcc_unreachable ();
+      break;
+
+    case MTIK_IMPOSSIBLE:
+      return;
+
+    case MTIK_INSERT_BEFORE_NEXT:
+      /* Attempt to add the fix-it hint before the primary location
+	 of RICHLOC.  */
+      richloc->add_fixit_insert_before (cpp_type2name (token_type, 0));
+      break;
+
+    case MTIK_INSERT_AFTER_PREV:
+      /* Attempt to add the fix-it hint after PREV_TOKEN_LOC.  */
+      richloc->add_fixit_insert_after (prev_token_loc,
+				       cpp_type2name (token_type, 0));
+      break;
+    }
+
+  /* If we were successful, use the fix-it hint's location as the
+     primary location within RICHLOC, adding the old primary location
+     back as a secondary location.  */
+  if (!richloc->seen_impossible_fixit_p ())
+    {
+      fixit_hint *hint = richloc->get_last_fixit_hint ();
+      location_t hint_loc = hint->get_start_loc ();
+      location_t old_loc = richloc->get_loc ();
+
+      richloc->set_range (line_table, 0, hint_loc, true);
+      richloc->add_range (old_loc, false);
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index da6a0be..7e1877e 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1550,6 +1550,9 @@ extern int c_flt_eval_method (bool ts18661_p);
 extern void add_no_sanitize_value (tree node, unsigned int flags);
 
 extern void maybe_add_include_fixit (rich_location *, const char *);
+extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
+						   enum cpp_ttype token_type,
+						   location_t prev_token_loc);
 
 #if CHECKING_P
 namespace selftest {
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 1a5e39e..a5e3ec4 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1041,13 +1041,21 @@ get_matching_symbol (enum cpp_ttype type)
    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).  */
+   CPP_CLOSE_PAREN).
+
+   If TYPE_IS_UNIQUE is true (the default) then msgid describes exactly
+   one type (e.g. "expected %<)%>") and thus it may be reasonable to
+   attempt to generate a fix-it hint for the problem.
+   Otherwise msgid describes multiple token types (e.g.
+   "expected %<;%>, %<,%> or %<)%>"), and thus we shouldn't attempt to
+   generate a fix-it hint.  */
 
 bool
 c_parser_require (c_parser *parser,
 		  enum cpp_ttype type,
 		  const char *msgid,
-		  location_t matching_location)
+		  location_t matching_location,
+		  bool type_is_unique)
 {
   if (c_parser_next_token_is (parser, type))
     {
@@ -1059,6 +1067,13 @@ c_parser_require (c_parser *parser,
       location_t next_token_loc = c_parser_peek_token (parser)->location;
       gcc_rich_location richloc (next_token_loc);
 
+      /* Potentially supply a fix-it hint, suggesting to add the
+	 missing token immediately after the *previous* token.
+	 This may move the primary location within richloc.  */
+      if (!parser->error && type_is_unique)
+	maybe_suggest_missing_token_insertion (&richloc, type,
+					       parser->last_token_location);
+
       /* If matching_location != UNKNOWN_LOCATION, highlight it.
 	 Attempt to consolidate diagnostics by printing it as a
 	 secondary range within the main diagnostic.  */
@@ -3975,7 +3990,8 @@ c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr)
 	    return get_parm_info (false, expr);
 	}
       if (!c_parser_require (parser, CPP_COMMA,
-			     "expected %<;%>, %<,%> or %<)%>"))
+			     "expected %<;%>, %<,%> or %<)%>",
+			     UNKNOWN_LOCATION, false))
 	{
 	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
 	  return NULL;
@@ -6429,7 +6445,8 @@ c_parser_asm_statement (c_parser *parser)
       if (!c_parser_require (parser, CPP_COLON,
 			     is_goto
 			     ? G_("expected %<:%>")
-			     : G_("expected %<:%> or %<)%>")))
+			     : G_("expected %<:%> or %<)%>"),
+			     UNKNOWN_LOCATION, is_goto))
 	goto error_close_paren;
 
       /* Once past any colon, we're no longer a simple asm.  */
diff --git a/gcc/c/c-parser.h b/gcc/c/c-parser.h
index 01a7b72..21e4054 100644
--- a/gcc/c/c-parser.h
+++ b/gcc/c/c-parser.h
@@ -137,7 +137,8 @@ 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,
-			      location_t matching_location = UNKNOWN_LOCATION);
+			      location_t matching_location = UNKNOWN_LOCATION,
+			      bool type_is_unique=true);
 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,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c104612..77e82c9 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2787,6 +2787,40 @@ get_matching_symbol (required_token token_desc)
     }
 }
 
+/* Attempt to convert TOKEN_DESC from a required_token to an
+   enum cpp_ttype, returning CPP_EOF if there is no good conversion.  */
+
+static enum cpp_ttype
+get_required_cpp_ttype (required_token token_desc)
+{
+  switch (token_desc)
+    {
+    case RT_SEMICOLON:
+      return CPP_SEMICOLON;
+    case RT_OPEN_PAREN:
+      return CPP_OPEN_PAREN;
+    case RT_CLOSE_BRACE:
+      return CPP_CLOSE_BRACE;
+    case RT_OPEN_BRACE:
+      return CPP_OPEN_BRACE;
+    case RT_CLOSE_SQUARE:
+      return CPP_CLOSE_SQUARE;
+    case RT_OPEN_SQUARE:
+      return CPP_OPEN_SQUARE;
+    case RT_COMMA:
+      return CPP_COMMA;
+    case RT_COLON:
+      return CPP_COLON;
+    case RT_CLOSE_PAREN:
+      return CPP_CLOSE_PAREN;
+
+    default:
+      /* Use CPP_EOF as a "no completions possible" code.  */
+      return CPP_EOF;
+    }
+}
+
+
 /* Subroutine of cp_parser_error and cp_parser_required_error.
 
    Issue a diagnostic of the form
@@ -2798,9 +2832,12 @@ get_matching_symbol (required_token token_desc)
    This bypasses the check for tentative passing, and potentially
    adds material needed by cp_parser_required_error.
 
-   If MISSING_TOKEN_DESC is not RT_NONE, and MATCHING_LOCATION is not
-   UNKNOWN_LOCATION, then we have an unmatched symbol at
-   MATCHING_LOCATION; highlight this secondary location.  */
+   If MISSING_TOKEN_DESC is not RT_NONE, then potentially add fix-it hints
+   suggesting insertion of the missing token.
+
+   Additionally, if MATCHING_LOCATION is not UNKNOWN_LOCATION, then we
+   have an unmatched symbol at MATCHING_LOCATION; highlight this secondary
+   location.  */
 
 static void
 cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
@@ -2839,6 +2876,14 @@ cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
 
   if (missing_token_desc != RT_NONE)
     {
+      /* Potentially supply a fix-it hint, suggesting to add the
+	 missing token immediately after the *previous* token.
+	 This may move the primary location within richloc.  */
+      enum cpp_ttype ttype = get_required_cpp_ttype (missing_token_desc);
+      location_t prev_token_loc
+	= cp_lexer_previous_token (parser->lexer)->location;
+      maybe_suggest_missing_token_insertion (&richloc, ttype, prev_token_loc);
+
       /* If matching_location != UNKNOWN_LOCATION, highlight it.
 	 Attempt to consolidate diagnostics by printing it as a
 	secondary range within the main diagnostic.  */
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
index 18816e0..fd4fe54 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
@@ -7,5 +7,5 @@ int main (void)
   
   array2[:] = array2[: ;  /* { dg-error "expected ']'" } */
 
-  return 0;
-} /* { dg-error "expected ';' before" "" { target c } } */
+  return 0; /* { dg-error "expected ';' before" "" { target c } } */
+}
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
index 2bb9134..d003d7c 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
@@ -7,6 +7,7 @@ int main (void)
   
   array2[:] = array2[1:2:] ;  /* { dg-error "expected expression before" "" { target c } } */ 
   /* { dg-error  "expected primary-expression before" "" { target c++ } .-1 } */
+  /* { dg-error "expected ';' before" "" { target c } .-2 } */
 
-  return 0; /* { dg-error "expected ';' before" "" { target c }  } */
+  return 0;
 }
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
index 9270007..14256e9 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
@@ -7,6 +7,7 @@ int main (void)
   
   array2[:] = array2[1: :] ;  /* { dg-error "expected expression before" "" { target c }  } */ 
   /* { dg-error "expected primary-expression before" "" { target c++ }  .-1 } */
+  /* { dg-error "expected ';' before" "" { target c } .-2 } */
 
-  return 0; /* { dg-error "expected ';' before" "" { target c } } */
+  return 0;
 }
diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c b/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
index a9a9d66..8c32ad9 100644
--- a/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
+++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
@@ -7,4 +7,5 @@ double f(double * A, double * B)
   return __sec_reduce_add((B[0:500])(; /* { dg-error "called object" "" { target c } } */
 /* { dg-error "expected expression before ';' token" "" { target c } .-1 } */
 /* { dg-error "expected primary-expression before ';' token" "" { target c++ } .-2 } */
-} /* { dg-error "expected" "" { target c } } */
+/* { dg-error "expected" "" { target c } .-3 } */
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/pr63326.c b/gcc/testsuite/c-c++-common/gomp/pr63326.c
index e319f49..3e62723 100644
--- a/gcc/testsuite/c-c++-common/gomp/pr63326.c
+++ b/gcc/testsuite/c-c++-common/gomp/pr63326.c
@@ -156,34 +156,34 @@ f4 (int x)
   {
     do
       #pragma omp barrier			/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp flush				/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp taskwait			/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp taskyield			/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   #pragma omp parallel
   {
     do
       #pragma omp cancel parallel		/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   #pragma omp parallel
   {
     do
       #pragma omp cancellation point parallel	/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   #pragma omp for ordered(1)
   for (i = 0; i < 16; i++)
@@ -191,28 +191,28 @@ f4 (int x)
       {
 	do
 	  #pragma omp ordered depend(source)	/* { dg-error "may only be used in compound statements" } */
-	while (0);
+	while (0); /* { dg-error "before" "" { target c++ } } */
       } /* { dg-error "before" "" { target c++ } } */
       {
 	do
 	  #pragma omp ordered depend(sink: i-1)	/* { dg-error "may only be used in compound statements" } */
-	while (0);
+	while (0); /* { dg-error "before" "" { target c++ } } */
       } /* { dg-error "before" "" { target c++ } } */
     }
   {
     do
       #pragma omp target enter data map(to:i)	/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp target update to(i)		/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
   {
     do
       #pragma omp target exit data map(from:i)	/* { dg-error "may only be used in compound statements" } */
-    while (0);
+    while (0); /* { dg-error "before" "" { target c++ } } */
   } /* { dg-error "before" "" { target c++ } } */
 }
 
diff --git a/gcc/testsuite/c-c++-common/missing-close-symbol.c b/gcc/testsuite/c-c++-common/missing-close-symbol.c
index 85b96f28..abeb837 100644
--- a/gcc/testsuite/c-c++-common/missing-close-symbol.c
+++ b/gcc/testsuite/c-c++-common/missing-close-symbol.c
@@ -12,6 +12,7 @@ void test_static_assert_same_line (void)
   /* { dg-begin-multiline-output "" }
    _Static_assert(sizeof(int) >= sizeof(char), "msg";
                  ~                                  ^
+                                                    )
      { dg-end-multiline-output "" } */
 }
 
@@ -25,6 +26,7 @@ void test_static_assert_different_line (void)
   /* { dg-begin-multiline-output "" }
     "msg";
          ^
+         )
      { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
    _Static_assert(sizeof(int) >= sizeof(char),
diff --git a/gcc/testsuite/c-c++-common/missing-symbol.c b/gcc/testsuite/c-c++-common/missing-symbol.c
index 33a501b..326b9fa 100644
--- a/gcc/testsuite/c-c++-common/missing-symbol.c
+++ b/gcc/testsuite/c-c++-common/missing-symbol.c
@@ -5,15 +5,14 @@ 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 "" } */
+  switch (i /* { dg-error "12: expected '\\)' before '.' token" } */
+    {
   /* { dg-begin-multiline-output "" }
    switch (i
-          ^
+          ~ ^
+            )
+     {
+     ~       
      { dg-end-multiline-output "" } */
 
     case 0:
@@ -30,21 +29,33 @@ int missing_close_paren_in_switch (int i)
 void missing_close_paren_in_if (void)
 {
   if (foo () /* { dg-line start_of_if } */
-      && bar () 
-    { /* { dg-error "5: expected '\\)' before '.' token" } */
+      && bar () /* { dg-error "16: expected '\\)' before '.' token" } */
+    {
       /* { dg-begin-multiline-output "" }
+       && bar ()
+                ^
+                )
      {
-     ^
+     ~           
          { 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-end-multiline-output "" } */
     }
-
 } /* { dg-error "1: expected" } */
   /* { dg-begin-multiline-output "" }
  }
  ^
      { dg-end-multiline-output "" } */
+
+int missing_colon_in_ternary (int flag)
+{
+  return flag ? 42 0; /* { dg-error "expected ':' before numeric constant" } */
+  /* { dg-begin-multiline-output "" }
+   return flag ? 42 0;
+                   ^~
+                   :
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C b/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
index 833fab7..727e74e 100644
--- a/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
+++ b/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
@@ -26,5 +26,5 @@ main()
 }
 
 // { dg-error "exponent has no digits" "exponent has no digits" { target *-*-* } 21 }
-// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 14 }
-// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 25 }
+// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 13 }
+// { dg-error "expected ';' before" "expected ';' before" { target *-*-* } 24 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr65202.C b/gcc/testsuite/g++.dg/cpp1y/pr65202.C
index 602b264..7ce4895 100644
--- a/gcc/testsuite/g++.dg/cpp1y/pr65202.C
+++ b/gcc/testsuite/g++.dg/cpp1y/pr65202.C
@@ -22,5 +22,5 @@ struct bar;
 int main()
 {
     foo<ns::bar> f;
-    adl::swap(f, f)
-} // { dg-error "" }
+    adl::swap(f, f) // { dg-error "expected ';'" }
+} // { dg-error "expected '.'" "expected end of namespace" }
diff --git a/gcc/testsuite/g++.dg/missing-symbol-2.C b/gcc/testsuite/g++.dg/missing-symbol-2.C
new file mode 100644
index 0000000..4a119f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/missing-symbol-2.C
@@ -0,0 +1,58 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+extern int foo (void);
+
+void missing_open_paren (void)
+{
+  if foo ()) /* { dg-error "expected '\\(' before 'foo'" } */
+    {
+    }
+  /* { dg-begin-multiline-output "" }
+   if foo ())
+      ^~~
+      (
+     { dg-end-multiline-output "" } */
+}
+
+
+void missing_close_square (void)
+{
+  const char test [42;  /* { dg-error "22: expected ']' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   const char test [42;
+                      ^
+                      ]
+     { dg-end-multiline-output "" } */
+}
+
+int missing_semicolon (void)
+{
+  return 42 /* { dg-error "expected ';'" } */
+}
+/* { dg-begin-multiline-output "" }
+   return 42
+            ^
+            ;
+ }
+ ~           
+   { dg-end-multiline-output "" } */
+
+
+int missing_colon_in_switch (int val)
+{
+  switch (val)
+    {
+    case 42 /* { dg-error "expected ':' before 'return'" } */
+      return 42;
+    /* { dg-begin-multiline-output "" }
+     case 42
+            ^
+            :
+       return 42;
+       ~~~~~~
+       { dg-end-multiline-output "" } */
+
+    default:
+      return val;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/other/do1.C b/gcc/testsuite/g++.dg/other/do1.C
index b3a9daf..db65e7d 100644
--- a/gcc/testsuite/g++.dg/other/do1.C
+++ b/gcc/testsuite/g++.dg/other/do1.C
@@ -7,7 +7,7 @@
 
 void init ()
 {
-  do {  } while (0)
-	    obj = 0; // { dg-error "expected|not declared" }
+  do {  } while (0) // { dg-error "expected ';'" }
+	    obj = 0; // { dg-error "not declared" }
      
 }
diff --git a/gcc/testsuite/g++.dg/parse/error11.C b/gcc/testsuite/g++.dg/parse/error11.C
index d118c19..1a49d6e 100644
--- a/gcc/testsuite/g++.dg/parse/error11.C
+++ b/gcc/testsuite/g++.dg/parse/error11.C
@@ -52,7 +52,7 @@ void func(void)
   Foo[:B> k1;       // { dg-bogus "cannot begin|alternate spelling" "smart error should not be triggered here" } 
 // { dg-error "6:missing template arguments before" "template" { target *-*-* } 51 }
 // { dg-error "9:expected primary-expression before ':' token" "primary" { target *-*-* } 51 }
-// { dg-error "9:expected '\]' before ':' token" "backslash" { target *-*-* } 51 }
+// { dg-error "8:expected '\]' before ':' token" "backslash" { target *-*-* } 51 }
 // { dg-error "6:missing template arguments before" "template" { target *-*-* } 52 }
 // { dg-error "7:expected primary-expression before ':' token" "primary" { target *-*-* } 52 }
 // { dg-error "7:expected '\]' before ':' token" "backslash" { target *-*-* } 52 }
diff --git a/gcc/testsuite/g++.dg/template/error11.C b/gcc/testsuite/g++.dg/template/error11.C
index 3a469fd..1640298 100644
--- a/gcc/testsuite/g++.dg/template/error11.C
+++ b/gcc/testsuite/g++.dg/template/error11.C
@@ -1,4 +1,4 @@
 // PR c++/12132
 
 inline template <int> void foo () {} // { dg-error "<" }
-void abort (); // { dg-error ";" }
+void abort (); // { dg-error ";" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/gcc.dg/missing-symbol-2.c b/gcc/testsuite/gcc.dg/missing-symbol-2.c
new file mode 100644
index 0000000..7ee795d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/missing-symbol-2.c
@@ -0,0 +1,71 @@
+/* { dg-options "-fdiagnostics-show-caret -Wno-switch-unreachable" } */
+
+extern int foo (void);
+
+void missing_open_paren (void)
+{
+  if foo ()) /* { dg-line missing_open_paren } */
+    {
+    }
+  /* { dg-error "expected '\\(' before 'foo'" "" { target c } missing_open_paren } */
+  /* { dg-begin-multiline-output "" }
+   if foo ())
+      ^~~
+      (
+     { dg-end-multiline-output "" } */
+  /* { dg-error "expected statement before '\\)' token"  "" { target c } missing_open_paren } */
+  /* { dg-begin-multiline-output "" }
+   if foo ())
+            ^
+     { dg-end-multiline-output "" } */
+}
+
+void missing_close_square (void)
+{
+  const char test [42;  /* { dg-error "22: expected ']' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   const char test [42;
+                      ^
+                      ]
+     { dg-end-multiline-output "" } */
+}
+
+int missing_semicolon (void)
+{
+  return 42 /* { dg-error "expected ';'" } */
+}
+/* { dg-begin-multiline-output "" }
+   return 42
+            ^
+            ;
+ }
+ ~           
+   { dg-end-multiline-output "" } */
+
+
+/* We don't offer a fix-it hint for this case in C, as it could be
+   colon or ellipsis.
+   TODO: we could be smarter about error-recovery here; given the
+   return perhaps we could assume a missing colon.  */
+
+int missing_colon_in_switch (int val)
+{
+  switch (val)
+    {
+    case 42
+      return 42; /* { dg-error "expected ':' or '...' before 'return'" } */
+    /* { dg-begin-multiline-output "" }
+       return 42;
+       ^~~~~~
+       { dg-end-multiline-output "" } */
+
+    default:
+      return val;
+    }
+}
+
+/* { dg-begin-multiline-output "" }
+ int dummy;
+ ^~~
+   { dg-end-multiline-output "" } */
+int dummy;/* { dg-error "expected declaration or statement at end of input" "" { target c } } */
diff --git a/gcc/testsuite/gcc.dg/missing-symbol-3.c b/gcc/testsuite/gcc.dg/missing-symbol-3.c
new file mode 100644
index 0000000..e2d00df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/missing-symbol-3.c
@@ -0,0 +1,50 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+/* A sequence of bogus _Static_assert.
+   We can offer fix-it hints for some of these, but not all.  */
+
+void test_static_assert_1 (void)
+{
+  _Static_assert sizeof(int) >= sizeof(char); /* { dg-error "expected '\\(' before 'sizeof'" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert sizeof(int) >= sizeof(char);
+                  ^~~~~~
+                  (
+     { dg-end-multiline-output "" } */
+}
+
+void test_static_assert_2 (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char); /* { dg-error "expected ',' before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char);
+                                             ^
+                                             ,
+     { dg-end-multiline-output "" } */
+}
+
+void test_static_assert_3 (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char),; /* { dg-error "expected string literal before ';' token" } */
+  /* { dg-begin-multiline-output "" }
+   _Static_assert(sizeof(int) >= sizeof(char),;
+                                              ^
+     { dg-end-multiline-output "" } */
+}
+
+void test_static_assert_4 (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 "" } */
+}
+
+/* The final one is correct.  */
+
+void test_static_assert_5 (void)
+{
+  _Static_assert(sizeof(int) >= sizeof(char), "msg");
+}
diff --git a/gcc/testsuite/gcc.dg/noncompile/940112-1.c b/gcc/testsuite/gcc.dg/noncompile/940112-1.c
index bb5e0f6..0a9e07d 100644
--- a/gcc/testsuite/gcc.dg/noncompile/940112-1.c
+++ b/gcc/testsuite/gcc.dg/noncompile/940112-1.c
@@ -3,5 +3,5 @@ f (int x)
 {
   double e = 1;
   e = 1;
-  return (e)
-}	/* { dg-error "parse error|syntax error|expected" } */
+  return (e) /* { dg-error "parse error|syntax error|expected" } */
+}	
diff --git a/gcc/testsuite/gcc.dg/noncompile/971104-1.c b/gcc/testsuite/gcc.dg/noncompile/971104-1.c
index 39e00c6..4a04dad 100644
--- a/gcc/testsuite/gcc.dg/noncompile/971104-1.c
+++ b/gcc/testsuite/gcc.dg/noncompile/971104-1.c
@@ -27,6 +27,6 @@ static void up(int sem){
     printf("%s had processes sleeping on it!\n",
     ({ "MUTEX     ", "BARB_SEM 1", "BARB_SEM 2", "CUST_SEM 1",
        "CUST_SEM 2", "WAIT_SEM 1", "WAIT_SEM 2", "WAIT_SEM 3",
-       "WAIT_SEM 4"}	 /* { dg-error "parse error|syntax error|expected" } */
-	[( sb.sem_num )]) ); /* { dg-error "expected" } */
+       "WAIT_SEM 4"}	 /* { dg-error "expected" } */
+	[( sb.sem_num )]) );
 }
diff --git a/gcc/testsuite/obj-c++.dg/exceptions-6.mm b/gcc/testsuite/obj-c++.dg/exceptions-6.mm
index 58882fe..6f6ba78 100644
--- a/gcc/testsuite/obj-c++.dg/exceptions-6.mm
+++ b/gcc/testsuite/obj-c++.dg/exceptions-6.mm
@@ -11,15 +11,15 @@ void test (id object)
   @throw object;   /* Ok */
   @throw;          /* { dg-error ".@throw. .rethrow. used outside of a @catch block" } */
   @throw (object); /* Ok.  */
-  @throw (id)0
-}                  /* { dg-error "expected" } */
+  @throw (id)0     /* { dg-error "expected" } */
+}
 
 void test2 (id object)
 {
   @throw object);  /* { dg-error "expected" } */
   @throw (...);    /* { dg-error "expected" } */
   @throw ();       /* { dg-error "expected" } */
-  @throw           
+  @throw           /* { dg-error "expected" } */
 }                  /* { dg-error "expected" } */
 
 void test3 (id object1, id object2)
diff --git a/gcc/testsuite/obj-c++.dg/pr48187.mm b/gcc/testsuite/obj-c++.dg/pr48187.mm
index 750710b..99677a5 100644
--- a/gcc/testsuite/obj-c++.dg/pr48187.mm
+++ b/gcc/testsuite/obj-c++.dg/pr48187.mm
@@ -1,19 +1,19 @@
 /* { dg-do compile } */
 
 @interface A
-{
+{    /* { dg-error "xpected" } */
   ]  /* { dg-error "xpected" } */
 }
 @end
 
 @interface B
-{
+{     /* { dg-error "xpected" } */
   ];  /* { dg-error "xpected" } */
 }
 @end
 
 @interface C
-{
+{     /* { dg-error "xpected" } */
   ];  /* { dg-error "xpected" } */
   int x;
 }
@@ -21,7 +21,7 @@
 
 @interface D
 {
-  (
+  (  /* { dg-error "xpected" } */
 }  /* { dg-error "xpected" } */
 @end
 
diff --git a/gcc/testsuite/objc.dg/exceptions-6.m b/gcc/testsuite/objc.dg/exceptions-6.m
index 58882fe..74be98d 100644
--- a/gcc/testsuite/objc.dg/exceptions-6.m
+++ b/gcc/testsuite/objc.dg/exceptions-6.m
@@ -11,8 +11,8 @@ void test (id object)
   @throw object;   /* Ok */
   @throw;          /* { dg-error ".@throw. .rethrow. used outside of a @catch block" } */
   @throw (object); /* Ok.  */
-  @throw (id)0
-}                  /* { dg-error "expected" } */
+  @throw (id)0     /* { dg-error "expected" } */
+}
 
 void test2 (id object)
 {
-- 
1.8.5.3

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

* PING: Re: [PATCH 1/2] C++: avoid partial duplicate implementation of cp_parser_error
  2017-09-26 13:56   ` [PATCH 1/2] C++: avoid partial duplicate implementation of cp_parser_error David Malcolm
@ 2017-10-11 17:26     ` David Malcolm
  2017-10-11 21:21     ` Jason Merrill
  1 sibling, 0 replies; 18+ messages in thread
From: David Malcolm @ 2017-10-11 17:26 UTC (permalink / raw)
  To: Jeff Law, gcc-patches

Ping

On Tue, 2017-09-26 at 09:56 -0400, David Malcolm wrote:
> In r251026 (aka 3fe34694f0990d1d649711ede0326497f8a849dc,
> "C/C++: show pertinent open token when missing a close token")
> I copied part of cp_parser_error into cp_parser_required_error,
> leading to duplication of code.
> 
> This patch eliminates this duplication by merging the two copies of
> the
> code into a new cp_parser_error_1 subroutine.
> 
> Doing so removes an indentation level, making the patch appear to
> have
> more churn than it really does.
> 
> The patch also undoes the change to g++.dg/parse/pragma2.C, as the
> old behavior is restored.
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> 
> OK for trunk?
> 
> gcc/cp/ChangeLog:
> 	* parser.c (get_matching_symbol): Move to before...
> 	(cp_parser_error): Split out into...
> 	(cp_parser_error_1): ...this new function, merging in content
> 	from...
> 	(cp_parser_required_error): ...here.  Eliminate partial
> duplicate
> 	of body of cp_parser_error in favor of a call to the new
> 	cp_parser_error_1 helper function.
> 
> gcc/testsuite/ChangeLog:
> 	* g++.dg/parse/pragma2.C: Update to reflect reinstatement of
> the
> 	"#pragma is not allowed here" error.
> ---
>  gcc/cp/parser.c                      | 169 ++++++++++++++++++++-----
> ----------
>  gcc/testsuite/g++.dg/parse/pragma2.C |   4 +-
>  2 files changed, 97 insertions(+), 76 deletions(-)
> 
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 25b91df..56d9442 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -2767,53 +2767,116 @@ cp_lexer_peek_conflict_marker (cp_lexer
> *lexer, enum cpp_ttype tok1_kind,
>    return true;
>  }
>  
> -/* If not parsing tentatively, issue a diagnostic of the form
> +/* 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 "(";
> +    }
> +}
> +
> +/* Subroutine of cp_parser_error and cp_parser_required_error.
> +
> +   Issue a diagnostic of the form
>        FILE:LINE: MESSAGE before TOKEN
>     where TOKEN is the next token in the input stream.  MESSAGE
>     (specified by the caller) is usually of the form "expected
> -   OTHER-TOKEN".  */
> +   OTHER-TOKEN".
> +
> +   This bypasses the check for tentative passing, and potentially
> +   adds material needed by cp_parser_required_error.
> +
> +   If MISSING_TOKEN_DESC is not RT_NONE, and MATCHING_LOCATION is
> not
> +   UNKNOWN_LOCATION, then we have an unmatched symbol at
> +   MATCHING_LOCATION; highlight this secondary location.  */
>  
>  static void
> -cp_parser_error (cp_parser* parser, const char* gmsgid)
> +cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
> +		   required_token missing_token_desc,
> +		   location_t matching_location)
>  {
> -  if (!cp_parser_simulate_error (parser))
> +  cp_token *token = cp_lexer_peek_token (parser->lexer);
> +  /* This diagnostic makes more sense if it is tagged to the line
> +     of the token we just peeked at.  */
> +  cp_lexer_set_source_position_from_token (token);
> +
> +  if (token->type == CPP_PRAGMA)
>      {
> -      cp_token *token = cp_lexer_peek_token (parser->lexer);
> -      /* This diagnostic makes more sense if it is tagged to the
> line
> -	 of the token we just peeked at.  */
> -      cp_lexer_set_source_position_from_token (token);
> +      error_at (token->location,
> +		"%<#pragma%> is not allowed here");
> +      cp_parser_skip_to_pragma_eol (parser, token);
> +      return;
> +    }
>  
> -      if (token->type == CPP_PRAGMA)
> +  /* If this is actually a conflict marker, report it as such.  */
> +  if (token->type == CPP_LSHIFT
> +      || token->type == CPP_RSHIFT
> +      || token->type == CPP_EQ_EQ)
> +    {
> +      location_t loc;
> +      if (cp_lexer_peek_conflict_marker (parser->lexer, token->type, 
> &loc))
>  	{
> -	  error_at (token->location,
> -		    "%<#pragma%> is not allowed here");
> -	  cp_parser_skip_to_pragma_eol (parser, token);
> +	  error_at (loc, "version control conflict marker in file");
>  	  return;
>  	}
> +    }
>  
> -      /* If this is actually a conflict marker, report it as
> such.  */
> -      if (token->type == CPP_LSHIFT
> -	  || token->type == CPP_RSHIFT
> -	  || token->type == CPP_EQ_EQ)
> -	{
> -	  location_t loc;
> -	  if (cp_lexer_peek_conflict_marker (parser->lexer, token-
> >type, &loc))
> -	    {
> -	      error_at (loc, "version control conflict marker in
> file");
> -	      return;
> -	    }
> -	}
> +  gcc_rich_location richloc (input_location);
> +
> +  bool added_matching_location = false;
> +
> +  if (missing_token_desc != RT_NONE)
> +    {
> +      /* If matching_location != UNKNOWN_LOCATION, highlight it.
> +	 Attempt to consolidate diagnostics by printing it as a
> +	secondary range within the main diagnostic.  */
> +      if (matching_location != UNKNOWN_LOCATION)
> +	added_matching_location
> +	  = richloc.add_location_if_nearby (matching_location);
> +    }
> +
> +  /* Actually emit the error.  */
> +  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, &richloc);
>  
> -      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, &richloc);
> +  if (missing_token_desc != RT_NONE)
> +    {
> +      /* 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 (missing_token_desc));
>      }
>  }
>  
> +/* If not parsing tentatively, issue a diagnostic of the form
> +      FILE:LINE: MESSAGE before TOKEN
> +   where TOKEN is the next token in the input stream.  MESSAGE
> +   (specified by the caller) is usually of the form "expected
> +   OTHER-TOKEN".  */
> +
> +static void
> +cp_parser_error (cp_parser* parser, const char* gmsgid)
> +{
> +  if (!cp_parser_simulate_error (parser))
> +    cp_parser_error_1 (parser, gmsgid, RT_NONE, UNKNOWN_LOCATION);
> +}
> +
>  /* Issue an error about name-lookup failing.  NAME is the
>     IDENTIFIER_NODE DECL is the result of
>     the lookup (as returned from cp_parser_lookup_name).  DESIRED is
> @@ -27960,24 +28023,6 @@ 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
> @@ -28155,31 +28200,7 @@ cp_parser_required_error (cp_parser *parser,
>      }
>  
>    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));
> -    }
> +    cp_parser_error_1 (parser, gmsgid, token_desc,
> matching_location);
>  }
>  
>  
> diff --git a/gcc/testsuite/g++.dg/parse/pragma2.C
> b/gcc/testsuite/g++.dg/parse/pragma2.C
> index 3dc5fc1..c5616ff 100644
> --- a/gcc/testsuite/g++.dg/parse/pragma2.C
> +++ b/gcc/testsuite/g++.dg/parse/pragma2.C
> @@ -4,5 +4,5 @@
>  // does not.
>  int f(int x,
>  #pragma interface  // { dg-error "not allowed here" }
> -      // { dg-bogus "expected identifier" "" { xfail *-*-* } .-1 }
> -      int y);
> +      // The parser gets confused and issues an error on the next
> line.
> +      int y); // { dg-bogus "" "" { xfail *-*-* } } 

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

* PING Re: [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v3)
  2017-10-05 16:08     ` [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v3) David Malcolm
@ 2017-10-11 17:27       ` David Malcolm
  2017-10-12  5:23       ` Jason Merrill
  1 sibling, 0 replies; 18+ messages in thread
From: David Malcolm @ 2017-10-11 17:27 UTC (permalink / raw)
  To: gcc-patches

Ping

On Thu, 2017-10-05 at 12:08 -0400, David Malcolm wrote:
> Here's a slight update to this patch, since v2 was made invalid by 
> r253411 ("C: underline parameters in mismatching function calls").
> 
> Both v2 and r253411 added code to c-parser.c/h to track the
> location_t
> of the last consumed token (although I somehow managed to name the
> new
> field in c_parser differently between the two versions...)
> 
> This version (v3) is the same as v2, but removes the copy of the
> above code, updating the usage sites to use the field name from
> r253411.
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu
> in conjunction with patch 1 of the kit:
>   https://gcc.gnu.org/ml/gcc-patches/2017-09/msg01745.html
> 
> OK for trunk?
> 
> Blurb from v2 follows:
> 
> The patch improves our C/C++ frontends' handling of missing
> symbols, by making c_parser_require and cp_parser_require use
> "better" locations for the diagnostic, and insert fix-it hints,
> under certain circumstances (see the comments in the patch for
> full details).
> 
> For example, for this code with a missing semicolon:
> 
>   $ cat test.c
>   int missing_semicolon (void)
>   {
>     return 42
>   }
> 
>   trunk currently emits:
> 
>   test.c:4:1: error: expected ';' before '}' token
>    }
>    ^
> 
> This patch adds a fix-it hint for the missing semicolon, and puts
> the error at the location of the missing semicolon, printing the
> followup token as a secondary location:
> 
>   test.c:3:12: error: expected ';' before '}' token
>      return 42
>               ^
>               ;
>    }
>    ~
> 
> More examples can be seen in the test cases.
> 
> This is a revised version of the patch I posted here:
>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00135.html
> 
> Some of the changes in that patch landed in trunk in r251026
> (aka 3fe34694f0990d1d649711ede0326497f8a849dc
> "C/C++: show pertinent open token when missing a close token"),
> so this patch contains the remaining part, updated also for the
> previous patch that reunifies the cloned copies of cp_parser_error
> introduced in r251026.
> 
> It also:
> - fixes the typo seen by Jeff
> - eliminated some unnecessary changes to c-c++-common/missing-
> symbol.c
> - fixes some bugs
> 
> r250133, r250134, and r251026 already incorporated the suggestion
> from
> Richard Sandiford to consolidate note-printing when the matching
> location
> is near the primary location of the diagnostic.
> 
> This patch doesn't address Joseph's requests to tackle PR 7356 and
> PR 18248, but he said that it was OK to leave these for followups.
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu
> in conjunction with patch 1 of the kit.
> 
> OK for trunk?
> 
> gcc/c-family/ChangeLog:
> 	* c-common.c (enum missing_token_insertion_kind): New enum.
> 	(get_missing_token_insertion_kind): New function.
> 	(maybe_suggest_missing_token_insertion): New function.
> 	* c-common.h (maybe_suggest_missing_token_insertion): New decl.
> 
> gcc/c/ChangeLog:
> 	* c-parser.c (c_parser_require): Add "type_is_unique" param and
> 	use it to guard calls to maybe_suggest_missing_token_insertion.
> 	(c_parser_parms_list_declarator): Override default value of new
> 	"type_is_unique" param to c_parser_require.
> 	(c_parser_asm_statement): Likewise.
> 	* c-parser.h (c_parser_require): Add "type_is_unique" param,
> 	defaulting to true.
> 
> gcc/cp/ChangeLog:
> 	* parser.c (get_required_cpp_ttype): New function.
> 	(cp_parser_error_1): Call it, using the result to call
> 	maybe_suggest_missing_token_insertion.
> 
> gcc/testsuite/ChangeLog:
> 	* c-c++-common/cilk-plus/AN/parser_errors.c: Update expected
> 	output to reflect changes to reported locations of missing
> 	symbols.
> 	* c-c++-common/cilk-plus/AN/parser_errors2.c: Likewise.
> 	* c-c++-common/cilk-plus/AN/parser_errors3.c: Likewise.
> 	* c-c++-common/cilk-plus/AN/pr61191.c: Likewise.
> 	* c-c++-common/gomp/pr63326.c: Likewise.
> 	* c-c++-common/missing-close-symbol.c: Likewise, also update
> for
> 	new fix-it hints.
> 	* c-c++-common/missing-symbol.c: Likewise, also add test
> coverage
> 	for missing colon in ternary operator.
> 	* g++.dg/cpp1y/digit-sep-neg.C: Likewise.
> 	* g++.dg/cpp1y/pr65202.C: Likewise.
> 	* g++.dg/missing-symbol-2.C: New test case.
> 	* g++.dg/other/do1.C: Update expected output to reflect
> 	changes to reported locations of missing symbols.
> 	* g++.dg/parse/error11.C: Likewise.
> 	* g++.dg/template/error11.C: Likewise.
> 	* gcc.dg/missing-symbol-2.c: New test case.
> 	* gcc.dg/missing-symbol-3.c: New test case.
> 	* gcc.dg/noncompile/940112-1.c: Update expected output to
> reflect
> 	changes to reported locations of missing symbols.
> 	* gcc.dg/noncompile/971104-1.c: Likewise.
> 	* obj-c++.dg/exceptions-6.mm: Likewise.
> 	* obj-c++.dg/pr48187.mm: Likewise.
> 	* objc.dg/exceptions-6.m: Likewise.
> ---
>  gcc/c-family/c-common.c                            | 158
> +++++++++++++++++++++
>  gcc/c-family/c-common.h                            |   3 +
>  gcc/c/c-parser.c                                   |  25 +++-
>  gcc/c/c-parser.h                                   |   3 +-
>  gcc/cp/parser.c                                    |  51 ++++++-
>  .../c-c++-common/cilk-plus/AN/parser_errors.c      |   4 +-
>  .../c-c++-common/cilk-plus/AN/parser_errors2.c     |   3 +-
>  .../c-c++-common/cilk-plus/AN/parser_errors3.c     |   3 +-
>  gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c  |   3 +-
>  gcc/testsuite/c-c++-common/gomp/pr63326.c          |  22 +--
>  gcc/testsuite/c-c++-common/missing-close-symbol.c  |   2 +
>  gcc/testsuite/c-c++-common/missing-symbol.c        |  35 +++--
>  gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C         |   4 +-
>  gcc/testsuite/g++.dg/cpp1y/pr65202.C               |   4 +-
>  gcc/testsuite/g++.dg/missing-symbol-2.C            |  58 ++++++++
>  gcc/testsuite/g++.dg/other/do1.C                   |   4 +-
>  gcc/testsuite/g++.dg/parse/error11.C               |   2 +-
>  gcc/testsuite/g++.dg/template/error11.C            |   2 +-
>  gcc/testsuite/gcc.dg/missing-symbol-2.c            |  71 +++++++++
>  gcc/testsuite/gcc.dg/missing-symbol-3.c            |  50 +++++++
>  gcc/testsuite/gcc.dg/noncompile/940112-1.c         |   4 +-
>  gcc/testsuite/gcc.dg/noncompile/971104-1.c         |   4 +-
>  gcc/testsuite/obj-c++.dg/exceptions-6.mm           |   6 +-
>  gcc/testsuite/obj-c++.dg/pr48187.mm                |   8 +-
>  gcc/testsuite/objc.dg/exceptions-6.m               |   4 +-
>  25 files changed, 476 insertions(+), 57 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/missing-symbol-2.C
>  create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/missing-symbol-3.c
> 
> diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
> index b3ec3a0..a23fa5f 100644
> --- a/gcc/c-family/c-common.c
> +++ b/gcc/c-family/c-common.c
> @@ -7946,6 +7946,164 @@ c_flt_eval_method (bool maybe_c11_only_p)
>      return c_ts18661_flt_eval_method ();
>  }
>  
> +/* An enum for get_missing_token_insertion_kind for describing the
> best
> +   place to insert a missing token, if there is one.  */
> +
> +enum missing_token_insertion_kind
> +{
> +  MTIK_IMPOSSIBLE,
> +  MTIK_INSERT_BEFORE_NEXT,
> +  MTIK_INSERT_AFTER_PREV
> +};
> +
> +/* Given a missing token of TYPE, determine if it is reasonable to
> +   emit a fix-it hint suggesting the insertion of the token, and,
> +   if so, where the token should be inserted relative to other
> tokens.
> +
> +   It only makes sense to do this for values of TYPE that are
> symbols.
> +
> +   Some symbols should go before the next token, e.g. in:
> +     if flag)
> +   we want to insert the missing '(' immediately before "flag",
> +   giving:
> +     if (flag)
> +   rather than:
> +     if( flag)
> +   These use MTIK_INSERT_BEFORE_NEXT.
> +
> +   Other symbols should go after the previous token, e.g. in:
> +     if (flag
> +       do_something ();
> +   we want to insert the missing ')' immediately after the "flag",
> +   giving:
> +     if (flag)
> +       do_something ();
> +   rather than:
> +     if (flag
> +       )do_something ();
> +   These use MTIK_INSERT_AFTER_PREV.  */
> +
> +static enum missing_token_insertion_kind
> +get_missing_token_insertion_kind (enum cpp_ttype type)
> +{
> +  switch (type)
> +    {
> +      /* Insert missing "opening" brackets immediately
> +	 before the next token.  */
> +    case CPP_OPEN_SQUARE:
> +    case CPP_OPEN_PAREN:
> +      return MTIK_INSERT_BEFORE_NEXT;
> +
> +      /* Insert other missing symbols immediately after
> +	 the previous token.  */
> +    case CPP_CLOSE_PAREN:
> +    case CPP_CLOSE_SQUARE:
> +    case CPP_SEMICOLON:
> +    case CPP_COMMA:
> +    case CPP_COLON:
> +      return MTIK_INSERT_AFTER_PREV;
> +
> +      /* Other kinds of token don't get fix-it hints.  */
> +    default:
> +      return MTIK_IMPOSSIBLE;
> +    }
> +}
> +
> +/* Given RICHLOC, a location for a diagnostic describing a missing
> token
> +   of kind TOKEN_TYPE, potentially add a fix-it hint suggesting the
> +   insertion of the token.
> +
> +   The location of the attempted fix-it hint depends on TOKEN_TYPE:
> +   it will either be:
> +     (a) immediately after PREV_TOKEN_LOC, or
> +
> +     (b) immediately before the primary location within RICHLOC
> (taken to
> +	 be that of the token following where the token was
> expected).
> +
> +   If we manage to add a fix-it hint, then the location of the
> +   fix-it hint is likely to be more useful as the primary location
> +   of the diagnostic than that of the following token, so we swap
> +   these locations.
> +
> +   For example, given this bogus code:
> +       123456789012345678901234567890
> +   1 | int missing_semicolon (void)
> +   2 | {
> +   3 |   return 42
> +   4 | }
> +
> +   we will emit:
> +
> +     "expected ';' before '}'"
> +
> +   RICHLOC's primary location is at the closing brace, so before
> "swapping"
> +   we would emit the error at line 4 column 1:
> +
> +       123456789012345678901234567890
> +   3 |   return 42  |< fix-it hint emitted for this line
> +     |            ; |
> +   4 | }            |< "expected ';' before '}'" emitted at this
> line
> +     | ^            |
> +
> +   It's more useful for the location of the diagnostic to be at the
> +   fix-it hint, so we swap the locations, so the primary location
> +   is at the fix-it hint, with the old primary location inserted
> +   as a secondary location, giving this, with the error at line 3
> +   column 12:
> +
> +       123456789012345678901234567890
> +   3 |   return 42   |< "expected ';' before '}'" emitted at this
> line,
> +     |            ^  |   with fix-it hint
> +   4 |            ;  |
> +     | }             |< secondary range emitted here
> +     | ~             |.  */
> +
> +void
> +maybe_suggest_missing_token_insertion (rich_location *richloc,
> +				       enum cpp_ttype token_type,
> +				       location_t prev_token_loc)
> +{
> +  gcc_assert (richloc);
> +
> +  enum missing_token_insertion_kind mtik
> +    = get_missing_token_insertion_kind (token_type);
> +
> +  switch (mtik)
> +    {
> +    default:
> +      gcc_unreachable ();
> +      break;
> +
> +    case MTIK_IMPOSSIBLE:
> +      return;
> +
> +    case MTIK_INSERT_BEFORE_NEXT:
> +      /* Attempt to add the fix-it hint before the primary location
> +	 of RICHLOC.  */
> +      richloc->add_fixit_insert_before (cpp_type2name (token_type,
> 0));
> +      break;
> +
> +    case MTIK_INSERT_AFTER_PREV:
> +      /* Attempt to add the fix-it hint after PREV_TOKEN_LOC.  */
> +      richloc->add_fixit_insert_after (prev_token_loc,
> +				       cpp_type2name (token_type,
> 0));
> +      break;
> +    }
> +
> +  /* If we were successful, use the fix-it hint's location as the
> +     primary location within RICHLOC, adding the old primary
> location
> +     back as a secondary location.  */
> +  if (!richloc->seen_impossible_fixit_p ())
> +    {
> +      fixit_hint *hint = richloc->get_last_fixit_hint ();
> +      location_t hint_loc = hint->get_start_loc ();
> +      location_t old_loc = richloc->get_loc ();
> +
> +      richloc->set_range (line_table, 0, hint_loc, true);
> +      richloc->add_range (old_loc, false);
> +    }
> +}
> +
>  #if CHECKING_P
>  
>  namespace selftest {
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index da6a0be..7e1877e 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -1550,6 +1550,9 @@ extern int c_flt_eval_method (bool ts18661_p);
>  extern void add_no_sanitize_value (tree node, unsigned int flags);
>  
>  extern void maybe_add_include_fixit (rich_location *, const char *);
> +extern void maybe_suggest_missing_token_insertion (rich_location
> *richloc,
> +						   enum cpp_ttype
> token_type,
> +						   location_t
> prev_token_loc);
>  
>  #if CHECKING_P
>  namespace selftest {
> diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
> index 1a5e39e..a5e3ec4 100644
> --- a/gcc/c/c-parser.c
> +++ b/gcc/c/c-parser.c
> @@ -1041,13 +1041,21 @@ get_matching_symbol (enum cpp_ttype type)
>     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).  */
> +   CPP_CLOSE_PAREN).
> +
> +   If TYPE_IS_UNIQUE is true (the default) then msgid describes
> exactly
> +   one type (e.g. "expected %<)%>") and thus it may be reasonable to
> +   attempt to generate a fix-it hint for the problem.
> +   Otherwise msgid describes multiple token types (e.g.
> +   "expected %<;%>, %<,%> or %<)%>"), and thus we shouldn't attempt
> to
> +   generate a fix-it hint.  */
>  
>  bool
>  c_parser_require (c_parser *parser,
>  		  enum cpp_ttype type,
>  		  const char *msgid,
> -		  location_t matching_location)
> +		  location_t matching_location,
> +		  bool type_is_unique)
>  {
>    if (c_parser_next_token_is (parser, type))
>      {
> @@ -1059,6 +1067,13 @@ c_parser_require (c_parser *parser,
>        location_t next_token_loc = c_parser_peek_token (parser)-
> >location;
>        gcc_rich_location richloc (next_token_loc);
>  
> +      /* Potentially supply a fix-it hint, suggesting to add the
> +	 missing token immediately after the *previous* token.
> +	 This may move the primary location within richloc.  */
> +      if (!parser->error && type_is_unique)
> +	maybe_suggest_missing_token_insertion (&richloc, type,
> +					       parser-
> >last_token_location);
> +
>        /* If matching_location != UNKNOWN_LOCATION, highlight it.
>  	 Attempt to consolidate diagnostics by printing it as a
>  	 secondary range within the main diagnostic.  */
> @@ -3975,7 +3990,8 @@ c_parser_parms_list_declarator (c_parser
> *parser, tree attrs, tree expr)
>  	    return get_parm_info (false, expr);
>  	}
>        if (!c_parser_require (parser, CPP_COMMA,
> -			     "expected %<;%>, %<,%> or %<)%>"))
> +			     "expected %<;%>, %<,%> or %<)%>",
> +			     UNKNOWN_LOCATION, false))
>  	{
>  	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
>  	  return NULL;
> @@ -6429,7 +6445,8 @@ c_parser_asm_statement (c_parser *parser)
>        if (!c_parser_require (parser, CPP_COLON,
>  			     is_goto
>  			     ? G_("expected %<:%>")
> -			     : G_("expected %<:%> or %<)%>")))
> +			     : G_("expected %<:%> or %<)%>"),
> +			     UNKNOWN_LOCATION, is_goto))
>  	goto error_close_paren;
>  
>        /* Once past any colon, we're no longer a simple asm.  */
> diff --git a/gcc/c/c-parser.h b/gcc/c/c-parser.h
> index 01a7b72..21e4054 100644
> --- a/gcc/c/c-parser.h
> +++ b/gcc/c/c-parser.h
> @@ -137,7 +137,8 @@ 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,
> -			      location_t matching_location =
> UNKNOWN_LOCATION);
> +			      location_t matching_location =
> UNKNOWN_LOCATION,
> +			      bool type_is_unique=true);
>  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,
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index c104612..77e82c9 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -2787,6 +2787,40 @@ get_matching_symbol (required_token
> token_desc)
>      }
>  }
>  
> +/* Attempt to convert TOKEN_DESC from a required_token to an
> +   enum cpp_ttype, returning CPP_EOF if there is no good
> conversion.  */
> +
> +static enum cpp_ttype
> +get_required_cpp_ttype (required_token token_desc)
> +{
> +  switch (token_desc)
> +    {
> +    case RT_SEMICOLON:
> +      return CPP_SEMICOLON;
> +    case RT_OPEN_PAREN:
> +      return CPP_OPEN_PAREN;
> +    case RT_CLOSE_BRACE:
> +      return CPP_CLOSE_BRACE;
> +    case RT_OPEN_BRACE:
> +      return CPP_OPEN_BRACE;
> +    case RT_CLOSE_SQUARE:
> +      return CPP_CLOSE_SQUARE;
> +    case RT_OPEN_SQUARE:
> +      return CPP_OPEN_SQUARE;
> +    case RT_COMMA:
> +      return CPP_COMMA;
> +    case RT_COLON:
> +      return CPP_COLON;
> +    case RT_CLOSE_PAREN:
> +      return CPP_CLOSE_PAREN;
> +
> +    default:
> +      /* Use CPP_EOF as a "no completions possible" code.  */
> +      return CPP_EOF;
> +    }
> +}
> +
> +
>  /* Subroutine of cp_parser_error and cp_parser_required_error.
>  
>     Issue a diagnostic of the form
> @@ -2798,9 +2832,12 @@ get_matching_symbol (required_token
> token_desc)
>     This bypasses the check for tentative passing, and potentially
>     adds material needed by cp_parser_required_error.
>  
> -   If MISSING_TOKEN_DESC is not RT_NONE, and MATCHING_LOCATION is
> not
> -   UNKNOWN_LOCATION, then we have an unmatched symbol at
> -   MATCHING_LOCATION; highlight this secondary location.  */
> +   If MISSING_TOKEN_DESC is not RT_NONE, then potentially add fix-it 
> hints
> +   suggesting insertion of the missing token.
> +
> +   Additionally, if MATCHING_LOCATION is not UNKNOWN_LOCATION, then
> we
> +   have an unmatched symbol at MATCHING_LOCATION; highlight this
> secondary
> +   location.  */
>  
>  static void
>  cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
> @@ -2839,6 +2876,14 @@ cp_parser_error_1 (cp_parser* parser, const
> char* gmsgid,
>  
>    if (missing_token_desc != RT_NONE)
>      {
> +      /* Potentially supply a fix-it hint, suggesting to add the
> +	 missing token immediately after the *previous* token.
> +	 This may move the primary location within richloc.  */
> +      enum cpp_ttype ttype = get_required_cpp_ttype
> (missing_token_desc);
> +      location_t prev_token_loc
> +	= cp_lexer_previous_token (parser->lexer)->location;
> +      maybe_suggest_missing_token_insertion (&richloc, ttype,
> prev_token_loc);
> +
>        /* If matching_location != UNKNOWN_LOCATION, highlight it.
>  	 Attempt to consolidate diagnostics by printing it as a
>  	secondary range within the main diagnostic.  */
> diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
> b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
> index 18816e0..fd4fe54 100644
> --- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
> +++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors.c
> @@ -7,5 +7,5 @@ int main (void)
>    
>    array2[:] = array2[: ;  /* { dg-error "expected ']'" } */
>  
> -  return 0;
> -} /* { dg-error "expected ';' before" "" { target c } } */
> +  return 0; /* { dg-error "expected ';' before" "" { target c } } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c 
> b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
> index 2bb9134..d003d7c 100644
> --- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
> +++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors2.c
> @@ -7,6 +7,7 @@ int main (void)
>    
>    array2[:] = array2[1:2:] ;  /* { dg-error "expected expression
> before" "" { target c } } */ 
>    /* { dg-error  "expected primary-expression before" "" { target
> c++ } .-1 } */
> +  /* { dg-error "expected ';' before" "" { target c } .-2 } */
>  
> -  return 0; /* { dg-error "expected ';' before" "" { target c }  }
> */
> +  return 0;
>  }
> diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c 
> b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
> index 9270007..14256e9 100644
> --- a/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
> +++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/parser_errors3.c
> @@ -7,6 +7,7 @@ int main (void)
>    
>    array2[:] = array2[1: :] ;  /* { dg-error "expected expression
> before" "" { target c }  } */ 
>    /* { dg-error "expected primary-expression before" "" { target c++
> }  .-1 } */
> +  /* { dg-error "expected ';' before" "" { target c } .-2 } */
>  
> -  return 0; /* { dg-error "expected ';' before" "" { target c } } */
> +  return 0;
>  }
> diff --git a/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
> b/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
> index a9a9d66..8c32ad9 100644
> --- a/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
> +++ b/gcc/testsuite/c-c++-common/cilk-plus/AN/pr61191.c
> @@ -7,4 +7,5 @@ double f(double * A, double * B)
>    return __sec_reduce_add((B[0:500])(; /* { dg-error "called object"
> "" { target c } } */
>  /* { dg-error "expected expression before ';' token" "" { target c }
> .-1 } */
>  /* { dg-error "expected primary-expression before ';' token" "" {
> target c++ } .-2 } */
> -} /* { dg-error "expected" "" { target c } } */
> +/* { dg-error "expected" "" { target c } .-3 } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/gomp/pr63326.c
> b/gcc/testsuite/c-c++-common/gomp/pr63326.c
> index e319f49..3e62723 100644
> --- a/gcc/testsuite/c-c++-common/gomp/pr63326.c
> +++ b/gcc/testsuite/c-c++-common/gomp/pr63326.c
> @@ -156,34 +156,34 @@ f4 (int x)
>    {
>      do
>        #pragma omp barrier			/* { dg-error "may
> only be used in compound statements" } */
> -    while (0);
> +    while (0); /* { dg-error "before" "" { target c++ } } */
>    } /* { dg-error "before" "" { target c++ } } */
>    {
>      do
>        #pragma omp flush				/* { dg-error 
> "may only be used in compound statements" } */
> -    while (0);
> +    while (0); /* { dg-error "before" "" { target c++ } } */
>    } /* { dg-error "before" "" { target c++ } } */
>    {
>      do
>        #pragma omp taskwait			/* { dg-error "may
> only be used in compound statements" } */
> -    while (0);
> +    while (0); /* { dg-error "before" "" { target c++ } } */
>    } /* { dg-error "before" "" { target c++ } } */
>    {
>      do
>        #pragma omp taskyield			/* { dg-error
> "may only be used in compound statements" } */
> -    while (0);
> +    while (0); /* { dg-error "before" "" { target c++ } } */
>    } /* { dg-error "before" "" { target c++ } } */
>    #pragma omp parallel
>    {
>      do
>        #pragma omp cancel parallel		/* { dg-error "may
> only be used in compound statements" } */
> -    while (0);
> +    while (0); /* { dg-error "before" "" { target c++ } } */
>    } /* { dg-error "before" "" { target c++ } } */
>    #pragma omp parallel
>    {
>      do
>        #pragma omp cancellation point parallel	/* { dg-error
> "may only be used in compound statements" } */
> -    while (0);
> +    while (0); /* { dg-error "before" "" { target c++ } } */
>    } /* { dg-error "before" "" { target c++ } } */
>    #pragma omp for ordered(1)
>    for (i = 0; i < 16; i++)
> @@ -191,28 +191,28 @@ f4 (int x)
>        {
>  	do
>  	  #pragma omp ordered depend(source)	/* { dg-error
> "may only be used in compound statements" } */
> -	while (0);
> +	while (0); /* { dg-error "before" "" { target c++ } } */
>        } /* { dg-error "before" "" { target c++ } } */
>        {
>  	do
>  	  #pragma omp ordered depend(sink: i-1)	/* { dg-error 
> "may only be used in compound statements" } */
> -	while (0);
> +	while (0); /* { dg-error "before" "" { target c++ } } */
>        } /* { dg-error "before" "" { target c++ } } */
>      }
>    {
>      do
>        #pragma omp target enter data map(to:i)	/* { dg-error
> "may only be used in compound statements" } */
> -    while (0);
> +    while (0); /* { dg-error "before" "" { target c++ } } */
>    } /* { dg-error "before" "" { target c++ } } */
>    {
>      do
>        #pragma omp target update to(i)		/* { dg-error
> "may only be used in compound statements" } */
> -    while (0);
> +    while (0); /* { dg-error "before" "" { target c++ } } */
>    } /* { dg-error "before" "" { target c++ } } */
>    {
>      do
>        #pragma omp target exit data map(from:i)	/* { dg-error
> "may only be used in compound statements" } */
> -    while (0);
> +    while (0); /* { dg-error "before" "" { target c++ } } */
>    } /* { dg-error "before" "" { target c++ } } */
>  }
>  
> diff --git a/gcc/testsuite/c-c++-common/missing-close-symbol.c
> b/gcc/testsuite/c-c++-common/missing-close-symbol.c
> index 85b96f28..abeb837 100644
> --- a/gcc/testsuite/c-c++-common/missing-close-symbol.c
> +++ b/gcc/testsuite/c-c++-common/missing-close-symbol.c
> @@ -12,6 +12,7 @@ void test_static_assert_same_line (void)
>    /* { dg-begin-multiline-output "" }
>     _Static_assert(sizeof(int) >= sizeof(char), "msg";
>                   ~                                  ^
> +                                                    )
>       { dg-end-multiline-output "" } */
>  }
>  
> @@ -25,6 +26,7 @@ void test_static_assert_different_line (void)
>    /* { dg-begin-multiline-output "" }
>      "msg";
>           ^
> +         )
>       { dg-end-multiline-output "" } */
>    /* { dg-begin-multiline-output "" }
>     _Static_assert(sizeof(int) >= sizeof(char),
> diff --git a/gcc/testsuite/c-c++-common/missing-symbol.c
> b/gcc/testsuite/c-c++-common/missing-symbol.c
> index 33a501b..326b9fa 100644
> --- a/gcc/testsuite/c-c++-common/missing-symbol.c
> +++ b/gcc/testsuite/c-c++-common/missing-symbol.c
> @@ -5,15 +5,14 @@ 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 "" } */
> +  switch (i /* { dg-error "12: expected '\\)' before '.' token" } */
> +    {
>    /* { dg-begin-multiline-output "" }
>     switch (i
> -          ^
> +          ~ ^
> +            )
> +     {
> +     ~       
>       { dg-end-multiline-output "" } */
>  
>      case 0:
> @@ -30,21 +29,33 @@ int missing_close_paren_in_switch (int i)
>  void missing_close_paren_in_if (void)
>  {
>    if (foo () /* { dg-line start_of_if } */
> -      && bar () 
> -    { /* { dg-error "5: expected '\\)' before '.' token" } */
> +      && bar () /* { dg-error "16: expected '\\)' before '.' token"
> } */
> +    {
>        /* { dg-begin-multiline-output "" }
> +       && bar ()
> +                ^
> +                )
>       {
> -     ^
> +     ~           
>           { 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-end-multiline-output "" } */
>      }
> -
>  } /* { dg-error "1: expected" } */
>    /* { dg-begin-multiline-output "" }
>   }
>   ^
>       { dg-end-multiline-output "" } */
> +
> +int missing_colon_in_ternary (int flag)
> +{
> +  return flag ? 42 0; /* { dg-error "expected ':' before numeric
> constant" } */
> +  /* { dg-begin-multiline-output "" }
> +   return flag ? 42 0;
> +                   ^~
> +                   :
> +     { dg-end-multiline-output "" } */
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
> b/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
> index 833fab7..727e74e 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/digit-sep-neg.C
> @@ -26,5 +26,5 @@ main()
>  }
>  
>  // { dg-error "exponent has no digits" "exponent has no digits" {
> target *-*-* } 21 }
> -// { dg-error "expected ';' before" "expected ';' before" { target
> *-*-* } 14 }
> -// { dg-error "expected ';' before" "expected ';' before" { target
> *-*-* } 25 }
> +// { dg-error "expected ';' before" "expected ';' before" { target
> *-*-* } 13 }
> +// { dg-error "expected ';' before" "expected ';' before" { target
> *-*-* } 24 }
> diff --git a/gcc/testsuite/g++.dg/cpp1y/pr65202.C
> b/gcc/testsuite/g++.dg/cpp1y/pr65202.C
> index 602b264..7ce4895 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/pr65202.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/pr65202.C
> @@ -22,5 +22,5 @@ struct bar;
>  int main()
>  {
>      foo<ns::bar> f;
> -    adl::swap(f, f)
> -} // { dg-error "" }
> +    adl::swap(f, f) // { dg-error "expected ';'" }
> +} // { dg-error "expected '.'" "expected end of namespace" }
> diff --git a/gcc/testsuite/g++.dg/missing-symbol-2.C
> b/gcc/testsuite/g++.dg/missing-symbol-2.C
> new file mode 100644
> index 0000000..4a119f8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/missing-symbol-2.C
> @@ -0,0 +1,58 @@
> +/* { dg-options "-fdiagnostics-show-caret" } */
> +
> +extern int foo (void);
> +
> +void missing_open_paren (void)
> +{
> +  if foo ()) /* { dg-error "expected '\\(' before 'foo'" } */
> +    {
> +    }
> +  /* { dg-begin-multiline-output "" }
> +   if foo ())
> +      ^~~
> +      (
> +     { dg-end-multiline-output "" } */
> +}
> +
> +
> +void missing_close_square (void)
> +{
> +  const char test [42;  /* { dg-error "22: expected ']' before ';'
> token" } */
> +  /* { dg-begin-multiline-output "" }
> +   const char test [42;
> +                      ^
> +                      ]
> +     { dg-end-multiline-output "" } */
> +}
> +
> +int missing_semicolon (void)
> +{
> +  return 42 /* { dg-error "expected ';'" } */
> +}
> +/* { dg-begin-multiline-output "" }
> +   return 42
> +            ^
> +            ;
> + }
> + ~           
> +   { dg-end-multiline-output "" } */
> +
> +
> +int missing_colon_in_switch (int val)
> +{
> +  switch (val)
> +    {
> +    case 42 /* { dg-error "expected ':' before 'return'" } */
> +      return 42;
> +    /* { dg-begin-multiline-output "" }
> +     case 42
> +            ^
> +            :
> +       return 42;
> +       ~~~~~~
> +       { dg-end-multiline-output "" } */
> +
> +    default:
> +      return val;
> +    }
> +}
> diff --git a/gcc/testsuite/g++.dg/other/do1.C
> b/gcc/testsuite/g++.dg/other/do1.C
> index b3a9daf..db65e7d 100644
> --- a/gcc/testsuite/g++.dg/other/do1.C
> +++ b/gcc/testsuite/g++.dg/other/do1.C
> @@ -7,7 +7,7 @@
>  
>  void init ()
>  {
> -  do {  } while (0)
> -	    obj = 0; // { dg-error "expected|not declared" }
> +  do {  } while (0) // { dg-error "expected ';'" }
> +	    obj = 0; // { dg-error "not declared" }
>       
>  }
> diff --git a/gcc/testsuite/g++.dg/parse/error11.C
> b/gcc/testsuite/g++.dg/parse/error11.C
> index d118c19..1a49d6e 100644
> --- a/gcc/testsuite/g++.dg/parse/error11.C
> +++ b/gcc/testsuite/g++.dg/parse/error11.C
> @@ -52,7 +52,7 @@ void func(void)
>    Foo[:B> k1;       // { dg-bogus "cannot begin|alternate spelling"
> "smart error should not be triggered here" } 
>  // { dg-error "6:missing template arguments before" "template" {
> target *-*-* } 51 }
>  // { dg-error "9:expected primary-expression before ':' token"
> "primary" { target *-*-* } 51 }
> -// { dg-error "9:expected '\]' before ':' token" "backslash" {
> target *-*-* } 51 }
> +// { dg-error "8:expected '\]' before ':' token" "backslash" {
> target *-*-* } 51 }
>  // { dg-error "6:missing template arguments before" "template" {
> target *-*-* } 52 }
>  // { dg-error "7:expected primary-expression before ':' token"
> "primary" { target *-*-* } 52 }
>  // { dg-error "7:expected '\]' before ':' token" "backslash" {
> target *-*-* } 52 }
> diff --git a/gcc/testsuite/g++.dg/template/error11.C
> b/gcc/testsuite/g++.dg/template/error11.C
> index 3a469fd..1640298 100644
> --- a/gcc/testsuite/g++.dg/template/error11.C
> +++ b/gcc/testsuite/g++.dg/template/error11.C
> @@ -1,4 +1,4 @@
>  // PR c++/12132
>  
>  inline template <int> void foo () {} // { dg-error "<" }
> -void abort (); // { dg-error ";" }
> +void abort (); // { dg-error ";" "" { target *-*-* } .-1 }
> diff --git a/gcc/testsuite/gcc.dg/missing-symbol-2.c
> b/gcc/testsuite/gcc.dg/missing-symbol-2.c
> new file mode 100644
> index 0000000..7ee795d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/missing-symbol-2.c
> @@ -0,0 +1,71 @@
> +/* { dg-options "-fdiagnostics-show-caret -Wno-switch-unreachable" }
> */
> +
> +extern int foo (void);
> +
> +void missing_open_paren (void)
> +{
> +  if foo ()) /* { dg-line missing_open_paren } */
> +    {
> +    }
> +  /* { dg-error "expected '\\(' before 'foo'" "" { target c }
> missing_open_paren } */
> +  /* { dg-begin-multiline-output "" }
> +   if foo ())
> +      ^~~
> +      (
> +     { dg-end-multiline-output "" } */
> +  /* { dg-error "expected statement before '\\)' token"  "" { target
> c } missing_open_paren } */
> +  /* { dg-begin-multiline-output "" }
> +   if foo ())
> +            ^
> +     { dg-end-multiline-output "" } */
> +}
> +
> +void missing_close_square (void)
> +{
> +  const char test [42;  /* { dg-error "22: expected ']' before ';'
> token" } */
> +  /* { dg-begin-multiline-output "" }
> +   const char test [42;
> +                      ^
> +                      ]
> +     { dg-end-multiline-output "" } */
> +}
> +
> +int missing_semicolon (void)
> +{
> +  return 42 /* { dg-error "expected ';'" } */
> +}
> +/* { dg-begin-multiline-output "" }
> +   return 42
> +            ^
> +            ;
> + }
> + ~           
> +   { dg-end-multiline-output "" } */
> +
> +
> +/* We don't offer a fix-it hint for this case in C, as it could be
> +   colon or ellipsis.
> +   TODO: we could be smarter about error-recovery here; given the
> +   return perhaps we could assume a missing colon.  */
> +
> +int missing_colon_in_switch (int val)
> +{
> +  switch (val)
> +    {
> +    case 42
> +      return 42; /* { dg-error "expected ':' or '...' before
> 'return'" } */
> +    /* { dg-begin-multiline-output "" }
> +       return 42;
> +       ^~~~~~
> +       { dg-end-multiline-output "" } */
> +
> +    default:
> +      return val;
> +    }
> +}
> +
> +/* { dg-begin-multiline-output "" }
> + int dummy;
> + ^~~
> +   { dg-end-multiline-output "" } */
> +int dummy;/* { dg-error "expected declaration or statement at end of
> input" "" { target c } } */
> diff --git a/gcc/testsuite/gcc.dg/missing-symbol-3.c
> b/gcc/testsuite/gcc.dg/missing-symbol-3.c
> new file mode 100644
> index 0000000..e2d00df
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/missing-symbol-3.c
> @@ -0,0 +1,50 @@
> +/* { dg-options "-fdiagnostics-show-caret" } */
> +
> +/* A sequence of bogus _Static_assert.
> +   We can offer fix-it hints for some of these, but not all.  */
> +
> +void test_static_assert_1 (void)
> +{
> +  _Static_assert sizeof(int) >= sizeof(char); /* { dg-error
> "expected '\\(' before 'sizeof'" } */
> +  /* { dg-begin-multiline-output "" }
> +   _Static_assert sizeof(int) >= sizeof(char);
> +                  ^~~~~~
> +                  (
> +     { dg-end-multiline-output "" } */
> +}
> +
> +void test_static_assert_2 (void)
> +{
> +  _Static_assert(sizeof(int) >= sizeof(char); /* { dg-error
> "expected ',' before ';' token" } */
> +  /* { dg-begin-multiline-output "" }
> +   _Static_assert(sizeof(int) >= sizeof(char);
> +                                             ^
> +                                             ,
> +     { dg-end-multiline-output "" } */
> +}
> +
> +void test_static_assert_3 (void)
> +{
> +  _Static_assert(sizeof(int) >= sizeof(char),; /* { dg-error
> "expected string literal before ';' token" } */
> +  /* { dg-begin-multiline-output "" }
> +   _Static_assert(sizeof(int) >= sizeof(char),;
> +                                              ^
> +     { dg-end-multiline-output "" } */
> +}
> +
> +void test_static_assert_4 (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 "" } */
> +}
> +
> +/* The final one is correct.  */
> +
> +void test_static_assert_5 (void)
> +{
> +  _Static_assert(sizeof(int) >= sizeof(char), "msg");
> +}
> diff --git a/gcc/testsuite/gcc.dg/noncompile/940112-1.c
> b/gcc/testsuite/gcc.dg/noncompile/940112-1.c
> index bb5e0f6..0a9e07d 100644
> --- a/gcc/testsuite/gcc.dg/noncompile/940112-1.c
> +++ b/gcc/testsuite/gcc.dg/noncompile/940112-1.c
> @@ -3,5 +3,5 @@ f (int x)
>  {
>    double e = 1;
>    e = 1;
> -  return (e)
> -}	/* { dg-error "parse error|syntax error|expected" } */
> +  return (e) /* { dg-error "parse error|syntax error|expected" } */
> +}	
> diff --git a/gcc/testsuite/gcc.dg/noncompile/971104-1.c
> b/gcc/testsuite/gcc.dg/noncompile/971104-1.c
> index 39e00c6..4a04dad 100644
> --- a/gcc/testsuite/gcc.dg/noncompile/971104-1.c
> +++ b/gcc/testsuite/gcc.dg/noncompile/971104-1.c
> @@ -27,6 +27,6 @@ static void up(int sem){
>      printf("%s had processes sleeping on it!\n",
>      ({ "MUTEX     ", "BARB_SEM 1", "BARB_SEM 2", "CUST_SEM 1",
>         "CUST_SEM 2", "WAIT_SEM 1", "WAIT_SEM 2", "WAIT_SEM 3",
> -       "WAIT_SEM 4"}	 /* { dg-error "parse error|syntax
> error|expected" } */
> -	[( sb.sem_num )]) ); /* { dg-error "expected" } */
> +       "WAIT_SEM 4"}	 /* { dg-error "expected" } */
> +	[( sb.sem_num )]) );
>  }
> diff --git a/gcc/testsuite/obj-c++.dg/exceptions-6.mm
> b/gcc/testsuite/obj-c++.dg/exceptions-6.mm
> index 58882fe..6f6ba78 100644
> --- a/gcc/testsuite/obj-c++.dg/exceptions-6.mm
> +++ b/gcc/testsuite/obj-c++.dg/exceptions-6.mm
> @@ -11,15 +11,15 @@ void test (id object)
>    @throw object;   /* Ok */
>    @throw;          /* { dg-error ".@throw. .rethrow. used outside of
> a @catch block" } */
>    @throw (object); /* Ok.  */
> -  @throw (id)0
> -}                  /* { dg-error "expected" } */
> +  @throw (id)0     /* { dg-error "expected" } */
> +}
>  
>  void test2 (id object)
>  {
>    @throw object);  /* { dg-error "expected" } */
>    @throw (...);    /* { dg-error "expected" } */
>    @throw ();       /* { dg-error "expected" } */
> -  @throw           
> +  @throw           /* { dg-error "expected" } */
>  }                  /* { dg-error "expected" } */
>  
>  void test3 (id object1, id object2)
> diff --git a/gcc/testsuite/obj-c++.dg/pr48187.mm b/gcc/testsuite/obj-
> c++.dg/pr48187.mm
> index 750710b..99677a5 100644
> --- a/gcc/testsuite/obj-c++.dg/pr48187.mm
> +++ b/gcc/testsuite/obj-c++.dg/pr48187.mm
> @@ -1,19 +1,19 @@
>  /* { dg-do compile } */
>  
>  @interface A
> -{
> +{    /* { dg-error "xpected" } */
>    ]  /* { dg-error "xpected" } */
>  }
>  @end
>  
>  @interface B
> -{
> +{     /* { dg-error "xpected" } */
>    ];  /* { dg-error "xpected" } */
>  }
>  @end
>  
>  @interface C
> -{
> +{     /* { dg-error "xpected" } */
>    ];  /* { dg-error "xpected" } */
>    int x;
>  }
> @@ -21,7 +21,7 @@
>  
>  @interface D
>  {
> -  (
> +  (  /* { dg-error "xpected" } */
>  }  /* { dg-error "xpected" } */
>  @end
>  
> diff --git a/gcc/testsuite/objc.dg/exceptions-6.m
> b/gcc/testsuite/objc.dg/exceptions-6.m
> index 58882fe..74be98d 100644
> --- a/gcc/testsuite/objc.dg/exceptions-6.m
> +++ b/gcc/testsuite/objc.dg/exceptions-6.m
> @@ -11,8 +11,8 @@ void test (id object)
>    @throw object;   /* Ok */
>    @throw;          /* { dg-error ".@throw. .rethrow. used outside of
> a @catch block" } */
>    @throw (object); /* Ok.  */
> -  @throw (id)0
> -}                  /* { dg-error "expected" } */
> +  @throw (id)0     /* { dg-error "expected" } */
> +}
>  
>  void test2 (id object)
>  {

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

* Re: [PATCH 1/2] C++: avoid partial duplicate implementation of cp_parser_error
  2017-09-26 13:56   ` [PATCH 1/2] C++: avoid partial duplicate implementation of cp_parser_error David Malcolm
  2017-10-11 17:26     ` PING: " David Malcolm
@ 2017-10-11 21:21     ` Jason Merrill
  2017-10-12 18:10       ` David Malcolm
  1 sibling, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2017-10-11 21:21 UTC (permalink / raw)
  To: David Malcolm; +Cc: Jeff Law, gcc-patches List

On Tue, Sep 26, 2017 at 9:56 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> In r251026 (aka 3fe34694f0990d1d649711ede0326497f8a849dc,
> "C/C++: show pertinent open token when missing a close token")
> I copied part of cp_parser_error into cp_parser_required_error,
> leading to duplication of code.
>
> This patch eliminates this duplication by merging the two copies of the
> code into a new cp_parser_error_1 subroutine.
>
> Doing so removes an indentation level, making the patch appear to have
> more churn than it really does.

FWIW, you could also attach a patch generated with -w to ignore
whitespace changes.

The patch is OK.

Jason

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

* Re: [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v3)
  2017-10-05 16:08     ` [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v3) David Malcolm
  2017-10-11 17:27       ` PING " David Malcolm
@ 2017-10-12  5:23       ` Jason Merrill
  1 sibling, 0 replies; 18+ messages in thread
From: Jason Merrill @ 2017-10-12  5:23 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches List

On Thu, Oct 5, 2017 at 12:08 PM, David Malcolm <dmalcolm@redhat.com> wrote:
> Here's a slight update to this patch, since v2 was made invalid by
> r253411 ("C: underline parameters in mismatching function calls").
>
> Both v2 and r253411 added code to c-parser.c/h to track the location_t
> of the last consumed token (although I somehow managed to name the new
> field in c_parser differently between the two versions...)
>
> This version (v3) is the same as v2, but removes the copy of the
> above code, updating the usage sites to use the field name from
> r253411.
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu
> in conjunction with patch 1 of the kit:
>   https://gcc.gnu.org/ml/gcc-patches/2017-09/msg01745.html
>
> OK for trunk?

OK.

Jason

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

* Re: [PATCH 1/2] C++: avoid partial duplicate implementation of cp_parser_error
  2017-10-11 21:21     ` Jason Merrill
@ 2017-10-12 18:10       ` David Malcolm
  0 siblings, 0 replies; 18+ messages in thread
From: David Malcolm @ 2017-10-12 18:10 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Jeff Law, gcc-patches List, David Malcolm

On Wed, 2017-10-11 at 17:18 -0400, Jason Merrill wrote:
> On Tue, Sep 26, 2017 at 9:56 AM, David Malcolm <dmalcolm@redhat.com>
> wrote:
> > In r251026 (aka 3fe34694f0990d1d649711ede0326497f8a849dc,
> > "C/C++: show pertinent open token when missing a close token")
> > I copied part of cp_parser_error into cp_parser_required_error,
> > leading to duplication of code.
> > 
> > This patch eliminates this duplication by merging the two copies of
> > the
> > code into a new cp_parser_error_1 subroutine.
> > 
> > Doing so removes an indentation level, making the patch appear to
> > have
> > more churn than it really does.
> 
> FWIW, you could also attach a patch generated with -w to ignore
> whitespace changes.
> 
> The patch is OK.
> 
> Jason

Thanks; committed to trunk as r253686.

For reference, here's a version of the patch generated with -w.

gcc/cp/ChangeLog:
	* parser.c (get_matching_symbol): Move to before...
	(cp_parser_error): Split out into...
	(cp_parser_error_1): ...this new function, merging in content
	from...
	(cp_parser_required_error): ...here.  Eliminate partial duplicate
	of body of cp_parser_error in favor of a call to the new
	cp_parser_error_1 helper function.

gcc/testsuite/ChangeLog:
	* g++.dg/parse/pragma2.C: Update to reflect reinstatement of the
	"#pragma is not allowed here" error.
---
 gcc/cp/parser.c                      | 119 ++++++++++++++++++++---------------
 gcc/testsuite/g++.dg/parse/pragma2.C |   4 +-
 2 files changed, 72 insertions(+), 51 deletions(-)

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index a556a21..4e08032 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2769,16 +2769,43 @@ cp_lexer_peek_conflict_marker (cp_lexer *lexer, enum cpp_ttype tok1_kind,
   return true;
 }
 
-/* If not parsing tentatively, issue a diagnostic of the form
+/* 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 "(";
+    }
+}
+
+/* Subroutine of cp_parser_error and cp_parser_required_error.
+
+   Issue a diagnostic of the form
       FILE:LINE: MESSAGE before TOKEN
    where TOKEN is the next token in the input stream.  MESSAGE
    (specified by the caller) is usually of the form "expected
-   OTHER-TOKEN".  */
+   OTHER-TOKEN".
+
+   This bypasses the check for tentative passing, and potentially
+   adds material needed by cp_parser_required_error.
+
+   If MISSING_TOKEN_DESC is not RT_NONE, and MATCHING_LOCATION is not
+   UNKNOWN_LOCATION, then we have an unmatched symbol at
+   MATCHING_LOCATION; highlight this secondary location.  */
 
 static void
-cp_parser_error (cp_parser* parser, const char* gmsgid)
-{
-  if (!cp_parser_simulate_error (parser))
+cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
+		   required_token missing_token_desc,
+		   location_t matching_location)
 {
   cp_token *token = cp_lexer_peek_token (parser->lexer);
   /* This diagnostic makes more sense if it is tagged to the line
@@ -2806,16 +2833,52 @@ cp_parser_error (cp_parser* parser, const char* gmsgid)
 	}
     }
 
-      rich_location richloc (line_table, input_location);
+  gcc_rich_location richloc (input_location);
+
+  bool added_matching_location = false;
+
+  if (missing_token_desc != RT_NONE)
+    {
+      /* If matching_location != UNKNOWN_LOCATION, highlight it.
+	 Attempt to consolidate diagnostics by printing it as a
+	secondary range within the main diagnostic.  */
+      if (matching_location != UNKNOWN_LOCATION)
+	added_matching_location
+	  = richloc.add_location_if_nearby (matching_location);
+    }
+
+  /* Actually emit the error.  */
   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, &richloc);
+
+  if (missing_token_desc != RT_NONE)
+    {
+      /* 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 (missing_token_desc));
     }
 }
 
+/* If not parsing tentatively, issue a diagnostic of the form
+      FILE:LINE: MESSAGE before TOKEN
+   where TOKEN is the next token in the input stream.  MESSAGE
+   (specified by the caller) is usually of the form "expected
+   OTHER-TOKEN".  */
+
+static void
+cp_parser_error (cp_parser* parser, const char* gmsgid)
+{
+  if (!cp_parser_simulate_error (parser))
+    cp_parser_error_1 (parser, gmsgid, RT_NONE, UNKNOWN_LOCATION);
+}
+
 /* Issue an error about name-lookup failing.  NAME is the
    IDENTIFIER_NODE DECL is the result of
    the lookup (as returned from cp_parser_lookup_name).  DESIRED is
@@ -28087,24 +28150,6 @@ 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
@@ -28282,31 +28327,7 @@ cp_parser_required_error (cp_parser *parser,
     }
 
   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));
-    }
+    cp_parser_error_1 (parser, gmsgid, token_desc, matching_location);
 }
 
 
diff --git a/gcc/testsuite/g++.dg/parse/pragma2.C b/gcc/testsuite/g++.dg/parse/pragma2.C
index 3dc5fc1..c5616ff 100644
--- a/gcc/testsuite/g++.dg/parse/pragma2.C
+++ b/gcc/testsuite/g++.dg/parse/pragma2.C
@@ -4,5 +4,5 @@
 // does not.
 int f(int x,
 #pragma interface  // { dg-error "not allowed here" }
-      // { dg-bogus "expected identifier" "" { xfail *-*-* } .-1 }
-      int y);
+      // The parser gets confused and issues an error on the next line.
+      int y); // { dg-bogus "" "" { xfail *-*-* } } 
-- 
1.8.5.3

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

end of thread, other threads:[~2017-10-12 17:34 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-03 18:04 [PATCH] C/C++: add fix-it hints for various missing symbols David Malcolm
2017-07-03 18:57 ` Richard Sandiford
2017-07-03 19:34   ` David Malcolm
2017-07-11 14:09   ` [committed] diagnostics: support compact printing of secondary locations David Malcolm
2017-07-11 17:34     ` Richard Sandiford
2017-07-03 23:01 ` [PATCH] C/C++: add fix-it hints for various missing symbols Joseph Myers
2017-07-05 15:32   ` David Malcolm
2017-07-05 16:17     ` Joseph Myers
2017-08-28 16:11 ` Jeff Law
2017-09-26 13:56 ` [PATCH 0/2] " David Malcolm
2017-09-26 13:56   ` [PATCH 1/2] C++: avoid partial duplicate implementation of cp_parser_error David Malcolm
2017-10-11 17:26     ` PING: " David Malcolm
2017-10-11 21:21     ` Jason Merrill
2017-10-12 18:10       ` David Malcolm
2017-09-26 13:56   ` [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v2) David Malcolm
2017-10-05 16:08     ` [PATCH 2/2] C/C++: add fix-it hints for various missing symbols (v3) David Malcolm
2017-10-11 17:27       ` PING " David Malcolm
2017-10-12  5:23       ` Jason Merrill

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