Provide diagnostic hints for missing C inttypes.h string constants.
This adds a flag to c_parser so we know when we were trying to construct a string literal. If there is a parse error and we were constructing a string literal, and the next token is an unknown identifier name, and we know there is a standard header that defines that name as a string literal, then add a missing header hint to the error messsage. The list of macro names are also used when providing a hint for missing identifiers. gcc/c-family/ChangeLog: * known-headers.cc (get_string_macro_hint): New function. (get_stdlib_header_for_name): Use get_string_macro_hint. (get_c_stdlib_header_for_string_macro_name): New function. * known-headers.h (get_c_stdlib_header_for_string_macro_name): New function declaration. gcc/c/ChangeLog: * c-parser.c (struct c_parser): Add seen_string_literal bitfield. (c_parser_consume_token): Reset seen_string_literal. (c_parser_error_richloc): Add name_hint if seen_string_literal and next token is a CPP_NAME and we have a missing header suggestion for the name. (c_parser_string_literal): Set seen_string_literal. gcc/testsuite/ChangeLog: * gcc.dg/spellcheck-inttypes.c: New test. * g++.dg/spellcheck-inttypes.C: Likewise.
This commit is contained in:
parent
34e4962aed
commit
9eea5d2ddf
|
@ -46,6 +46,49 @@ struct stdlib_hint
|
|||
const char *header[NUM_STDLIBS];
|
||||
};
|
||||
|
||||
/* Given non-NULL NAME, return the header name defining it (as literal
|
||||
string) within either the standard library (with '<' and '>'), or
|
||||
NULL.
|
||||
|
||||
Only handle string macros, so that this can be used for
|
||||
get_stdlib_header_for_name and
|
||||
get_c_stdlib_header_for_string_macro_name. */
|
||||
|
||||
static const char *
|
||||
get_string_macro_hint (const char *name, enum stdlib lib)
|
||||
{
|
||||
/* <inttypes.h> and <cinttypes>. */
|
||||
static const char *c99_cxx11_macros[] =
|
||||
{ "PRId8", "PRId16", "PRId32", "PRId64",
|
||||
"PRIi8", "PRIi16", "PRIi32", "PRIi64",
|
||||
"PRIo8", "PRIo16", "PRIo32", "PRIo64",
|
||||
"PRIu8", "PRIu16", "PRIu32", "PRIu64",
|
||||
"PRIx8", "PRIx16", "PRIx32", "PRIx64",
|
||||
"PRIX8", "PRIX16", "PRIX32", "PRIX64",
|
||||
|
||||
"PRIdPTR", "PRIiPTR", "PRIoPTR", "PRIuPTR", "PRIxPTR", "PRIXPTR",
|
||||
|
||||
"SCNd8", "SCNd16", "SCNd32", "SCNd64",
|
||||
"SCNi8", "SCNi16", "SCNi32", "SCNi64",
|
||||
"SCNo8", "SCNo16", "SCNo32", "SCNo64",
|
||||
"SCNu8", "SCNu16", "SCNu32", "SCNu64",
|
||||
"SCNx8", "SCNx16", "SCNx32", "SCNx64",
|
||||
|
||||
"SCNdPTR", "SCNiPTR", "SCNoPTR", "SCNuPTR", "SCNxPTR" };
|
||||
|
||||
if ((lib == STDLIB_C && flag_isoc99)
|
||||
|| (lib == STDLIB_CPLUSPLUS && cxx_dialect >= cxx11 ))
|
||||
{
|
||||
const size_t num_c99_cxx11_macros
|
||||
= sizeof (c99_cxx11_macros) / sizeof (c99_cxx11_macros[0]);
|
||||
for (size_t i = 0; i < num_c99_cxx11_macros; i++)
|
||||
if (strcmp (name, c99_cxx11_macros[i]) == 0)
|
||||
return lib == STDLIB_C ? "<inttypes.h>" : "<cinttypes>";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Given non-NULL NAME, return the header name defining it within either
|
||||
the standard library (with '<' and '>'), or NULL.
|
||||
Only handles a subset of the most common names within the stdlibs. */
|
||||
|
@ -196,7 +239,7 @@ get_stdlib_header_for_name (const char *name, enum stdlib lib)
|
|||
if (strcmp (name, c99_cxx11_hints[i].name) == 0)
|
||||
return c99_cxx11_hints[i].header[lib];
|
||||
|
||||
return NULL;
|
||||
return get_string_macro_hint (name, lib);
|
||||
}
|
||||
|
||||
/* Given non-NULL NAME, return the header name defining it within the C
|
||||
|
@ -217,6 +260,14 @@ get_cp_stdlib_header_for_name (const char *name)
|
|||
return get_stdlib_header_for_name (name, STDLIB_CPLUSPLUS);
|
||||
}
|
||||
|
||||
/* Given non-NULL NAME, return the header name defining a string macro
|
||||
within the C standard library (with '<' and '>'), or NULL. */
|
||||
const char *
|
||||
get_c_stdlib_header_for_string_macro_name (const char *name)
|
||||
{
|
||||
return get_string_macro_hint (name, STDLIB_C);
|
||||
}
|
||||
|
||||
/* Implementation of class suggest_missing_header. */
|
||||
|
||||
/* suggest_missing_header's ctor. */
|
||||
|
|
|
@ -23,6 +23,8 @@ along with GCC; see the file COPYING3. If not see
|
|||
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);
|
||||
|
||||
/* Subclass of deferred_diagnostic for suggesting to the user
|
||||
that they have missed a #include. */
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "c-family/name-hint.h"
|
||||
#include "tree-iterator.h"
|
||||
#include "memmodel.h"
|
||||
#include "c-family/known-headers.h"
|
||||
|
||||
/* We need to walk over decls with incomplete struct/union/enum types
|
||||
after parsing the whole translation unit.
|
||||
|
@ -223,6 +224,13 @@ struct GTY(()) c_parser {
|
|||
keywords are valid. */
|
||||
BOOL_BITFIELD objc_property_attr_context : 1;
|
||||
|
||||
/* Whether we have just seen/constructed a string-literal. Set when
|
||||
returning a string-literal from c_parser_string_literal. Reset
|
||||
in consume_token. Useful when we get a parse error and see an
|
||||
unknown token, which could have been a string-literal constant
|
||||
macro. */
|
||||
BOOL_BITFIELD seen_string_literal : 1;
|
||||
|
||||
/* Location of the last consumed token. */
|
||||
location_t last_token_location;
|
||||
};
|
||||
|
@ -853,6 +861,7 @@ c_parser_consume_token (c_parser *parser)
|
|||
}
|
||||
}
|
||||
parser->tokens_avail--;
|
||||
parser->seen_string_literal = false;
|
||||
}
|
||||
|
||||
/* Expect the current token to be a #pragma. Consume it and remember
|
||||
|
@ -966,6 +975,25 @@ c_parser_error_richloc (c_parser *parser, const char *gmsgid,
|
|||
}
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
auto_diagnostic_group d;
|
||||
name_hint h;
|
||||
if (parser->seen_string_literal && token->type == CPP_NAME)
|
||||
{
|
||||
tree name = token->value;
|
||||
const char *token_name = IDENTIFIER_POINTER (name);
|
||||
const char *header_hint
|
||||
= get_c_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));
|
||||
}
|
||||
|
||||
c_parse_error (gmsgid,
|
||||
/* Because c_parse_error does not understand
|
||||
CPP_KEYWORD, keywords are treated like
|
||||
|
@ -7539,6 +7567,7 @@ c_parser_string_literal (c_parser *parser, bool translate, bool wide_ok)
|
|||
ret.original_code = STRING_CST;
|
||||
ret.original_type = NULL_TREE;
|
||||
set_c_expr_source_range (&ret, get_range_from_loc (line_table, loc));
|
||||
parser->seen_string_literal = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/* { dg-options "-std=c++11" } */
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
/* Missing <cinttypes>. */
|
||||
|
||||
int8_t i8;
|
||||
int16_t i16;
|
||||
int32_t i32;
|
||||
int64_t i64;
|
||||
|
||||
intptr_t ip;
|
||||
uintptr_t up;
|
||||
|
||||
/* As an identifier. */
|
||||
const char *hex8_fmt = PRIx8; /* { dg-error "'PRIx8' was not declared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIx8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
const char *hex16_fmt = PRIx16; /* { dg-error "'PRIx16' was not declared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIx16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
const char *hex32_fmt = PRIx32; /* { dg-error "'PRIx32' was not declared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIx32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
const char *hex64_fmt = PRIx64; /* { dg-error "'PRIx64' was not declared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIx64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
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 } */
|
||||
|
||||
void test_printf (void)
|
||||
{
|
||||
printf ("some format strings %s, %s, %s, %s, %s, %s\n",
|
||||
PRId8, /* { dg-error "'PRId8' was not declared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRId8' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
PRIi16, /* { dg-error "'PRIi16' was not declared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIi16' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
PRIo32, /* { dg-error "'PRIo32' was not declared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIo32' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
PRIu64, /* { dg-error "'PRIu64' was not declared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIu64' is defined in header '<cinttypes>'; did you forget to '#include <cinttypes>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
PRIx32, /* { dg-error "'PRIx32' was not declared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { 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 } */
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/* { dg-options "-std=c99" } */
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
/* Missing <inttypes.h>. */
|
||||
|
||||
int8_t i8;
|
||||
int16_t i16;
|
||||
int32_t i32;
|
||||
int64_t i64;
|
||||
|
||||
intptr_t ip;
|
||||
uintptr_t up;
|
||||
|
||||
/* As an identifier. */
|
||||
const char *hex8_fmt = PRIx8; /* { dg-error "'PRIx8' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIx8' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
const char *hex16_fmt = PRIx16; /* { dg-error "'PRIx16' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIx16' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
const char *hex32_fmt = PRIx32; /* { dg-error "'PRIx32' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIx32' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
const char *hex64_fmt = PRIx64; /* { dg-error "'PRIx64' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIx64' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
const char *hexptr_fmt = PRIxPTR; /* { dg-error "'PRIxPTR' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIxPTR' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "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 '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "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 '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "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 '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "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 '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "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 '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
|
||||
void test_printf (void)
|
||||
{
|
||||
printf ("some format strings %s, %s, %s, %s, %s, %s\n",
|
||||
PRId8, /* { dg-error "'PRId8' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRId8' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
PRIi16, /* { dg-error "'PRIi16' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIi16' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
PRIo32, /* { dg-error "'PRIo32' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIo32' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
PRIu64, /* { dg-error "'PRIu64' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIu64' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
PRIoPTR); /* { dg-error "'PRIoPTR' undeclared" "undeclared identifier" { target *-*-* } } */
|
||||
/* { dg-message "'PRIoPTR' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
|
||||
printf ("%" PRIo8 "\n", i8); /* { dg-error "expected" } */
|
||||
/* { dg-message "'PRIo8' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
printf ("%" PRIo16 "\n", i16); /* { dg-error "expected" } */
|
||||
/* { dg-message "'PRIo16' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
printf ("%" PRIo32 "\n", i32); /* { dg-error "expected" } */
|
||||
/* { dg-message "'PRIo32' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
printf ("%" PRIo64 "\n", i64); /* { dg-error "expected" } */
|
||||
/* { dg-message "'PRIo64' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
printf ("%" PRIoPTR "\n", ip); /* { dg-error "expected" } */
|
||||
/* { dg-message "'PRIoPTR' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
}
|
||||
|
||||
void test_scanf (void)
|
||||
{
|
||||
scanf ("%" SCNu8 "\n", &i8); /* { dg-error "expected" } */
|
||||
/* { dg-message "'SCNu8' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
scanf ("%" SCNu16 "\n", &i16); /* { dg-error "expected" } */
|
||||
/* { dg-message "'SCNu16' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
scanf ("%" SCNu32 "\n", &i32); /* { dg-error "expected" } */
|
||||
/* { dg-message "'SCNu32' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
scanf ("%" SCNu64 "\n", &i64); /* { dg-error "expected" } */
|
||||
/* { dg-message "'SCNu64' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
scanf ("%" SCNuPTR "\n", &ip); /* { dg-error "expected" } */
|
||||
/* { dg-message "'SCNuPTR' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
scanf ("%" SCNxPTR "\n", &up); /* { dg-error "expected" } */
|
||||
/* { dg-message "'SCNxPTR' is defined in header '<inttypes.h>'; did you forget to '#include <inttypes.h>'?" "replacement note" { target *-*-* } .-1 } */
|
||||
}
|
Loading…
Reference in New Issue