Provide diagnostic hints for missing C++ cinttypes string constants.
authorMark Wielaard <mark@klomp.org>
Sat, 23 May 2020 22:44:22 +0000 (00:44 +0200)
committerMark Wielaard <mark@klomp.org>
Wed, 3 Jun 2020 23:22:53 +0000 (01:22 +0200)
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_cp_stdlib_header_for_string_macro_name):
New function declaration.

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
gcc/c-family/known-headers.h
gcc/cp/parser.c
gcc/testsuite/g++.dg/spellcheck-inttypes.C

index c07cfd1db815533acd6e8f075a5979316251c284..977230a586db41e4d1694507a5cd764c44de0860 100644 (file)
@@ -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.  */
index a69bbbf28e7649ba081cee6e19bed392429a1ff5..f0c89dc9019d622e63ce372a48e114ed04bb56ef 100644 (file)
@@ -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.  */
index 74c40efd936c940ea46c0c30c62f411e38908a8e..b0b31d241f3d8970612717e7e7be9226f07f725c 100644 (file)
@@ -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"
 
 \f
 /* 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
index c5861127ca6dc5a4a94cab131c904b2a0463420e..84bfc125513c456a3d2a4c05510d03c0df76f446 100644 (file)
@@ -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 } */
 }