[2/2] Provide diagnostic hints for missing C++ cinttypes string constants.

Message ID 20200524003014.32652-3-mark@klomp.org
State New
Headers show
Series
  • [1/2] Provide diagnostic hints for missing C inttypes.h string constants.
Related show

Commit Message

Mark Wielaard May 24, 2020, 12:30 a.m.
When reporting an error in cp_parser and we notice a string literal
followed by an unknown name check whether there is a known standard
header containing a string macro with the same name, then add a hint
to the error message to include that header.

gcc/c-family/ChangeLog:

	* known-headers.cc (get_cp_stdlib_header_for_string_macro_name):
	New function.
	* known-headers.h (get_c_stdlib_header_for_string_macro_name):
	New function definition.

gcc/cp/ChangeLog:

	* parser.c (cp_lexer_safe_previous_token): New function.
	(cp_parser_error_1): Add name_hint if the previous token is
	a string literal and next token is a CPP_NAME and we have a
	missing header suggestion for the name.

gcc/testsuite/ChangeLog:

	* g++.dg/spellcheck-inttypes.C: Add string-literal testcases.
---
 gcc/c-family/known-headers.cc              |  8 +++++
 gcc/c-family/known-headers.h               |  1 +
 gcc/cp/parser.c                            | 36 ++++++++++++++++++++
 gcc/testsuite/g++.dg/spellcheck-inttypes.C | 39 ++++++++++++++++++++++
 4 files changed, 84 insertions(+)

-- 
2.20.1

Comments

Patrick Palka via Gcc-patches May 25, 2020, 4:26 p.m. | #1
On 5/23/20 8:30 PM, Mark Wielaard wrote:
> When reporting an error in cp_parser and we notice a string literal

> followed by an unknown name check whether there is a known standard

> header containing a string macro with the same name, then add a hint

> to the error message to include that header.

> 

> gcc/c-family/ChangeLog:

> 

> 	* known-headers.cc (get_cp_stdlib_header_for_string_macro_name):

> 	New function.

> 	* known-headers.h (get_c_stdlib_header_for_string_macro_name):


Missing 'p'.

> 	New function definition.


Declaration, not definition.

The C++ changes are OK with these ChangeLog corrections.

> gcc/cp/ChangeLog:

> 

> 	* parser.c (cp_lexer_safe_previous_token): New function.

> 	(cp_parser_error_1): Add name_hint if the previous token is

> 	a string literal and next token is a CPP_NAME and we have a

> 	missing header suggestion for the name.

> 

> gcc/testsuite/ChangeLog:

> 

> 	* g++.dg/spellcheck-inttypes.C: Add string-literal testcases.

> ---

>   gcc/c-family/known-headers.cc              |  8 +++++

>   gcc/c-family/known-headers.h               |  1 +

>   gcc/cp/parser.c                            | 36 ++++++++++++++++++++

>   gcc/testsuite/g++.dg/spellcheck-inttypes.C | 39 ++++++++++++++++++++++

>   4 files changed, 84 insertions(+)

> 

> diff --git a/gcc/c-family/known-headers.cc b/gcc/c-family/known-headers.cc

> index c07cfd1db815..977230a586db 100644

> --- a/gcc/c-family/known-headers.cc

> +++ b/gcc/c-family/known-headers.cc

> @@ -268,6 +268,14 @@ get_c_stdlib_header_for_string_macro_name (const char *name)

>     return get_string_macro_hint (name, STDLIB_C);

>   }

>   

> +/* Given non-NULL NAME, return the header name defining a string macro

> +   within the C++ standard library (with '<' and '>'), or NULL.  */

> +const char *

> +get_cp_stdlib_header_for_string_macro_name (const char *name)

> +{

> +  return get_string_macro_hint (name, STDLIB_CPLUSPLUS);

> +}

> +

>   /* Implementation of class suggest_missing_header.  */

>   

>   /* suggest_missing_header's ctor.  */

> diff --git a/gcc/c-family/known-headers.h b/gcc/c-family/known-headers.h

> index a69bbbf28e76..f0c89dc9019d 100644

> --- a/gcc/c-family/known-headers.h

> +++ b/gcc/c-family/known-headers.h

> @@ -24,6 +24,7 @@ extern const char *get_c_stdlib_header_for_name (const char *name);

>   extern const char *get_cp_stdlib_header_for_name (const char *name);

>   

>   extern const char *get_c_stdlib_header_for_string_macro_name (const char *n);

> +extern const char *get_cp_stdlib_header_for_string_macro_name (const char *n);

>   

>   /* Subclass of deferred_diagnostic for suggesting to the user

>      that they have missed a #include.  */

> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c

> index 54ca875ce54c..95b8c635fc65 100644

> --- a/gcc/cp/parser.c

> +++ b/gcc/cp/parser.c

> @@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see

>   #include "tree-iterator.h"

>   #include "cp-name-hint.h"

>   #include "memmodel.h"

> +#include "c-family/known-headers.h"

>   

>   

>   /* The lexer.  */

> @@ -776,6 +777,20 @@ cp_lexer_previous_token (cp_lexer *lexer)

>     return cp_lexer_token_at (lexer, tp);

>   }

>   

> +/* Same as above, but return NULL when the lexer doesn't own the token

> +   buffer or if the next_token is at the start of the token

> +   vector.  */

> +

> +static cp_token *

> +cp_lexer_safe_previous_token (cp_lexer *lexer)

> +{

> +  if (lexer->buffer)

> +    if (lexer->next_token != lexer->buffer->address ())

> +      return cp_lexer_previous_token (lexer);

> +

> +  return NULL;

> +}

> +

>   /* Overload for make_location, taking the lexer to mean the location of the

>      previous token.  */

>   

> @@ -2919,6 +2934,7 @@ cp_parser_error_1 (cp_parser* parser, const char* gmsgid,

>   	}

>       }

>   

> +  auto_diagnostic_group d;

>     gcc_rich_location richloc (input_location);

>   

>     bool added_matching_location = false;

> @@ -2941,6 +2957,26 @@ cp_parser_error_1 (cp_parser* parser, const char* gmsgid,

>   	  = richloc.add_location_if_nearby (matching_location);

>       }

>   

> +  /* If we were parsing a string-literal and there is an unknown name

> +     token right after, then check to see if that could also have been

> +     a literal string by checking the name against a list of known

> +     standard string literal constants defined in header files. If

> +     there is one, then add that as an hint to the error message. */

> +  name_hint h;

> +  cp_token *prev_token = cp_lexer_safe_previous_token (parser->lexer);

> +  if (prev_token && cp_parser_is_string_literal (prev_token)

> +      && token->type == CPP_NAME)

> +    {

> +      tree name = token->u.value;

> +      const char *token_name = IDENTIFIER_POINTER (name);

> +      const char *header_hint

> +	= get_cp_stdlib_header_for_string_macro_name (token_name);

> +      if (header_hint != NULL)

> +	h = name_hint (NULL, new suggest_missing_header (token->location,

> +							 token_name,

> +							 header_hint));

> +    }

> +

>     /* Actually emit the error.  */

>     c_parse_error (gmsgid,

>   		 /* Because c_parser_error does not understand

> diff --git a/gcc/testsuite/g++.dg/spellcheck-inttypes.C b/gcc/testsuite/g++.dg/spellcheck-inttypes.C

> index c5861127ca6d..84bfc125513c 100644

> --- a/gcc/testsuite/g++.dg/spellcheck-inttypes.C

> +++ b/gcc/testsuite/g++.dg/spellcheck-inttypes.C

> @@ -23,6 +23,18 @@ const char *hex64_fmt = PRIx64; /* { dg-error "'PRIx64' was not declared" "undec

>   const char *hexptr_fmt = PRIxPTR; /* { dg-error "'PRIxPTR' was not declared" "undeclared identifier" { target *-*-* } } */

>   /* { dg-message "'PRIxPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

>   

> +/* As a part of a string-literal.  */

> +const char *dec8msg_fmt = "Provide %" PRId8 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> +/* { dg-message "'PRId8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +const char *dec16msg_fmt = "Provide %" PRId16 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> +/* { dg-message "'PRId16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +const char *dec32msg_fmt = "Provide %" PRId32 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> +/* { dg-message "'PRId32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +const char *dec64msg_fmt = "Provide %" PRId64 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> +/* { dg-message "'PRId64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +const char *decptrmsg_fmt = "Provide %" PRIdPTR "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> +/* { dg-message "'PRIdPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +

>   void test_printf (void)

>   {

>     printf ("some format strings %s, %s, %s, %s, %s, %s\n",

> @@ -38,4 +50,31 @@ void test_printf (void)

>   /* { dg-message "'PRIx32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

>   	  PRIoPTR);  /* { dg-error "'PRIoPTR' was not declared" "undeclared identifier" { target *-*-* } } */

>   /* { dg-message "'PRIoPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +

> +  printf ("%" PRIo8 "\n", i8); /* { dg-error "expected" } */

> +/* { dg-message "'PRIo8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +  printf ("%" PRIo16 "\n", i16); /* { dg-error "expected" } */

> +/* { dg-message "'PRIo16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +  printf ("%" PRIo32 "\n", i32); /* { dg-error "expected" } */

> +/* { dg-message "'PRIo32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +  printf ("%" PRIo64 "\n", i64); /* { dg-error "expected" } */

> +/* { dg-message "'PRIo64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +  printf ("%" PRIoPTR "\n", ip); /* { dg-error "expected" } */

> +/* { dg-message "'PRIoPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +}

> +

> +void test_scanf (void)

> +{

> +  scanf ("%" SCNu8 "\n", &i8); /* { dg-error "expected" } */

> +/* { dg-message "'SCNu8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +  scanf ("%" SCNu16 "\n", &i16); /* { dg-error "expected" } */

> +/* { dg-message "'SCNu16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +  scanf ("%" SCNu32 "\n", &i32); /* { dg-error "expected" } */

> +/* { dg-message "'SCNu32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +  scanf ("%" SCNu64 "\n", &i64); /* { dg-error "expected" } */

> +/* { dg-message "'SCNu64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +  scanf ("%" SCNuPTR "\n", &ip); /* { dg-error "expected" } */

> +/* { dg-message "'SCNuPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> +  scanf ("%" SCNxPTR "\n", &up); /* { dg-error "expected" } */

> +/* { dg-message "'SCNxPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

>   }

>
Mark Wielaard May 28, 2020, 11:33 p.m. | #2
Hi,

On Mon, May 25, 2020 at 12:26:33PM -0400, Jason Merrill wrote:
> On 5/23/20 8:30 PM, Mark Wielaard wrote:

> > When reporting an error in cp_parser and we notice a string literal

> > followed by an unknown name check whether there is a known standard

> > header containing a string macro with the same name, then add a hint

> > to the error message to include that header.

> > 

> > gcc/c-family/ChangeLog:

> > 

> > 	* known-headers.cc (get_cp_stdlib_header_for_string_macro_name):

> > 	New function.

> > 	* known-headers.h (get_c_stdlib_header_for_string_macro_name):

> 

> Missing 'p'.

> 

> > 	New function definition.

> 

> Declaration, not definition.

> 

> The C++ changes are OK with these ChangeLog corrections.


Thanks. David, are you OK with the diagnostic changes?

Who can we trick into reviewing the C frontend changes in the 1/2
patch that this depends on?

Cheers,

Mark

> > gcc/cp/ChangeLog:

> > 

> > 	* parser.c (cp_lexer_safe_previous_token): New function.

> > 	(cp_parser_error_1): Add name_hint if the previous token is

> > 	a string literal and next token is a CPP_NAME and we have a

> > 	missing header suggestion for the name.

> > 

> > gcc/testsuite/ChangeLog:

> > 

> > 	* g++.dg/spellcheck-inttypes.C: Add string-literal testcases.

> > ---

> >   gcc/c-family/known-headers.cc              |  8 +++++

> >   gcc/c-family/known-headers.h               |  1 +

> >   gcc/cp/parser.c                            | 36 ++++++++++++++++++++

> >   gcc/testsuite/g++.dg/spellcheck-inttypes.C | 39 ++++++++++++++++++++++

> >   4 files changed, 84 insertions(+)

> > 

> > diff --git a/gcc/c-family/known-headers.cc b/gcc/c-family/known-headers.cc

> > index c07cfd1db815..977230a586db 100644

> > --- a/gcc/c-family/known-headers.cc

> > +++ b/gcc/c-family/known-headers.cc

> > @@ -268,6 +268,14 @@ get_c_stdlib_header_for_string_macro_name (const char *name)

> >     return get_string_macro_hint (name, STDLIB_C);

> >   }

> > +/* Given non-NULL NAME, return the header name defining a string macro

> > +   within the C++ standard library (with '<' and '>'), or NULL.  */

> > +const char *

> > +get_cp_stdlib_header_for_string_macro_name (const char *name)

> > +{

> > +  return get_string_macro_hint (name, STDLIB_CPLUSPLUS);

> > +}

> > +

> >   /* Implementation of class suggest_missing_header.  */

> >   /* suggest_missing_header's ctor.  */

> > diff --git a/gcc/c-family/known-headers.h b/gcc/c-family/known-headers.h

> > index a69bbbf28e76..f0c89dc9019d 100644

> > --- a/gcc/c-family/known-headers.h

> > +++ b/gcc/c-family/known-headers.h

> > @@ -24,6 +24,7 @@ extern const char *get_c_stdlib_header_for_name (const char *name);

> >   extern const char *get_cp_stdlib_header_for_name (const char *name);

> >   extern const char *get_c_stdlib_header_for_string_macro_name (const char *n);

> > +extern const char *get_cp_stdlib_header_for_string_macro_name (const char *n);

> >   /* Subclass of deferred_diagnostic for suggesting to the user

> >      that they have missed a #include.  */

> > diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c

> > index 54ca875ce54c..95b8c635fc65 100644

> > --- a/gcc/cp/parser.c

> > +++ b/gcc/cp/parser.c

> > @@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see

> >   #include "tree-iterator.h"

> >   #include "cp-name-hint.h"

> >   #include "memmodel.h"

> > +#include "c-family/known-headers.h"

> >   

> >   /* The lexer.  */

> > @@ -776,6 +777,20 @@ cp_lexer_previous_token (cp_lexer *lexer)

> >     return cp_lexer_token_at (lexer, tp);

> >   }

> > +/* Same as above, but return NULL when the lexer doesn't own the token

> > +   buffer or if the next_token is at the start of the token

> > +   vector.  */

> > +

> > +static cp_token *

> > +cp_lexer_safe_previous_token (cp_lexer *lexer)

> > +{

> > +  if (lexer->buffer)

> > +    if (lexer->next_token != lexer->buffer->address ())

> > +      return cp_lexer_previous_token (lexer);

> > +

> > +  return NULL;

> > +}

> > +

> >   /* Overload for make_location, taking the lexer to mean the location of the

> >      previous token.  */

> > @@ -2919,6 +2934,7 @@ cp_parser_error_1 (cp_parser* parser, const char* gmsgid,

> >   	}

> >       }

> > +  auto_diagnostic_group d;

> >     gcc_rich_location richloc (input_location);

> >     bool added_matching_location = false;

> > @@ -2941,6 +2957,26 @@ cp_parser_error_1 (cp_parser* parser, const char* gmsgid,

> >   	  = richloc.add_location_if_nearby (matching_location);

> >       }

> > +  /* If we were parsing a string-literal and there is an unknown name

> > +     token right after, then check to see if that could also have been

> > +     a literal string by checking the name against a list of known

> > +     standard string literal constants defined in header files. If

> > +     there is one, then add that as an hint to the error message. */

> > +  name_hint h;

> > +  cp_token *prev_token = cp_lexer_safe_previous_token (parser->lexer);

> > +  if (prev_token && cp_parser_is_string_literal (prev_token)

> > +      && token->type == CPP_NAME)

> > +    {

> > +      tree name = token->u.value;

> > +      const char *token_name = IDENTIFIER_POINTER (name);

> > +      const char *header_hint

> > +	= get_cp_stdlib_header_for_string_macro_name (token_name);

> > +      if (header_hint != NULL)

> > +	h = name_hint (NULL, new suggest_missing_header (token->location,

> > +							 token_name,

> > +							 header_hint));

> > +    }

> > +

> >     /* Actually emit the error.  */

> >     c_parse_error (gmsgid,

> >   		 /* Because c_parser_error does not understand

> > diff --git a/gcc/testsuite/g++.dg/spellcheck-inttypes.C b/gcc/testsuite/g++.dg/spellcheck-inttypes.C

> > index c5861127ca6d..84bfc125513c 100644

> > --- a/gcc/testsuite/g++.dg/spellcheck-inttypes.C

> > +++ b/gcc/testsuite/g++.dg/spellcheck-inttypes.C

> > @@ -23,6 +23,18 @@ const char *hex64_fmt = PRIx64; /* { dg-error "'PRIx64' was not declared" "undec

> >   const char *hexptr_fmt = PRIxPTR; /* { dg-error "'PRIxPTR' was not declared" "undeclared identifier" { target *-*-* } } */

> >   /* { dg-message "'PRIxPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +/* As a part of a string-literal.  */

> > +const char *dec8msg_fmt = "Provide %" PRId8 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> > +/* { dg-message "'PRId8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +const char *dec16msg_fmt = "Provide %" PRId16 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> > +/* { dg-message "'PRId16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +const char *dec32msg_fmt = "Provide %" PRId32 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> > +/* { dg-message "'PRId32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +const char *dec64msg_fmt = "Provide %" PRId64 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> > +/* { dg-message "'PRId64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +const char *decptrmsg_fmt = "Provide %" PRIdPTR "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */

> > +/* { dg-message "'PRIdPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +

> >   void test_printf (void)

> >   {

> >     printf ("some format strings %s, %s, %s, %s, %s, %s\n",

> > @@ -38,4 +50,31 @@ void test_printf (void)

> >   /* { dg-message "'PRIx32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> >   	  PRIoPTR);  /* { dg-error "'PRIoPTR' was not declared" "undeclared identifier" { target *-*-* } } */

> >   /* { dg-message "'PRIoPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +

> > +  printf ("%" PRIo8 "\n", i8); /* { dg-error "expected" } */

> > +/* { dg-message "'PRIo8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +  printf ("%" PRIo16 "\n", i16); /* { dg-error "expected" } */

> > +/* { dg-message "'PRIo16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +  printf ("%" PRIo32 "\n", i32); /* { dg-error "expected" } */

> > +/* { dg-message "'PRIo32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +  printf ("%" PRIo64 "\n", i64); /* { dg-error "expected" } */

> > +/* { dg-message "'PRIo64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +  printf ("%" PRIoPTR "\n", ip); /* { dg-error "expected" } */

> > +/* { dg-message "'PRIoPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +}

> > +

> > +void test_scanf (void)

> > +{

> > +  scanf ("%" SCNu8 "\n", &i8); /* { dg-error "expected" } */

> > +/* { dg-message "'SCNu8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +  scanf ("%" SCNu16 "\n", &i16); /* { dg-error "expected" } */

> > +/* { dg-message "'SCNu16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +  scanf ("%" SCNu32 "\n", &i32); /* { dg-error "expected" } */

> > +/* { dg-message "'SCNu32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +  scanf ("%" SCNu64 "\n", &i64); /* { dg-error "expected" } */

> > +/* { dg-message "'SCNu64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +  scanf ("%" SCNuPTR "\n", &ip); /* { dg-error "expected" } */

> > +/* { dg-message "'SCNuPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> > +  scanf ("%" SCNxPTR "\n", &up); /* { dg-error "expected" } */

> > +/* { dg-message "'SCNxPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */

> >   }

> > 

>
Patrick Palka via Gcc-patches May 29, 2020, 12:36 p.m. | #3
On Fri, 2020-05-29 at 01:33 +0200, Mark Wielaard wrote:
> Hi,

> 

> On Mon, May 25, 2020 at 12:26:33PM -0400, Jason Merrill wrote:

> > On 5/23/20 8:30 PM, Mark Wielaard wrote:

> > > When reporting an error in cp_parser and we notice a string

> > > literal

> > > followed by an unknown name check whether there is a known

> > > standard

> > > header containing a string macro with the same name, then add a

> > > hint

> > > to the error message to include that header.

> > > 

> > > gcc/c-family/ChangeLog:

> > > 

> > > 	* known-headers.cc

> > > (get_cp_stdlib_header_for_string_macro_name):

> > > 	New function.

> > > 	* known-headers.h (get_c_stdlib_header_for_string_macro_name):

> > 

> > Missing 'p'.

> > 

> > > 	New function definition.

> > 

> > Declaration, not definition.

> > 

> > The C++ changes are OK with these ChangeLog corrections.

> 

> Thanks. David, are you OK with the diagnostic changes?


Yes.

> Who can we trick into reviewing the C frontend changes in the 1/2

> patch that this depends on?

> 

> Cheers,

> 

> Mark

Patch

diff --git a/gcc/c-family/known-headers.cc b/gcc/c-family/known-headers.cc
index c07cfd1db815..977230a586db 100644
--- a/gcc/c-family/known-headers.cc
+++ b/gcc/c-family/known-headers.cc
@@ -268,6 +268,14 @@  get_c_stdlib_header_for_string_macro_name (const char *name)
   return get_string_macro_hint (name, STDLIB_C);
 }
 
+/* Given non-NULL NAME, return the header name defining a string macro
+   within the C++ standard library (with '<' and '>'), or NULL.  */
+const char *
+get_cp_stdlib_header_for_string_macro_name (const char *name)
+{
+  return get_string_macro_hint (name, STDLIB_CPLUSPLUS);
+}
+
 /* Implementation of class suggest_missing_header.  */
 
 /* suggest_missing_header's ctor.  */
diff --git a/gcc/c-family/known-headers.h b/gcc/c-family/known-headers.h
index a69bbbf28e76..f0c89dc9019d 100644
--- a/gcc/c-family/known-headers.h
+++ b/gcc/c-family/known-headers.h
@@ -24,6 +24,7 @@  extern const char *get_c_stdlib_header_for_name (const char *name);
 extern const char *get_cp_stdlib_header_for_name (const char *name);
 
 extern const char *get_c_stdlib_header_for_string_macro_name (const char *n);
+extern const char *get_cp_stdlib_header_for_string_macro_name (const char *n);
 
 /* Subclass of deferred_diagnostic for suggesting to the user
    that they have missed a #include.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 54ca875ce54c..95b8c635fc65 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -45,6 +45,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "cp-name-hint.h"
 #include "memmodel.h"
+#include "c-family/known-headers.h"
 
 
 /* The lexer.  */
@@ -776,6 +777,20 @@  cp_lexer_previous_token (cp_lexer *lexer)
   return cp_lexer_token_at (lexer, tp);
 }
 
+/* Same as above, but return NULL when the lexer doesn't own the token
+   buffer or if the next_token is at the start of the token
+   vector.  */
+
+static cp_token *
+cp_lexer_safe_previous_token (cp_lexer *lexer)
+{
+  if (lexer->buffer)
+    if (lexer->next_token != lexer->buffer->address ())
+      return cp_lexer_previous_token (lexer);
+
+  return NULL;
+}
+
 /* Overload for make_location, taking the lexer to mean the location of the
    previous token.  */
 
@@ -2919,6 +2934,7 @@  cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
 	}
     }
 
+  auto_diagnostic_group d;
   gcc_rich_location richloc (input_location);
 
   bool added_matching_location = false;
@@ -2941,6 +2957,26 @@  cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
 	  = richloc.add_location_if_nearby (matching_location);
     }
 
+  /* If we were parsing a string-literal and there is an unknown name
+     token right after, then check to see if that could also have been
+     a literal string by checking the name against a list of known
+     standard string literal constants defined in header files. If
+     there is one, then add that as an hint to the error message. */
+  name_hint h;
+  cp_token *prev_token = cp_lexer_safe_previous_token (parser->lexer);
+  if (prev_token && cp_parser_is_string_literal (prev_token)
+      && token->type == CPP_NAME)
+    {
+      tree name = token->u.value;
+      const char *token_name = IDENTIFIER_POINTER (name);
+      const char *header_hint
+	= get_cp_stdlib_header_for_string_macro_name (token_name);
+      if (header_hint != NULL)
+	h = name_hint (NULL, new suggest_missing_header (token->location,
+							 token_name,
+							 header_hint));
+    }
+
   /* Actually emit the error.  */
   c_parse_error (gmsgid,
 		 /* Because c_parser_error does not understand
diff --git a/gcc/testsuite/g++.dg/spellcheck-inttypes.C b/gcc/testsuite/g++.dg/spellcheck-inttypes.C
index c5861127ca6d..84bfc125513c 100644
--- a/gcc/testsuite/g++.dg/spellcheck-inttypes.C
+++ b/gcc/testsuite/g++.dg/spellcheck-inttypes.C
@@ -23,6 +23,18 @@  const char *hex64_fmt = PRIx64; /* { dg-error "'PRIx64' was not declared" "undec
 const char *hexptr_fmt = PRIxPTR; /* { dg-error "'PRIxPTR' was not declared" "undeclared identifier" { target *-*-* } } */
 /* { dg-message "'PRIxPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
 
+/* As a part of a string-literal.  */
+const char *dec8msg_fmt = "Provide %" PRId8 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */
+/* { dg-message "'PRId8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+const char *dec16msg_fmt = "Provide %" PRId16 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */
+/* { dg-message "'PRId16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+const char *dec32msg_fmt = "Provide %" PRId32 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */
+/* { dg-message "'PRId32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+const char *dec64msg_fmt = "Provide %" PRId64 "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */
+/* { dg-message "'PRId64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+const char *decptrmsg_fmt = "Provide %" PRIdPTR "\n"; /* { dg-error "expected" "expected string-literal" { target *-*-* } } */
+/* { dg-message "'PRIdPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+
 void test_printf (void)
 {
   printf ("some format strings %s, %s, %s, %s, %s, %s\n",
@@ -38,4 +50,31 @@  void test_printf (void)
 /* { dg-message "'PRIx32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
 	  PRIoPTR);  /* { dg-error "'PRIoPTR' was not declared" "undeclared identifier" { target *-*-* } } */
 /* { dg-message "'PRIoPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+
+  printf ("%" PRIo8 "\n", i8); /* { dg-error "expected" } */
+/* { dg-message "'PRIo8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+  printf ("%" PRIo16 "\n", i16); /* { dg-error "expected" } */
+/* { dg-message "'PRIo16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+  printf ("%" PRIo32 "\n", i32); /* { dg-error "expected" } */
+/* { dg-message "'PRIo32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+  printf ("%" PRIo64 "\n", i64); /* { dg-error "expected" } */
+/* { dg-message "'PRIo64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+  printf ("%" PRIoPTR "\n", ip); /* { dg-error "expected" } */
+/* { dg-message "'PRIoPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+}
+
+void test_scanf (void)
+{
+  scanf ("%" SCNu8 "\n", &i8); /* { dg-error "expected" } */
+/* { dg-message "'SCNu8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+  scanf ("%" SCNu16 "\n", &i16); /* { dg-error "expected" } */
+/* { dg-message "'SCNu16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+  scanf ("%" SCNu32 "\n", &i32); /* { dg-error "expected" } */
+/* { dg-message "'SCNu32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+  scanf ("%" SCNu64 "\n", &i64); /* { dg-error "expected" } */
+/* { dg-message "'SCNu64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+  scanf ("%" SCNuPTR "\n", &ip); /* { dg-error "expected" } */
+/* { dg-message "'SCNuPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
+  scanf ("%" SCNxPTR "\n", &up); /* { dg-error "expected" } */
+/* { dg-message "'SCNxPTR' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
 }