fix-it hints: insert_before vs insert_after
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 13 Sep 2016 16:08:59 +0000 (16:08 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Tue, 13 Sep 2016 16:08:59 +0000 (16:08 +0000)
The API for adding "insert text" fix-it hints was unclear
about exactly where the text should be inserted relative
to the given insertion point.

This patch clarifies things by renaming the pertinent methods from
  richloc.add_fixit_insert
to
  richloc.add_fixit_insert_before
and adding:
  richloc.add_fixit_insert_after

The latter allows us to consolidate some failure-handling into
class rich_location, rather than having to have every such diagnostic
check for it.

The patch also adds a description of how fix-it hints work to the
comment for class rich_location within libcpp/include/line-map.h.

gcc/c-family/ChangeLog:
* c-common.c (warn_logical_not_parentheses): Replace
rich_location::add_fixit_insert calls with add_fixit_insert_before
and add_fixit_insert_after, eliminating the "next_loc" calculation.

gcc/c/ChangeLog:
* c-parser.c (c_parser_declaration_or_fndef): Update for renaming
of add_fixit_insert to add_fixit_insert_before.

gcc/cp/ChangeLog:
* parser.c (cp_parser_class_specifier_1): Update for renaming of
add_fixit_insert to add_fixit_insert_before.
(cp_parser_class_head): Likewise.

gcc/ChangeLog:
* diagnostic-show-locus.c (selftest::test_one_liner_fixit_insert):
Rename to...
(selftest::test_one_liner_fixit_insert_before): ...this, and update
for renaming of add_fixit_insert to add_fixit_insert_before.
(selftest::test_one_liner_fixit_insert_after): New function.
(selftest::test_one_liner_fixit_validation_adhoc_locations):
Update for renaming of add_fixit_insert to
add_fixit_insert_before.
(selftest::test_one_liner_many_fixits): Likewise.
(selftest::test_diagnostic_show_locus_one_liner): Update for
renaming, call new test function.
(selftest::test_diagnostic_show_locus_fixit_lines): Update for
renaming of add_fixit_insert to add_fixit_insert_before.
(selftest::test_fixit_consolidation): Likewise.
* diagnostic.c (selftest::test_print_parseable_fixits_insert):
Likewise.
* edit-context.c (selftest::test_applying_fixits_insert): Rename
to...
(selftest::test_applying_fixits_insert_before): ...this.
(selftest::test_applying_fixits_insert): Update for renaming of
add_fixit_insert to add_fixit_insert_before.
(selftest::test_applying_fixits_insert_after): New function.
(selftest::test_applying_fixits_insert_after_at_line_end): New
function.
(selftest::test_applying_fixits_insert_after_failure): New
function.
(selftest::test_applying_fixits_multiple): Update for renaming of
add_fixit_insert to add_fixit_insert_before.
(selftest::change_line): Likewise.
(selftest::test_applying_fixits_unreadable_file): Likewise.
(selftest::test_applying_fixits_line_out_of_range): Likewise.
(selftest::test_applying_fixits_column_validation): Likewise.
(selftest::test_applying_fixits_column_validation): Likewise.
(selftest::edit_context_c_tests): Update for renamed test
function; call new test functions.

gcc/testsuite/ChangeLog:
* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
(test_show_locus): Replace rich_location::add_fixit_insert calls
with add_fixit_insert_before and add_fixit_insert_after.

libcpp/ChangeLog:
* include/line-map.h (class rich_location): Add description of
fix-it hints to leading comment.
(rich_location::add_fixit_insert): Rename both overloaded methods
to..
(rich_location::add_fixit_insert_before): ...this, updating their
comments.
(rich_location::add_fixit_insert_after): Two new overloaded
methods.
(rich_location::stop_supporting_fixits): New method.
* line-map.c (rich_location::add_fixit_insert): Rename both
overloaded methods to..
(rich_location::add_fixit_insert_before): ...this, updating their
comments.
(rich_location::add_fixit_insert_after): Two new methods.
(rich_location::reject_impossible_fixit): Split out
failure-handling into...
(rich_location::stop_supporting_fixits): New method.

From-SVN: r240115

15 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c/ChangeLog
gcc/c/c-parser.c
gcc/cp/ChangeLog
gcc/cp/parser.c
gcc/diagnostic-show-locus.c
gcc/diagnostic.c
gcc/edit-context.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
libcpp/ChangeLog
libcpp/include/line-map.h
libcpp/line-map.c

index 58ab8355a75dae773d3cecefb51b0dff298938a3..93c90ce07856497b4a4ccdbe94caf983fb7c8771 100644 (file)
@@ -1,3 +1,41 @@
+2016-09-13  David Malcolm  <dmalcolm@redhat.com>
+
+       * diagnostic-show-locus.c (selftest::test_one_liner_fixit_insert):
+       Rename to...
+       (selftest::test_one_liner_fixit_insert_before): ...this, and update
+       for renaming of add_fixit_insert to add_fixit_insert_before.
+       (selftest::test_one_liner_fixit_insert_after): New function.
+       (selftest::test_one_liner_fixit_validation_adhoc_locations):
+       Update for renaming of add_fixit_insert to
+       add_fixit_insert_before.
+       (selftest::test_one_liner_many_fixits): Likewise.
+       (selftest::test_diagnostic_show_locus_one_liner): Update for
+       renaming, call new test function.
+       (selftest::test_diagnostic_show_locus_fixit_lines): Update for
+       renaming of add_fixit_insert to add_fixit_insert_before.
+       (selftest::test_fixit_consolidation): Likewise.
+       * diagnostic.c (selftest::test_print_parseable_fixits_insert):
+       Likewise.
+       * edit-context.c (selftest::test_applying_fixits_insert): Rename
+       to...
+       (selftest::test_applying_fixits_insert_before): ...this.
+       (selftest::test_applying_fixits_insert): Update for renaming of
+       add_fixit_insert to add_fixit_insert_before.
+       (selftest::test_applying_fixits_insert_after): New function.
+       (selftest::test_applying_fixits_insert_after_at_line_end): New
+       function.
+       (selftest::test_applying_fixits_insert_after_failure): New
+       function.
+       (selftest::test_applying_fixits_multiple): Update for renaming of
+       add_fixit_insert to add_fixit_insert_before.
+       (selftest::change_line): Likewise.
+       (selftest::test_applying_fixits_unreadable_file): Likewise.
+       (selftest::test_applying_fixits_line_out_of_range): Likewise.
+       (selftest::test_applying_fixits_column_validation): Likewise.
+       (selftest::test_applying_fixits_column_validation): Likewise.
+       (selftest::edit_context_c_tests): Update for renamed test
+       function; call new test functions.
+
 2016-09-13  Pat Haugen  <pthaugen@us.ibm.com>
 
        PR tree-optimization/77536
index d8f1808e6998a07c7a2e22c5e82bab7bd8f681fb..5a2cbbef4160c07dd3ce3e63db75f2cae28ad5d2 100644 (file)
@@ -1,3 +1,9 @@
+2016-09-13  David Malcolm  <dmalcolm@redhat.com>
+
+       * c-common.c (warn_logical_not_parentheses): Replace
+       rich_location::add_fixit_insert calls with add_fixit_insert_before
+       and add_fixit_insert_after, eliminating the "next_loc" calculation.
+
 2016-09-13  Jason Merrill  <jason@redhat.com>
            Tom de Vries  <tom@codesourcery.com>
 
index 73bd43f8703290e0cf77aa43180f8e80b2bf0441..1132a03138446ef14795186f547b4d1b53ecd575 100644 (file)
@@ -1544,11 +1544,8 @@ warn_logical_not_parentheses (location_t location, enum tree_code code,
     {
       location_t lhs_loc = EXPR_LOCATION (lhs);
       rich_location richloc (line_table, lhs_loc);
-      richloc.add_fixit_insert (lhs_loc, "(");
-      location_t finish = get_finish (lhs_loc);
-      location_t next_loc
-       = linemap_position_for_loc_and_offset (line_table, finish, 1);
-      richloc.add_fixit_insert (next_loc, ")");
+      richloc.add_fixit_insert_before (lhs_loc, "(");
+      richloc.add_fixit_insert_after (lhs_loc, ")");
       inform_at_rich_loc (&richloc, "add parentheses around left hand side "
                          "expression to silence this warning");
     }
index c856374993958813fa1c3fba2d00ae42810aed72..4a9881d351a5fd8531552066ada950057be7afbc 100644 (file)
@@ -1,3 +1,8 @@
+2016-09-13  David Malcolm  <dmalcolm@redhat.com>
+
+       * c-parser.c (c_parser_declaration_or_fndef): Update for renaming
+       of add_fixit_insert to add_fixit_insert_before.
+
 2016-09-13  Marek Polacek  <polacek@redhat.com>
 
        * c-typeck.c (build_unary_op): Rename FLAG parameter to NOCONVERT.  Use
index a3044244f6cbacf928408fb52a0d47482070cb75..e71c0d5b9b543199f83232599ba760ca4c6f1d5c 100644 (file)
@@ -1685,7 +1685,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
       if (tag_exists_p (RECORD_TYPE, name))
        {
          /* This is not C++ with its implicit typedef.  */
-         richloc.add_fixit_insert ("struct ");
+         richloc.add_fixit_insert_before ("struct ");
          error_at_rich_loc (&richloc,
                             "unknown type name %qE;"
                             " use %<struct%> keyword to refer to the type",
@@ -1693,7 +1693,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
        }
       else if (tag_exists_p (UNION_TYPE, name))
        {
-         richloc.add_fixit_insert ("union ");
+         richloc.add_fixit_insert_before ("union ");
          error_at_rich_loc (&richloc,
                             "unknown type name %qE;"
                             " use %<union%> keyword to refer to the type",
@@ -1701,7 +1701,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
        }
       else if (tag_exists_p (ENUMERAL_TYPE, name))
        {
-         richloc.add_fixit_insert ("enum ");
+         richloc.add_fixit_insert_before ("enum ");
          error_at_rich_loc (&richloc,
                             "unknown type name %qE;"
                             " use %<enum%> keyword to refer to the type",
index f572c851b6263add01650373f3c8c838a5cc0bfe..79691bf68927fc0020b725e76e3cdcc2b82f6111 100644 (file)
@@ -1,3 +1,9 @@
+2016-09-13  David Malcolm  <dmalcolm@redhat.com>
+
+       * parser.c (cp_parser_class_specifier_1): Update for renaming of
+       add_fixit_insert to add_fixit_insert_before.
+       (cp_parser_class_head): Likewise.
+
 2016-09-12  Bernd Edlinger  <bernd.edlinger@hotmail.de>
 
        PR c++/77496
index ca9f8b9761a5aaf829f81f21e8166078466bd270..73a37814b5969c12b4dadafdbbf271be6cc6a5f5 100644 (file)
@@ -21594,7 +21594,7 @@ cp_parser_class_specifier_1 (cp_parser* parser)
          next_loc = linemap_position_for_loc_and_offset (line_table, loc, 1);
 
        rich_location richloc (line_table, next_loc);
-       richloc.add_fixit_insert (next_loc, ";");
+       richloc.add_fixit_insert_before (next_loc, ";");
 
        if (CLASSTYPE_DECLARED_CLASS (type))
          error_at_rich_loc (&richloc,
@@ -22037,7 +22037,8 @@ cp_parser_class_head (cp_parser* parser,
                          class_head_start_location,
                          get_finish (type_start_token->location));
       rich_location richloc (line_table, reported_loc);
-      richloc.add_fixit_insert (class_head_start_location, "template <> ");
+      richloc.add_fixit_insert_before (class_head_start_location,
+                                       "template <> ");
       error_at_rich_loc
         (&richloc,
          "an explicit specialization must be preceded by %<template <>%>");
index 00a95a19cc012af51a131f4390518ac90532dcf3..331eb92f9686b1c4d23a0c976a19718c7badf09c 100644 (file)
@@ -1661,12 +1661,12 @@ test_one_liner_multiple_carets_and_ranges ()
 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
 
 static void
-test_one_liner_fixit_insert ()
+test_one_liner_fixit_insert_before ()
 {
   test_diagnostic_context dc;
   location_t caret = linemap_position_for_column (line_table, 7);
   rich_location richloc (line_table, caret);
-  richloc.add_fixit_insert ("&");
+  richloc.add_fixit_insert_before ("&");
   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
   ASSERT_STREQ ("\n"
                " foo = bar.field;\n"
@@ -1675,6 +1675,25 @@ test_one_liner_fixit_insert ()
                pp_formatted_text (dc.printer));
 }
 
+/* Insertion fix-it hint: adding a "[0]" after "foo". */
+
+static void
+test_one_liner_fixit_insert_after ()
+{
+  test_diagnostic_context dc;
+  location_t start = linemap_position_for_column (line_table, 1);
+  location_t finish = linemap_position_for_column (line_table, 3);
+  location_t foo = make_location (start, start, finish);
+  rich_location richloc (line_table, foo);
+  richloc.add_fixit_insert_after ("[0]");
+  diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+  ASSERT_STREQ ("\n"
+               " foo = bar.field;\n"
+               " ^~~\n"
+               "    [0]\n",
+               pp_formatted_text (dc.printer));
+}
+
 /* Removal fix-it hint: removal of the ".field". */
 
 static void
@@ -1785,7 +1804,7 @@ test_one_liner_fixit_validation_adhoc_locations ()
   /* Insert.  */
   {
     rich_location richloc (line_table, loc);
-    richloc.add_fixit_insert (loc, "test");
+    richloc.add_fixit_insert_before (loc, "test");
     /* It should not have been discarded by the validator.  */
     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
 
@@ -1843,7 +1862,7 @@ test_one_liner_many_fixits ()
   location_t equals = linemap_position_for_column (line_table, 5);
   rich_location richloc (line_table, equals);
   for (int i = 0; i < 19; i++)
-    richloc.add_fixit_insert ("a");
+    richloc.add_fixit_insert_before ("a");
   ASSERT_EQ (19, richloc.get_num_fixit_hints ());
   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
   ASSERT_STREQ ("\n"
@@ -1898,7 +1917,8 @@ test_diagnostic_show_locus_one_liner (const line_table_case &case_)
   test_one_liner_simple_caret ();
   test_one_liner_caret_and_range ();
   test_one_liner_multiple_carets_and_ranges ();
-  test_one_liner_fixit_insert ();
+  test_one_liner_fixit_insert_before ();
+  test_one_liner_fixit_insert_after ();
   test_one_liner_fixit_remove ();
   test_one_liner_fixit_replace ();
   test_one_liner_fixit_replace_non_equal_range ();
@@ -1949,7 +1969,7 @@ test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
     const location_t colon
       = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
     rich_location richloc (line_table, colon);
-    richloc.add_fixit_insert (x, ".");
+    richloc.add_fixit_insert_before (x, ".");
     richloc.add_fixit_replace (colon, "=");
     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
     ASSERT_STREQ ("\n"
@@ -1970,7 +1990,7 @@ test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
     const location_t colon
       = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
     rich_location richloc (line_table, colon);
-    richloc.add_fixit_insert (y, ".");
+    richloc.add_fixit_insert_before (y, ".");
     richloc.add_fixit_replace (colon, "=");
     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
     ASSERT_STREQ ("\n"
@@ -2012,8 +2032,8 @@ test_fixit_consolidation (const line_table_case &case_)
   /* Insert + insert. */
   {
     rich_location richloc (line_table, caret);
-    richloc.add_fixit_insert (c10, "foo");
-    richloc.add_fixit_insert (c15, "bar");
+    richloc.add_fixit_insert_before (c10, "foo");
+    richloc.add_fixit_insert_before (c15, "bar");
 
     if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
       /* Bogus column info for 2nd fixit, so no fixits.  */
@@ -2026,7 +2046,7 @@ test_fixit_consolidation (const line_table_case &case_)
   /* Insert + replace. */
   {
     rich_location richloc (line_table, caret);
-    richloc.add_fixit_insert (c10, "foo");
+    richloc.add_fixit_insert_before (c10, "foo");
     richloc.add_fixit_replace (source_range::from_locations (c15, c17),
                               "bar");
 
@@ -2043,7 +2063,7 @@ test_fixit_consolidation (const line_table_case &case_)
     rich_location richloc (line_table, caret);
     richloc.add_fixit_replace (source_range::from_locations (c10, c15),
                               "bar");
-    richloc.add_fixit_insert (c17, "foo");
+    richloc.add_fixit_insert_before (c17, "foo");
 
     if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
       /* Bogus column info for 2nd fixit, so no fixits.  */
index 46cdb629e8d55e7b05c409e94a05a5b5ea6bbc18..585028ec21185094ab1d9e1289ea4f1e339390fe 100644 (file)
@@ -1504,7 +1504,7 @@ test_print_parseable_fixits_insert ()
   linemap_line_start (line_table, 5, 100);
   linemap_add (line_table, LC_LEAVE, false, NULL, 0);
   location_t where = linemap_position_for_column (line_table, 10);
-  richloc.add_fixit_insert (where, "added content");
+  richloc.add_fixit_insert_before (where, "added content");
 
   print_parseable_fixits (&pp, &richloc);
   ASSERT_STREQ ("fix-it:\"test.c\":{5:10-5:10}:\"added content\"\n",
index 087764ec2de32d69ecfd26f69522c033c4f4391d..5945f423d29c7091866243444de1669a06d7c8c9 100644 (file)
@@ -918,10 +918,10 @@ test_get_content ()
   }
 }
 
-/* Test applying an "insert" fixit.  */
+/* Test applying an "insert" fixit, using insert_before.  */
 
 static void
-test_applying_fixits_insert (const line_table_case &case_)
+test_applying_fixits_insert_before (const line_table_case &case_)
 {
   /* Create a tempfile and write some text to it.
      .........................0000000001111111.
@@ -937,7 +937,7 @@ test_applying_fixits_insert (const line_table_case &case_)
   /* Add a comment in front of "bar.field".  */
   location_t start = linemap_position_for_column (line_table, 7);
   rich_location richloc (line_table, start);
-  richloc.add_fixit_insert ("/* inserted */");
+  richloc.add_fixit_insert_before ("/* inserted */");
 
   if (start > LINE_MAP_MAX_LOCATION_WITH_COLS)
     return;
@@ -971,6 +971,142 @@ test_applying_fixits_insert (const line_table_case &case_)
                " /* after */\n", diff);
 }
 
+/* Test applying an "insert" fixit, using insert_after, with
+   a range of length > 1 (to ensure that the end-point of
+   the input range is used).  */
+
+static void
+test_applying_fixits_insert_after (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+     .........................0000000001111111.
+     .........................1234567890123456.  */
+  const char *old_content = ("/* before */\n"
+                            "foo = bar.field;\n"
+                            "/* after */\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, tmp.get_filename (), 2);
+
+  /* Add a comment after "field".  */
+  location_t start = linemap_position_for_column (line_table, 11);
+  location_t finish = linemap_position_for_column (line_table, 15);
+  location_t field = make_location (start, start, finish);
+  rich_location richloc (line_table, field);
+  richloc.add_fixit_insert_after ("/* inserted */");
+
+  if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  /* Verify that the text was inserted after the end of "field". */
+  edit_context edit;
+  edit.add_fixits (&richloc);
+  auto_free <char *> new_content = edit.get_content (filename);
+  ASSERT_STREQ ("/* before */\n"
+               "foo = bar.field/* inserted */;\n"
+               "/* after */\n", new_content);
+
+  /* Verify diff.  */
+  auto_free <char *> diff = edit.generate_diff (false);
+  ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
+               " /* before */\n"
+               "-foo = bar.field;\n"
+               "+foo = bar.field/* inserted */;\n"
+               " /* after */\n", diff);
+}
+
+/* Test applying an "insert" fixit, using insert_after at the end of
+   a line (contrast with test_applying_fixits_insert_after_failure
+   below).  */
+
+static void
+test_applying_fixits_insert_after_at_line_end (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+     .........................0000000001111111.
+     .........................1234567890123456.  */
+  const char *old_content = ("/* before */\n"
+                            "foo = bar.field;\n"
+                            "/* after */\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, tmp.get_filename (), 2);
+
+  /* Add a comment after the semicolon.  */
+  location_t loc = linemap_position_for_column (line_table, 16);
+  rich_location richloc (line_table, loc);
+  richloc.add_fixit_insert_after ("/* inserted */");
+
+  if (loc > 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 ("/* before */\n"
+               "foo = bar.field;/* inserted */\n"
+               "/* after */\n", new_content);
+
+  /* Verify diff.  */
+  auto_free <char *> diff = edit.generate_diff (false);
+  ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
+               " /* before */\n"
+               "-foo = bar.field;\n"
+               "+foo = bar.field;/* inserted */\n"
+               " /* after */\n", diff);
+}
+
+/* Test of a failed attempt to apply an "insert" fixit, using insert_after,
+   due to the relevant linemap ending.  Contrast with
+   test_applying_fixits_insert_after_at_line_end above.  */
+
+static void
+test_applying_fixits_insert_after_failure (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+     .........................0000000001111111.
+     .........................1234567890123456.  */
+  const char *old_content = ("/* before */\n"
+                            "foo = bar.field;\n"
+                            "/* after */\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, tmp.get_filename (), 2);
+
+  /* Add a comment after the semicolon.  */
+  location_t loc = linemap_position_for_column (line_table, 16);
+  rich_location richloc (line_table, loc);
+
+  /* We want a failure of linemap_position_for_loc_and_offset.
+     We can do this by starting a new linemap at line 3, so that
+     there is no appropriate location value for the insertion point
+     within the linemap for line 2.  */
+  linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
+
+  /* The failure fails to happen at the transition point from
+     packed ranges to unpacked ranges (where there are some "spare"
+     location_t values).  Skip the test there.  */
+  if (loc >= LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES)
+    return;
+
+  /* Offsetting "loc" should now fail (by returning the input loc. */
+  ASSERT_EQ (loc, linemap_position_for_loc_and_offset (line_table, loc, 1));
+
+  /* Hence attempting to use add_fixit_insert_after at the end of the line
+     should now fail.  */
+  richloc.add_fixit_insert_after ("/* inserted */");
+  ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
+
+  edit_context edit;
+  edit.add_fixits (&richloc);
+  ASSERT_FALSE (edit.valid_p ());
+  ASSERT_EQ (NULL, edit.get_content (filename));
+  ASSERT_EQ (NULL, edit.generate_diff (false));
+}
+
 /* Test applying a "replace" fixit that grows the affected line.  */
 
 static void
@@ -1135,11 +1271,11 @@ test_applying_fixits_multiple (const line_table_case &case_)
 
   /* Add a comment in front of "bar.field".  */
   rich_location insert_a (line_table, c7);
-  insert_a.add_fixit_insert (c7, "/* alpha */");
+  insert_a.add_fixit_insert_before (c7, "/* alpha */");
 
   /* Add a comment after "bar.field;".  */
   rich_location insert_b (line_table, c17);
-  insert_b.add_fixit_insert (c17, "/* beta */");
+  insert_b.add_fixit_insert_before (c17, "/* beta */");
 
   /* Replace "bar" with "pub".   */
   rich_location replace_a (line_table, c7);
@@ -1203,7 +1339,7 @@ change_line (edit_context &edit, int line_num)
     }
 
   rich_location insert (line_table, loc);
-  insert.add_fixit_insert ("CHANGED: ");
+  insert.add_fixit_insert_before ("CHANGED: ");
   edit.add_fixits (&insert);
   return loc;
 }
@@ -1371,8 +1507,8 @@ test_applying_fixits_unreadable_file ()
   location_t loc = linemap_position_for_column (line_table, 1);
 
   rich_location insert (line_table, loc);
-  insert.add_fixit_insert ("change 1");
-  insert.add_fixit_insert ("change 2");
+  insert.add_fixit_insert_before ("change 1");
+  insert.add_fixit_insert_before ("change 2");
 
   edit_context edit;
   /* Attempting to add the fixits affecting the unreadable file
@@ -1403,7 +1539,7 @@ test_applying_fixits_line_out_of_range ()
   location_t loc = linemap_position_for_column (line_table, 1);
 
   rich_location insert (line_table, loc);
-  insert.add_fixit_insert ("change");
+  insert.add_fixit_insert_before ("change");
 
   /* Verify that attempting the insertion puts an edit_context
      into an invalid state.  */
@@ -1440,7 +1576,7 @@ test_applying_fixits_column_validation (const line_table_case &case_)
   /* Verify inserting at the end of the line.  */
   {
     rich_location richloc (line_table, c11);
-    richloc.add_fixit_insert (c15, " change");
+    richloc.add_fixit_insert_before (c15, " change");
 
     /* Col 15 is at the end of the line, so the insertion
        should succeed.  */
@@ -1456,7 +1592,7 @@ test_applying_fixits_column_validation (const line_table_case &case_)
   /* Verify inserting beyond the end of the line.  */
   {
     rich_location richloc (line_table, c11);
-    richloc.add_fixit_insert (c16, " change");
+    richloc.add_fixit_insert_before (c16, " change");
 
     /* Col 16 is beyond the end of the line, so the insertion
        should fail gracefully.  */
@@ -1510,7 +1646,10 @@ void
 edit_context_c_tests ()
 {
   test_get_content ();
-  for_each_line_table_case (test_applying_fixits_insert);
+  for_each_line_table_case (test_applying_fixits_insert_before);
+  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_growing_replace);
   for_each_line_table_case (test_applying_fixits_shrinking_replace);
   for_each_line_table_case (test_applying_fixits_remove);
index 2ebe9e68f9074c3f1753866e59b2cf92c0955643..d6f6b32ba4b1169797202ad38f1c7b8756b85787 100644 (file)
@@ -1,3 +1,9 @@
+2016-09-13  David Malcolm  <dmalcolm@redhat.com>
+
+       * gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
+       (test_show_locus): Replace rich_location::add_fixit_insert calls
+       with add_fixit_insert_before and add_fixit_insert_after.
+
 2016-09-13  Jason Merrill  <jason@redhat.com>
            Tom de Vries  <tom@codesourcery.com>
 
index ea28f046e8d5db88ff51c206a4a902e7f8f3aa34..3efc7dfa0b4c40ed7b9cad8e75da79011cfdce68 100644 (file)
@@ -263,8 +263,8 @@ test_show_locus (function *fun)
       location_t start = get_loc (line, 19);
       location_t finish = get_loc (line, 22);
       rich_location richloc (line_table, make_location (start, start, finish));
-      richloc.add_fixit_insert (start, "{");
-      richloc.add_fixit_insert (get_loc (line, 23), "}");
+      richloc.add_fixit_insert_before ("{");
+      richloc.add_fixit_insert_after ("}");
       warning_at_rich_loc (&richloc, 0, "example of insertion hints");
     }
 
index 792f76ae3b5ec03cb1afc585d5c6b42ed07e3d7b..95e12a9ba6b5439d91b0fefd16d9d7ad67dc428a 100644 (file)
@@ -1,3 +1,23 @@
+2016-09-13  David Malcolm  <dmalcolm@redhat.com>
+
+       * include/line-map.h (class rich_location): Add description of
+       fix-it hints to leading comment.
+       (rich_location::add_fixit_insert): Rename both overloaded methods
+       to..
+       (rich_location::add_fixit_insert_before): ...this, updating their
+       comments.
+       (rich_location::add_fixit_insert_after): Two new overloaded
+       methods.
+       (rich_location::stop_supporting_fixits): New method.
+       * line-map.c (rich_location::add_fixit_insert): Rename both
+       overloaded methods to..
+       (rich_location::add_fixit_insert_before): ...this, updating their
+       comments.
+       (rich_location::add_fixit_insert_after): Two new methods.
+       (rich_location::reject_impossible_fixit): Split out
+       failure-handling into...
+       (rich_location::stop_supporting_fixits): New method.
+
 2016-09-02  David Malcolm  <dmalcolm@redhat.com>
 
        * include/line-map.h (rich_location::seen_impossible_fixit_p): New
index 6b3c474a0de9246a6a19e44c9e2412135f2439b1..939bfcc5712532636dceb150e6703ec230290c54 100644 (file)
@@ -1484,7 +1484,84 @@ class fixit_hint;
    - range 0 is at the "%s" with start = caret = "%" and finish at
      the "s".
    - range 1 has start/finish covering the "101" and is not flagged for
-     caret printing; it is perhaps at the start of "101".  */
+     caret printing; it is perhaps at the start of "101".
+
+
+   Fix-it hints
+   ------------
+
+   Rich locations can also contain "fix-it hints", giving suggestions
+   for the user on how to edit their code to fix a problem.  These
+   can be expressed as insertions, replacements, and removals of text.
+   The edits by default are relative to the zeroth range within the
+   rich_location, but optionally they can be expressed relative to
+   other locations (using various overloaded methods of the form
+   rich_location::add_fixit_*).
+
+   For example:
+
+   Example F: fix-it hint: insert_before
+   *************************************
+      ptr = arr[0];
+           ^~~~~~
+           &
+   This rich location has a single range (range 0) covering "arr[0]",
+   with the caret at the start.  The rich location has a single
+   insertion fix-it hint, inserted before range 0, added via
+     richloc.add_fixit_insert_before ("&");
+
+   Example G: multiple fix-it hints: insert_before and insert_after
+   ****************************************************************
+      #define FN(ARG0, ARG1, ARG2) fn(ARG0, ARG1, ARG2)
+                                     ^~~~  ^~~~  ^~~~
+                                     (   ) (   ) (   )
+   This rich location has three ranges, covering "arg0", "arg1",
+   and "arg2", all with caret-printing enabled.
+   The rich location has 6 insertion fix-it hints: each arg
+   has a pair of insertion fix-it hints, suggesting wrapping
+   them with parentheses: one a '(' inserted before,
+   the other a ')' inserted after, added via
+     richloc.add_fixit_insert_before (LOC, "(");
+   and
+     richloc.add_fixit_insert_after (LOC, ")");
+
+   Example H: fix-it hint: removal
+   *******************************
+     struct s {int i};;
+                     ^
+                     -
+   This rich location has a single range at the stray trailing
+   semicolon, along with a single removal fix-it hint, covering
+   the same range, added via:
+     richloc.add_fixit_remove ();
+
+   Example I: fix-it hint: replace
+   *******************************
+      c = s.colour;
+           ^~~~~~
+           color
+   This rich location has a single range (range 0) covering "colour",
+   and a single "replace" fix-it hint, covering the same range,
+   added via
+     richloc.add_fixit_replace ("color");
+
+   Adding a fix-it hint can fail: for example, attempts to insert content
+   at the transition between two line maps may fail due to there being no
+   source_location (aka location_t) value to express the new location.
+
+   Attempts to add a fix-it hint within a macro expansion will fail.
+
+   The rich_location API handles these failures gracefully, so that
+   diagnostics can attempt to add fix-it hints without each needing
+   extensive checking.
+
+   Fix-it hints within a rich_location are "atomic": if any hints can't
+   be applied, none of them will be (tracked by the m_seen_impossible_fixit
+   flag), and no fix-its hints will be displayed for that rich_location.
+   This implies that diagnostic messages need to be worded in such a way
+   that they make sense whether or not the fix-it hints are displayed,
+   or that richloc.seen_impossible_fixit_p () should be checked before
+   issuing the diagnostics.  */
 
 class rich_location
 {
@@ -1522,14 +1599,25 @@ class rich_location
 
   /* Methods for adding insertion fix-it hints.  */
 
-  /* Suggest inserting NEW_CONTENT at the primary range's caret.  */
+  /* Suggest inserting NEW_CONTENT immediately before the primary
+     range's start.  */
   void
-  add_fixit_insert (const char *new_content);
+  add_fixit_insert_before (const char *new_content);
 
-  /* Suggest inserting NEW_CONTENT at WHERE.  */
+  /* Suggest inserting NEW_CONTENT immediately before the start of WHERE.  */
   void
-  add_fixit_insert (source_location where,
-                   const char *new_content);
+  add_fixit_insert_before (source_location where,
+                          const char *new_content);
+
+  /* Suggest inserting NEW_CONTENT immediately after the end of the primary
+     range.  */
+  void
+  add_fixit_insert_after (const char *new_content);
+
+  /* Suggest inserting NEW_CONTENT immediately after the end of WHERE.  */
+  void
+  add_fixit_insert_after (source_location where,
+                         const char *new_content);
 
   /* Methods for adding removal fix-it hints.  */
 
@@ -1571,6 +1659,7 @@ class rich_location
 
 private:
   bool reject_impossible_fixit (source_location where);
+  void stop_supporting_fixits ();
   void add_fixit (fixit_hint *hint);
 
 public:
index f69c60c78377bb8ba28d9dc80e039de8a266dfb9..742af0a07bbd39f055687e60df0ac752ad80f30c 100644 (file)
@@ -2109,26 +2109,61 @@ rich_location::set_range (line_maps * /*set*/, unsigned int idx,
 /* Methods for adding insertion fix-it hints.  */
 
 /* Add a fixit-hint, suggesting insertion of NEW_CONTENT
-   at the primary range's caret location.  */
+   immediately before the primary range's start location.  */
 
 void
-rich_location::add_fixit_insert (const char *new_content)
+rich_location::add_fixit_insert_before (const char *new_content)
 {
-  add_fixit_insert (get_loc (), new_content);
+  add_fixit_insert_before (get_loc (), new_content);
 }
 
 /* Add a fixit-hint, suggesting insertion of NEW_CONTENT
-   at WHERE.  */
+   immediately before the start of WHERE.  */
 
 void
-rich_location::add_fixit_insert (source_location where,
-                                const char *new_content)
+rich_location::add_fixit_insert_before (source_location where,
+                                       const char *new_content)
 {
-  where = get_pure_location (m_line_table, where);
+  source_location start = get_range_from_loc (m_line_table, where).m_start;
 
-  if (reject_impossible_fixit (where))
+  if (reject_impossible_fixit (start))
     return;
-  add_fixit (new fixit_insert (where, new_content));
+  add_fixit (new fixit_insert (start, new_content));
+}
+
+/* Add a fixit-hint, suggesting insertion of NEW_CONTENT
+   immediately after the primary range's end-point.  */
+
+void
+rich_location::add_fixit_insert_after (const char *new_content)
+{
+  add_fixit_insert_after (get_loc (), new_content);
+}
+
+/* Add a fixit-hint, suggesting insertion of NEW_CONTENT
+   immediately after the end-point of WHERE.  */
+
+void
+rich_location::add_fixit_insert_after (source_location where,
+                                      const char *new_content)
+{
+  source_location finish = get_range_from_loc (m_line_table, where).m_finish;
+
+  if (reject_impossible_fixit (finish))
+    return;
+
+  source_location next_loc
+    = linemap_position_for_loc_and_offset (m_line_table, finish, 1);
+
+  /* linemap_position_for_loc_and_offset can fail, if so, it returns
+     its input value.  */
+  if (next_loc == finish)
+    {
+      stop_supporting_fixits ();
+      return;
+    }
+
+  add_fixit (new fixit_insert (next_loc, new_content));
 }
 
 /* Methods for adding removal fix-it hints.  */
@@ -2278,14 +2313,22 @@ rich_location::reject_impossible_fixit (source_location where)
   /* Otherwise we have an attempt to add a fix-it with an "awkward"
      location: either one that we can't obtain column information
      for (within an ordinary map), or one within a macro expansion.  */
+  stop_supporting_fixits ();
+  return true;
+}
+
+/* Mark this rich_location as not supporting fixits, purging any that were
+   already added.  */
+
+void
+rich_location::stop_supporting_fixits ()
+{
   m_seen_impossible_fixit = true;
 
   /* Purge the rich_location of any fix-its that were already added. */
   for (unsigned int i = 0; i < m_fixit_hints.count (); i++)
     delete get_fixit_hint (i);
   m_fixit_hints.truncate (0);
-
-  return true;
 }
 
 /* Add HINT to the fix-it hints in this rich_location.  */