From 2c474893871c5684f8578a65ac1e24c37eef526d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
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<foo *> (ptr);
   }

   void test_2 (const foo *ptr)
   {
  -  foo *f = (foo *)ptr;
  +  foo *f = const_cast<foo *> (ptr);
   }

   void test_3 (bar *ptr)
   {
  -  foo *f = (foo *)ptr;
  +  foo *f = reinterpret_cast<foo *> (ptr);
   }

   void test_4 (bar *ptr)
   {
  -  foo *f = (foo *)ptr->field;
  +  foo *f = const_cast<foo *> (ptr->field);
   }

   void test_5 ()
   {
     bar b_inst;
  -  foo *f = (foo *)&b_inst;
  +  foo *f = reinterpret_cast<foo *> (&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<foo *> (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<foo *> (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<foo *> (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<foo *> (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<foo *> (&b_inst)
+     { dg-end-multiline-output "" } */
+}
+
+/* We don't offer suggestions for templates.  */
+
+template <typename T>
+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

