From 2c474893871c5684f8578a65ac1e24c37eef526d Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Thu, 27 Apr 2017 10:14:47 -0400 Subject: [PATCH 2/6] FIXME: v2: C++: Add fix-it hints for -Wold-style-cast Changes since v1: - updated expected fixit-formatting - added new test cases as suggested by Florian - use "tf_none" rather than "0 /* complain */" Blurb from v1 of patch follows: Volker's patch for -Wold-style-cast inspired me to add a fix-it hint for when this warning fires. The fix-it hints print in a rather odd way, as seen in the test case (which is arguably a bug in diagnostic-show-locus.c), but the patch generated with -fdiagnostics-generate-patch shows that they do the right thing: void test_1 (void *ptr) { - foo *f = (foo *)ptr; + foo *f = static_cast (ptr); } void test_2 (const foo *ptr) { - foo *f = (foo *)ptr; + foo *f = const_cast (ptr); } void test_3 (bar *ptr) { - foo *f = (foo *)ptr; + foo *f = reinterpret_cast (ptr); } void test_4 (bar *ptr) { - foo *f = (foo *)ptr->field; + foo *f = const_cast (ptr->field); } void test_5 () { bar b_inst; - foo *f = (foo *)&b_inst; + foo *f = reinterpret_cast (&b_inst); } gcc/cp/ChangeLog: * parser.c (get_cast_suggestion): New function. (maybe_add_cast_fixit): New function. (cp_parser_cast_expression): Capture the location of the closing parenthesis. Call maybe_add_cast_fixit when emitting warnings about old-style casts. gcc/testsuite/ChangeLog: * g++.dg/other/old-style-cast-fixits.C: New test case. --- gcc/cp/parser.c | 94 ++++++++++++++++++++- gcc/testsuite/g++.dg/other/old-style-cast-fixits.C | 95 ++++++++++++++++++++++ 2 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/other/old-style-cast-fixits.C diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 313eebb..5433111 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -8636,6 +8636,85 @@ cp_parser_tokens_start_cast_expression (cp_parser *parser) } } +/* Try to find a legal C++-style cast to DST_TYPE for ORIG_EXPR, trying them + in the order: const_cast, static_cast, reinterpret_cast. + + Don't suggest dynamic_cast. + + Return the first legal cast kind found, or NULL otherwise. */ + +static const char * +get_cast_suggestion (tree dst_type, tree orig_expr) +{ + tree trial; + + /* Reuse the parser logic by attempting to build the various kinds of + cast, with "complain" disabled. + Identify the first such cast that is valid. */ + + /* Don't attempt to run such logic within template processing. */ + if (processing_template_decl) + return NULL; + + /* First try const_cast. */ + trial = build_const_cast (dst_type, orig_expr, tf_none); + if (trial != error_mark_node) + return "const_cast"; + + /* If that fails, try static_cast. */ + trial = build_static_cast (dst_type, orig_expr, tf_none); + if (trial != error_mark_node) + return "static_cast"; + + /* Finally, try reinterpret_cast. */ + trial = build_reinterpret_cast (dst_type, orig_expr, tf_none); + if (trial != error_mark_node) + return "reinterpret_cast"; + + /* No such cast possible. */ + return NULL; +} + +/* If -Wold-style-cast is enabled, add fix-its to RICHLOC, + suggesting how to convert a C-style cast of the form: + + (DST_TYPE)ORIG_EXPR + + to a C++-style cast. + + The primary range of RICHLOC is asssumed to be that of the original + expression. OPEN_PAREN_LOC and CLOSE_PAREN_LOC give the locations + of the parens in the C-style cast. */ + +static void +maybe_add_cast_fixit (rich_location *rich_loc, location_t open_paren_loc, + location_t close_paren_loc, tree orig_expr, + tree dst_type) +{ + /* This function is non-trivial, so bail out now if the warning isn't + going to be emitted. */ + if (!warn_old_style_cast) + return; + + /* Try to find a legal C++ cast, trying them in order: + const_cast, static_cast, reinterpret_cast. */ + const char *cast_suggestion = get_cast_suggestion (dst_type, orig_expr); + if (!cast_suggestion) + return; + + /* Replace the open paren with "CAST_SUGGESTION<". */ + pretty_printer pp; + pp_printf (&pp, "%s<", cast_suggestion); + rich_loc->add_fixit_replace (open_paren_loc, pp_formatted_text (&pp)); + + /* Replace the close paren with "> (". */ + rich_loc->add_fixit_replace (close_paren_loc, "> ("); + + /* Add a closing paren after the expr (the primary range of RICH_LOC). */ + rich_loc->add_fixit_insert_after (")"); +} + + /* Parse a cast-expression. cast-expression: @@ -8671,6 +8750,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, /* Consume the `('. */ cp_token *open_paren = cp_lexer_consume_token (parser->lexer); location_t open_paren_loc = open_paren->location; + location_t close_paren_loc = UNKNOWN_LOCATION; /* A very tricky bit is that `(struct S) { 3 }' is a compound-literal (which we permit in C++ as an extension). @@ -8733,7 +8813,10 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, /* Look for the type-id. */ type = cp_parser_type_id (parser); /* Look for the closing `)'. */ - cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN); + cp_token *close_paren + = cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN); + if (close_paren) + close_paren_loc = close_paren->location; parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; } @@ -8763,8 +8846,13 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, && !in_system_header_at (input_location) && !VOID_TYPE_P (type) && current_lang_name != lang_name_c) - warning (OPT_Wold_style_cast, - "use of old-style cast to %qT", type); + { + gcc_rich_location rich_loc (input_location); + maybe_add_cast_fixit (&rich_loc, open_paren_loc, close_paren_loc, + expr, type); + warning_at_rich_loc (&rich_loc, OPT_Wold_style_cast, + "use of old-style cast to %qT", type); + } /* Only type conversions to integral or enumeration types can be used in constant-expressions. */ diff --git a/gcc/testsuite/g++.dg/other/old-style-cast-fixits.C b/gcc/testsuite/g++.dg/other/old-style-cast-fixits.C new file mode 100644 index 0000000..a10b623 --- /dev/null +++ b/gcc/testsuite/g++.dg/other/old-style-cast-fixits.C @@ -0,0 +1,95 @@ +// { dg-options "-Wold-style-cast -fdiagnostics-show-caret" } + +struct foo {}; +struct bar { const foo *field; }; + +void test_1 (void *ptr) +{ + foo *f = (foo *)ptr; // { dg-warning "old-style cast" } + /* { dg-begin-multiline-output "" } + foo *f = (foo *)ptr; + ^~~ + ---------- + static_cast (ptr) + { dg-end-multiline-output "" } */ +} + +void test_2 (const foo *ptr) +{ + foo *f = (foo *)ptr; // { dg-warning "old-style cast" } + /* { dg-begin-multiline-output "" } + foo *f = (foo *)ptr; + ^~~ + ---------- + const_cast (ptr) + { dg-end-multiline-output "" } */ +} + +void test_3 (bar *ptr) +{ + foo *f = (foo *)ptr; // { dg-warning "old-style cast" } + /* { dg-begin-multiline-output "" } + foo *f = (foo *)ptr; + ^~~ + ---------- + reinterpret_cast (ptr) + { dg-end-multiline-output "" } */ +} + +void test_4 (bar *ptr) +{ + foo *f = (foo *)ptr->field; // { dg-warning "old-style cast" } + /* { dg-begin-multiline-output "" } + foo *f = (foo *)ptr->field; + ^~~~~ + ----------------- + const_cast (ptr->field) + { dg-end-multiline-output "" } */ +} + +void test_5 () +{ + bar b_inst; + foo *f = (foo *)&b_inst; // { dg-warning "old-style cast" } + /* { dg-begin-multiline-output "" } + foo *f = (foo *)&b_inst; + ^~~~~~ + -------------- + reinterpret_cast (&b_inst) + { dg-end-multiline-output "" } */ +} + +/* We don't offer suggestions for templates. */ + +template +void test_6 (void *ptr) +{ + foo *f = (foo *)ptr; // { dg-warning "old-style cast" } + /* { dg-begin-multiline-output "" } + foo *f = (foo *)ptr; + ^~~ + { dg-end-multiline-output "" } */ +} + +/* We don't offer suggestions where a single C++-style cast can't be + used. */ + +void test_7 (const void *ptr) +{ + foo *f = (foo *)ptr; // { dg-warning "old-style cast" } + /* { dg-begin-multiline-output "" } + foo *f = (foo *)ptr; + ^~~ + { dg-end-multiline-output "" } */ +} + +/* Likewise, no single C++-style cast is usable here. */ + +void test_8 (const bar &b_inst) +{ + foo *f = (foo *)&b_inst; // { dg-warning "old-style cast" } + /* { dg-begin-multiline-output "" } + foo *f = (foo *)&b_inst; + ^~~~~~ + { dg-end-multiline-output "" } */ +} -- 1.8.5.3