From e4e746958288711bd750b2b4cd46e776e0bf02ac Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 13 Sep 2016 15:42:36 -0400
Subject: [PATCH 15/44] FIXME: WIP on support for newlines in fixits

---
 gcc/diagnostic-show-locus.c |  19 ++++---
 gcc/edit-context.c          | 126 +++++++++++++++++++++++++++++++++++++++++---
 libcpp/line-map.c           |  14 +----
 3 files changed, 128 insertions(+), 31 deletions(-)

diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 45c7a56..5e90833 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -2222,10 +2222,6 @@ test_fixit_insert_containing_newline (const line_table_case &case_)
   location_t line_start = linemap_position_for_column (line_table, 1);
   richloc.add_fixit_insert_before (line_start, "      break;\n");
 
-  /* Newlines are not yet supported within fix-it hints, so
-     the fix-it should not be displayed.  */
-  ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
-
   if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
     return;
 
@@ -2233,8 +2229,11 @@ test_fixit_insert_containing_newline (const line_table_case &case_)
   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
   ASSERT_STREQ ("\n"
 		"     case 'b':\n"
-		"     ^~~~~~~~~\n",
+		"     ^~~~~~~~~\n"
+		"       break;\n"
+		"\n",
 		pp_formatted_text (dc.printer));
+  // FIXME: ^^ the above is misleading, and should be fixed
 }
 
 /* Replacement fix-it hint containing a newline.
@@ -2261,10 +2260,6 @@ test_fixit_replace_containing_newline (const line_table_case &case_)
   source_range range = source_range::from_locations (start, finish);
   richloc.add_fixit_replace (range, "\n =");
 
-  /* Newlines are not yet supported within fix-it hints, so
-     the fix-it should not be displayed.  */
-  ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
-
   if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
     return;
 
@@ -2272,8 +2267,12 @@ test_fixit_replace_containing_newline (const line_table_case &case_)
   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
   ASSERT_STREQ ("\n"
 		" foo = bar ();\n"
-		"             ^\n",
+		"             ^\n"
+		"    ---\n"
+		"    \n"
+		" =\n",
 		pp_formatted_text (dc.printer));
+  // FIXME: the above is misleading, and should be fixed
 }
 
 /* Run all of the selftests within this file.  */
diff --git a/gcc/edit-context.c b/gcc/edit-context.c
index 8bf2b7c..934ca13 100644
--- a/gcc/edit-context.c
+++ b/gcc/edit-context.c
@@ -453,25 +453,38 @@ edited_file::print_diff (pretty_printer *pp, bool show_filenames)
 }
 
 /* Print one hunk within a unified diff to PP, covering the
-   given range of lines.  */
+   given range of lines.  OLD_START_OF_HUNK and OLD_END_OF_HUNK are
+   line numbers in the unedited version of the file.  */
 
 void
-edited_file::print_diff_hunk (pretty_printer *pp, int start_of_hunk,
-			      int end_of_hunk)
+edited_file::print_diff_hunk (pretty_printer *pp, int old_start_of_hunk,
+			      int old_end_of_hunk)
 {
-  int num_lines = end_of_hunk - start_of_hunk + 1;
+  int old_num_lines = old_end_of_hunk - old_start_of_hunk + 1;
+
+  // TODO: calculate new_num_lines based on if any newlines have been added
+#if 0
+  int new_num_lines = get_effective_line_count (start_of_hunk, end_of_hunk);
+  // FIXME: does this affect effective start-of-hunk numbering later on...
+#else
+  int new_num_lines = old_num_lines;
+#endif
+
+  int new_start_of_hunk = old_start_of_hunk;
+  //int new_end_of_hunk = old_end_of_hunk;
 
   pp_string (pp, colorize_start (pp_show_color (pp), "diff-hunk"));
-  pp_printf (pp, "@@ -%i,%i +%i,%i @@\n", start_of_hunk, num_lines,
-	     start_of_hunk, num_lines);
+  pp_printf (pp, "@@ -%i,%i +%i,%i @@\n", old_start_of_hunk, old_num_lines,
+	     new_start_of_hunk, new_num_lines);
   pp_string (pp, colorize_stop (pp_show_color (pp)));
 
-  int line_num = start_of_hunk;
-  while (line_num <= end_of_hunk)
+  int line_num = old_start_of_hunk;
+  while (line_num <= old_end_of_hunk)
     {
       edited_line *el = get_line (line_num);
       if (el)
 	{
+	  // TODO: what about added newlines?
 	  /* We have an edited line.
 	     Consolidate into runs of changed lines.  */
 	  const int first_changed_line_in_run = line_num;
@@ -1019,6 +1032,59 @@ test_applying_fixits_insert_after_failure (const line_table_case &case_)
   ASSERT_EQ (NULL, edit.generate_diff (false));
 }
 
+/* Test applying an "insert" fixit that adds a newline.  */
+
+static void
+test_applying_fixits_insert_containing_newline (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+     .........................0000000001111111.
+     .........................1234567890123456.  */
+  const char *old_content = ("    case 'a':\n" /* line 1. */
+			     "      x = a;\n"  /* line 2. */
+			     "    case 'b':\n" /* line 3. */
+			     "      x = b;\n");/* line 4. */
+
+  temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
+  const char *filename = tmp.get_filename ();
+  line_table_test ltt (case_);
+  linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
+
+  /* Add a "break;" on a line by itself before line 3 i.e. before
+     column 1 of line 3. */
+  location_t case_start = linemap_position_for_column (line_table, 5);
+  location_t case_finish = linemap_position_for_column (line_table, 13);
+  location_t case_loc = make_location (case_start, case_start, case_finish);
+  rich_location richloc (line_table, case_loc);
+  location_t line_start = linemap_position_for_column (line_table, 1);
+  richloc.add_fixit_insert_before (line_start, "      break;\n");
+
+  if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  edit_context edit;
+  edit.add_fixits (&richloc);
+  auto_free <char *> new_content = edit.get_content (filename);
+  ASSERT_STREQ (("    case 'a':\n"
+		 "      x = a;\n"
+		 "      break;\n"
+		 "    case 'b':\n"
+		 "      x = b;\n"),
+		new_content);
+
+  /* Verify diff.  */
+#if 0
+  auto_free <char *> diff = edit.generate_diff (false);
+  ASSERT_STREQ (("@@ -1,4 +1,5 @@\n"
+		 "     case 'a':\n"
+		 "       x = a;\n"
+		 "+      break;\n"
+		 "     case 'b':\n"
+		 "       x = b;\n"),
+		diff);
+#endif
+}
+
 /* Test applying a "replace" fixit that grows the affected line.  */
 
 static void
@@ -1109,6 +1175,48 @@ test_applying_fixits_shrinking_replace (const line_table_case &case_)
     }
 }
 
+/* Replacement fix-it hint containing a newline.  */
+
+static void
+test_applying_fixits_replace_containing_newline (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+    .........................0000000001111.
+    .........................1234567890123.  */
+  const char *old_content = "foo = bar ();\n";
+
+  temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
+  const char *filename = tmp.get_filename ();
+  line_table_test ltt (case_);
+  linemap_add (line_table, LC_ENTER, false, filename, 1);
+
+  /* Replace the " = " with "\n  = ", as if we were reformatting an
+     overly long line.  */
+  location_t start = linemap_position_for_column (line_table, 4);
+  location_t finish = linemap_position_for_column (line_table, 6);
+  location_t loc = linemap_position_for_column (line_table, 13);
+  rich_location richloc (line_table, loc);
+  source_range range = source_range::from_locations (start, finish);
+  richloc.add_fixit_replace (range, "\n  = ");
+
+  if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  edit_context edit;
+  edit.add_fixits (&richloc);
+  auto_free <char *> new_content = edit.get_content (filename);
+  ASSERT_STREQ ("foo\n  = bar ();\n", new_content);
+
+  /* Verify diff.  */
+  auto_free <char *> diff = edit.generate_diff (false);
+#if 0
+  ASSERT_STREQ ("@@ -1,1 +1,2 @@\n"
+		"-foo = bar ();\n"
+		"+foo\n"
+		"+  = bar ();\n", diff);
+#endif
+}
+
 /* Test applying a "remove" fixit.  */
 
 static void
@@ -1562,8 +1670,10 @@ edit_context_c_tests ()
   for_each_line_table_case (test_applying_fixits_insert_after);
   for_each_line_table_case (test_applying_fixits_insert_after_at_line_end);
   for_each_line_table_case (test_applying_fixits_insert_after_failure);
+  for_each_line_table_case (test_applying_fixits_insert_containing_newline);
   for_each_line_table_case (test_applying_fixits_growing_replace);
   for_each_line_table_case (test_applying_fixits_shrinking_replace);
+  for_each_line_table_case (test_applying_fixits_replace_containing_newline);
   for_each_line_table_case (test_applying_fixits_remove);
   for_each_line_table_case (test_applying_fixits_multiple);
   for_each_line_table_case (test_applying_fixits_multiple_lines);
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index 07e3acb..8498ce7 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -2128,12 +2128,7 @@ rich_location::add_fixit_insert_before (source_location where,
 
   if (reject_impossible_fixit (start))
     return;
-  /* We do not yet support newlines within fix-it hints.  */
-  if (strchr (new_content, '\n'))
-    {
-      stop_supporting_fixits ();
-      return;
-    }
+
   add_fixit (new fixit_insert (start, new_content));
 }
 
@@ -2277,13 +2272,6 @@ rich_location::add_fixit_replace (source_range src_range,
   if (reject_impossible_fixit (src_range.m_finish))
     return;
 
-  /* We do not yet support newlines within fix-it hints.  */
-  if (strchr (new_content, '\n'))
-    {
-      stop_supporting_fixits ();
-      return;
-    }
-
   /* Consolidate neighboring fixits.  */
   fixit_hint *prev = get_last_fixit_hint ();
   if (prev)
-- 
1.8.5.3

