On Mon, 2016-06-06 at 15:17 +0000, Joseph Myers wrote: > On Tue, 31 May 2016, David Malcolm wrote: > > > Ping: > > https://gcc.gnu.org/ml/gcc-patches/2016-04/msg01834.html > > OK. What about field names in designated initializers (both C99 > -style and > old-style)?
This patch adds fixits for named initializers, both old-style, and C99-style. I noticed that the existing error message: "unknown field %qE specified in initializer" didn't contain the type name, so I changed it to: "%qT has no member named %qE" to specify this (and for consistency with other messages). Successfully bootstrapped®rtested on x86_64-pc-linux-gnu. OK for trunk? gcc/c/ChangeLog: * c-parser.c (c_parser_initelt): Provide location of name for new location_t param of set_init_label. * c-tree.h (set_init_label): Add location_t param. * c-typeck.c (set_init_index): Add "fieldname_loc" location_t param and use it when issuing error messages about unrecognized field names. Attempt to provide a fixit hint if appropriate, otherwise update the error message to provide the type name. gcc/testsuite/ChangeLog: * gcc.dg/c99-init-2.c (c): Update expected error message. * gcc.dg/init-bad-8.c (foo): Likewise. * gcc.dg/spellcheck-fields-3.c: New test case. --- gcc/c/c-parser.c | 2 + gcc/c/c-tree.h | 2 +- gcc/c/c-typeck.c | 21 +++++++++- gcc/testsuite/gcc.dg/c99-init-2.c | 2 +- gcc/testsuite/gcc.dg/init-bad-8.c | 2 +- gcc/testsuite/gcc.dg/spellcheck-fields-3.c | 66 ++++++++++++++++++++++++++++++ 6 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/spellcheck-fields-3.c diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 2fef1ac..5974ebb 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -4397,6 +4397,7 @@ c_parser_initelt (c_parser *parser, struct obstack * braced_init_obstack) /* Old-style structure member designator. */ set_init_label (c_parser_peek_token (parser)->location, c_parser_peek_token (parser)->value, + c_parser_peek_token (parser)->location, braced_init_obstack); /* Use the colon as the error location. */ pedwarn (c_parser_peek_2nd_token (parser)->location, OPT_Wpedantic, @@ -4426,6 +4427,7 @@ c_parser_initelt (c_parser *parser, struct obstack * braced_init_obstack) if (c_parser_next_token_is (parser, CPP_NAME)) { set_init_label (des_loc, c_parser_peek_token (parser)->value, + c_parser_peek_token (parser)->location, braced_init_obstack); c_parser_consume_token (parser); } diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index b4374e3..8f10a13 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -639,7 +639,7 @@ extern void finish_implicit_inits (location_t, struct obstack *); extern void push_init_level (location_t, int, struct obstack *); extern struct c_expr pop_init_level (location_t, int, struct obstack *); extern void set_init_index (location_t, tree, tree, struct obstack *); -extern void set_init_label (location_t, tree, struct obstack *); +extern void set_init_label (location_t, tree, location_t, struct obstack *); extern void process_init_element (location_t, struct c_expr, bool, struct obstack *); extern tree build_compound_literal (location_t, tree, tree, bool); diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index cd8e9e5..8dea62b 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -8200,7 +8200,7 @@ set_init_index (location_t loc, tree first, tree last, /* Within a struct initializer, specify the next field to be initialized. */ void -set_init_label (location_t loc, tree fieldname, +set_init_label (location_t loc, tree fieldname, location_t fieldname_loc, struct obstack *braced_init_obstack) { tree field; @@ -8219,7 +8219,24 @@ set_init_label (location_t loc, tree fieldname, field = lookup_field (constructor_type, fieldname); if (field == 0) - error_at (loc, "unknown field %qE specified in initializer", fieldname); + { + tree guessed_id = lookup_field_fuzzy (constructor_type, fieldname); + if (guessed_id) + { + rich_location rich_loc (line_table, fieldname_loc); + source_range component_range = + get_range_from_loc (line_table, fieldname_loc); + rich_loc.add_fixit_replace (component_range, + IDENTIFIER_POINTER (guessed_id)); + error_at_rich_loc + (&rich_loc, + "%qT has no member named %qE; did you mean %qE?", + constructor_type, fieldname, guessed_id); + } + else + error_at (fieldname_loc, "%qT has no member named %qE", + constructor_type, fieldname); + } else do { diff --git a/gcc/testsuite/gcc.dg/c99-init-2.c b/gcc/testsuite/gcc.dg/c99-init-2.c index d3a331f..c07005b 100644 --- a/gcc/testsuite/gcc.dg/c99-init-2.c +++ b/gcc/testsuite/gcc.dg/c99-init-2.c @@ -9,7 +9,7 @@ typedef struct { } A; A a = { [2] = 1 }; /* { dg-error "(array index in non-array)|(near initialization)" } */ int b[] = { .B = 1 }; /* { dg-error "(field name not in record)|(near initialization)" } */ -A c[] = { [0].D = 1 }; /* { dg-error "unknown field" } */ +A c[] = { [0].D = 1 }; /* { dg-error "15: has no member named .D." } */ int d; int e = { d++ }; /* { dg-error "(is not constant)|(near initialization)" } */ A f[2] = { [0].C[0] = 1, [2] = { 2, { 1, 2 } } };/* { dg-error "(array index in initializer exceeds array bounds)|(near initialization)" } */ diff --git a/gcc/testsuite/gcc.dg/init-bad-8.c b/gcc/testsuite/gcc.dg/init-bad-8.c index b321323..f7b0f7f 100644 --- a/gcc/testsuite/gcc.dg/init-bad-8.c +++ b/gcc/testsuite/gcc.dg/init-bad-8.c @@ -6,5 +6,5 @@ struct S { int i, j, k; }; void foo (void) { - struct S s = { .i = 1, .j = 2, .l = 4}; /* { dg-error "34:unknown field .l. specified in initializer" } */ + struct S s = { .i = 1, .j = 2, .l = 4}; /* { dg-error "35: .struct S. has no member named .l." } */ } diff --git a/gcc/testsuite/gcc.dg/spellcheck-fields-3.c b/gcc/testsuite/gcc.dg/spellcheck-fields-3.c new file mode 100644 index 0000000..003a0b5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/spellcheck-fields-3.c @@ -0,0 +1,66 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-show-caret -std=c99" } */ + +/* Tests of incorrect name initializers. + Verify that we get underlines and, where appropriate, fixit hints. */ + +struct foo +{ + int foo; + int bar; +}; + +union u +{ + int color; + int shape; +}; + +/* Old-style named initializers. */ + +struct foo old_style_f = { + foa: 1, /* { dg-error ".struct foo. has no member named .foa.; did you mean .foo." } */ +/* { dg-begin-multiline-output "" } + foa: 1, + ^~~ + foo + { dg-end-multiline-output "" } */ + + this_does_not_match: 3 /* { dg-error ".struct foo. has no member named .this_does_not_match." } */ + +/* { dg-begin-multiline-output "" } + this_does_not_match: 3 + ^~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +}; + +union u old_style_u = { colour: 3 }; /* { dg-error ".union u. has no member named .colour.; did you mean .color.?" } */ +/* { dg-begin-multiline-output "" } + union u old_style_u = { colour: 3 }; + ^~~~~~ + color + { dg-end-multiline-output "" } */ + +/* C99-style named initializers. */ + +struct foo c99_style_f = { + .foa = 1, /* { dg-error ".struct foo. has no member named .foa.; did you mean .foo." } */ +/* { dg-begin-multiline-output "" } + .foa = 1, + ^~~ + foo + { dg-end-multiline-output "" } */ + + .this_does_not_match = 3 /* { dg-error ".struct foo. has no member named .this_does_not_match." } */ +/* { dg-begin-multiline-output "" } + .this_does_not_match = 3 + ^~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +}; + +union u c99_style_u = { .colour=3 }; /* { dg-error ".union u. has no member named .colour.; did you mean .color.?" } */ +/* { dg-begin-multiline-output "" } + union u c99_style_u = { .colour=3 }; + ^~~~~~ + color + { dg-end-multiline-output "" } */ -- 1.8.5.3