From 1dc83b460653c29f96b4659579e2151fae0d1e6e Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Sun, 24 May 2020 00:44:22 +0200 Subject: [PATCH] Provide diagnostic hints for missing C++ cinttypes string constants. 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 | 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 c07cfd1db81..977230a586d 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 a69bbbf28e7..f0c89dc9019 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 74c40efd936..b0b31d241f3 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 c5861127ca6..84bfc125513 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 ''; did you forget to '#include '?" "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 ''; did you forget to '#include '?" "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 ''; did you forget to '#include '?" "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 ''; did you forget to '#include '?" "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 ''; did you forget to '#include '?" "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 ''; did you forget to '#include '?" "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 ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ PRIoPTR); /* { dg-error "'PRIoPTR' was not declared" "undeclared identifier" { target *-*-* } } */ /* { dg-message "'PRIoPTR' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + + printf ("%" PRIo8 "\n", i8); /* { dg-error "expected" } */ +/* { dg-message "'PRIo8' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + printf ("%" PRIo16 "\n", i16); /* { dg-error "expected" } */ +/* { dg-message "'PRIo16' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + printf ("%" PRIo32 "\n", i32); /* { dg-error "expected" } */ +/* { dg-message "'PRIo32' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + printf ("%" PRIo64 "\n", i64); /* { dg-error "expected" } */ +/* { dg-message "'PRIo64' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + printf ("%" PRIoPTR "\n", ip); /* { dg-error "expected" } */ +/* { dg-message "'PRIoPTR' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ +} + +void test_scanf (void) +{ + scanf ("%" SCNu8 "\n", &i8); /* { dg-error "expected" } */ +/* { dg-message "'SCNu8' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + scanf ("%" SCNu16 "\n", &i16); /* { dg-error "expected" } */ +/* { dg-message "'SCNu16' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + scanf ("%" SCNu32 "\n", &i32); /* { dg-error "expected" } */ +/* { dg-message "'SCNu32' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + scanf ("%" SCNu64 "\n", &i64); /* { dg-error "expected" } */ +/* { dg-message "'SCNu64' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + scanf ("%" SCNuPTR "\n", &ip); /* { dg-error "expected" } */ +/* { dg-message "'SCNuPTR' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ + scanf ("%" SCNxPTR "\n", &up); /* { dg-error "expected" } */ +/* { dg-message "'SCNxPTR' is defined in header ''; did you forget to '#include '?" "replacement note" { target *-*-* } .-1 } */ } -- 2.30.2