mirror of git://gcc.gnu.org/git/gcc.git
Support fix-it hints that add new lines
Previously fix-it hints couldn't contain newlines. This is
due to the need to print something user-readable for them
within diagnostic-show-locus, and for handling them within
edit-context for printing diffs and regenerating content.
This patch enables limited support for fix-it hints with newlines,
for suggesting adding new lines.
Such a fix-it hint must have exactly one newline character, at the
end of the content. It must be an insertion at the beginning of
a line (so that e.g. fix-its that split a pre-existing line are
still rejected).
They are printed by diagnostic-show-locus with a '+' in the
left-hand margin, like this:
test.c:42:4: note: suggest adding 'break;' here
+ break;
case 'b':
^~~~~~~~~
and the printer injects "spans" if the insertion location is not
near the primary range of the diagnostic e.g.:
test.c:4:2: note: unrecognized 'putchar'; suggest including '<stdio.h>'
test.c:1:1:
+#include <stdio.h>
test.c:4:2:
putchar (ch);
^~~~~~~
gcc/ChangeLog:
* diagnostic-show-locus.c
(layout::should_print_annotation_line_p): Make private.
(layout::print_annotation_line): Make private.
(layout::annotation_line_showed_range_p): Make private.
(layout::show_ruler): Make private.
(layout::print_source_line): Make private. Pass in line and
line_width, rather than calling location_get_source_line. Drop
returned value.
(layout::print_leading_fixits): New method.
(layout::print_any_fixits): Rename to...
(layout::print_trailing_fixits): ...this, and make private.
Don't print newline fixits.
(diagnostic_show_locus): Move logic for printing one row into...
(layout::print_line): ...this new function. Move the
location_get_source_line call and error-handling from
print_source_line to here. Call print_leading_fixits, and rename
print_any_fixits to print_trailing_fixits.
(selftest::test_fixit_insert_containing_newline): Update now that
newlines are partially supported.
(selftest::test_fixit_insert_containing_newline_2): New test.
(selftest::test_fixit_replace_containing_newline): Update comments.
(selftest::diagnostic_show_locus_c_tests): Call the new test.
* edit-context.c (class added_line): New class.
(class edited_line): Describe newline handling in comment.
(edited_line::actually_edited_p): New method.
(edited_line::print_content): Delete redundant decl.
(edited_line::m_predecessors): New field.
(edited_file::print_content): Call edited_line::print_content.
(edited_file::print_diff): Update to support newlines.
(edited_file::print_diff_hunk): Likewise.
(edited_file::print_run_of_changed_lines): New function.
(edited_file::print_diff_line): Convert to...
(print_diff_line): ...this.
(edited_file::get_effective_line_count): New function.
(edited_line::edited_line): Initialize new field m_predecessors.
(edited_line::~edited_line): Clean up m_predecessors.
(edited_line::apply_fixit): Handle newlines.
(edited_line::get_effective_line_count): New function.
(edited_line::print_content): New function.
(edited_line::print_diff_lines): New function.
(selftest::test_applying_fixits_insert_containing_newline): New
test.
(selftest::test_applying_fixits_replace_containing_newline): New
test.
(selftest::insert_line): New function.
(selftest::test_applying_fixits_multiple_lines): Add example of
inserting a line.
(selftest::edit_context_c_tests): Call the new tests.
gcc/testsuite/ChangeLog:
* gcc.dg/plugin/diagnostic-test-show-locus-bw.c
(test_fixit_insert_newline): New function.
* gcc.dg/plugin/diagnostic-test-show-locus-color.c
(test_fixit_insert_newline): New function.
* gcc.dg/plugin/diagnostic-test-show-locus-generate-patch.c
(test_fixit_insert_newline): New function.
* gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c
(test_fixit_insert_newline): New function.
* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
(test_show_locus): Handle test_fixit_insert_newline.
libcpp/ChangeLog:
* include/line-map.h (class rich_location): Update description of
newline handling.
(class fixit_hint): Likewise.
(fixit_hint::ends_with_newline_p): New decl.
* line-map.c (rich_location::maybe_add_fixit): Support newlines
in fix-it hints that are insertions of single lines at the start
of a line. Don't consolidate into such fix-it hints.
(fixit_hint::ends_with_newline_p): New method.
From-SVN: r247522
This commit is contained in:
parent
19576ba740
commit
ad53f12355
|
|
@ -1,3 +1,54 @@
|
|||
2017-05-02 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* diagnostic-show-locus.c
|
||||
(layout::should_print_annotation_line_p): Make private.
|
||||
(layout::print_annotation_line): Make private.
|
||||
(layout::annotation_line_showed_range_p): Make private.
|
||||
(layout::show_ruler): Make private.
|
||||
(layout::print_source_line): Make private. Pass in line and
|
||||
line_width, rather than calling location_get_source_line. Drop
|
||||
returned value.
|
||||
(layout::print_leading_fixits): New method.
|
||||
(layout::print_any_fixits): Rename to...
|
||||
(layout::print_trailing_fixits): ...this, and make private.
|
||||
Don't print newline fixits.
|
||||
(diagnostic_show_locus): Move logic for printing one row into...
|
||||
(layout::print_line): ...this new function. Move the
|
||||
location_get_source_line call and error-handling from
|
||||
print_source_line to here. Call print_leading_fixits, and rename
|
||||
print_any_fixits to print_trailing_fixits.
|
||||
(selftest::test_fixit_insert_containing_newline): Update now that
|
||||
newlines are partially supported.
|
||||
(selftest::test_fixit_insert_containing_newline_2): New test.
|
||||
(selftest::test_fixit_replace_containing_newline): Update comments.
|
||||
(selftest::diagnostic_show_locus_c_tests): Call the new test.
|
||||
* edit-context.c (class added_line): New class.
|
||||
(class edited_line): Describe newline handling in comment.
|
||||
(edited_line::actually_edited_p): New method.
|
||||
(edited_line::print_content): Delete redundant decl.
|
||||
(edited_line::m_predecessors): New field.
|
||||
(edited_file::print_content): Call edited_line::print_content.
|
||||
(edited_file::print_diff): Update to support newlines.
|
||||
(edited_file::print_diff_hunk): Likewise.
|
||||
(edited_file::print_run_of_changed_lines): New function.
|
||||
(edited_file::print_diff_line): Convert to...
|
||||
(print_diff_line): ...this.
|
||||
(edited_file::get_effective_line_count): New function.
|
||||
(edited_line::edited_line): Initialize new field m_predecessors.
|
||||
(edited_line::~edited_line): Clean up m_predecessors.
|
||||
(edited_line::apply_fixit): Handle newlines.
|
||||
(edited_line::get_effective_line_count): New function.
|
||||
(edited_line::print_content): New function.
|
||||
(edited_line::print_diff_lines): New function.
|
||||
(selftest::test_applying_fixits_insert_containing_newline): New
|
||||
test.
|
||||
(selftest::test_applying_fixits_replace_containing_newline): New
|
||||
test.
|
||||
(selftest::insert_line): New function.
|
||||
(selftest::test_applying_fixits_multiple_lines): Add example of
|
||||
inserting a line.
|
||||
(selftest::edit_context_c_tests): Call the new tests.
|
||||
|
||||
2017-05-02 Bin Cheng <bin.cheng@arm.com>
|
||||
|
||||
* tree-ssa-loop-ivopts.c (get_scaled_computation_cost_at): Delete
|
||||
|
|
|
|||
|
|
@ -203,16 +203,20 @@ class layout
|
|||
|
||||
expanded_location get_expanded_location (const line_span *) const;
|
||||
|
||||
bool print_source_line (int row, line_bounds *lbounds_out);
|
||||
bool should_print_annotation_line_p (int row) const;
|
||||
void print_annotation_line (int row, const line_bounds lbounds);
|
||||
bool annotation_line_showed_range_p (int line, int start_column,
|
||||
int finish_column) const;
|
||||
void print_any_fixits (int row);
|
||||
|
||||
void show_ruler (int max_column) const;
|
||||
void print_line (int row);
|
||||
|
||||
private:
|
||||
void print_leading_fixits (int row);
|
||||
void print_source_line (int row, const char *line, int line_width,
|
||||
line_bounds *lbounds_out);
|
||||
bool should_print_annotation_line_p (int row) const;
|
||||
void print_annotation_line (int row, const line_bounds lbounds);
|
||||
void print_trailing_fixits (int row);
|
||||
|
||||
bool annotation_line_showed_range_p (int line, int start_column,
|
||||
int finish_column) const;
|
||||
void show_ruler (int max_column) const;
|
||||
|
||||
bool validate_fixit_hint_p (const fixit_hint *hint);
|
||||
|
||||
void calculate_line_spans ();
|
||||
|
|
@ -1055,21 +1059,15 @@ layout::calculate_line_spans ()
|
|||
}
|
||||
}
|
||||
|
||||
/* Attempt to print line ROW of source code, potentially colorized at any
|
||||
ranges.
|
||||
Return true if the line was printed, populating *LBOUNDS_OUT.
|
||||
Return false if the source line could not be read, leaving *LBOUNDS_OUT
|
||||
untouched. */
|
||||
/* Print line ROW of source code, potentially colorized at any ranges, and
|
||||
populate *LBOUNDS_OUT.
|
||||
LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
|
||||
is its width. */
|
||||
|
||||
bool
|
||||
layout::print_source_line (int row, line_bounds *lbounds_out)
|
||||
void
|
||||
layout::print_source_line (int row, const char *line, int line_width,
|
||||
line_bounds *lbounds_out)
|
||||
{
|
||||
int line_width;
|
||||
const char *line = location_get_source_line (m_exploc.file, row,
|
||||
&line_width);
|
||||
if (!line)
|
||||
return false;
|
||||
|
||||
m_colorizer.set_normal_text ();
|
||||
|
||||
/* We will stop printing the source line at any trailing
|
||||
|
|
@ -1124,7 +1122,6 @@ layout::print_source_line (int row, line_bounds *lbounds_out)
|
|||
|
||||
lbounds_out->m_first_non_ws = first_non_ws;
|
||||
lbounds_out->m_last_non_ws = last_non_ws;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Determine if we should print an annotation line for ROW.
|
||||
|
|
@ -1186,7 +1183,46 @@ layout::print_annotation_line (int row, const line_bounds lbounds)
|
|||
print_newline ();
|
||||
}
|
||||
|
||||
/* Subroutine of layout::print_any_fixits.
|
||||
/* If there are any fixit hints inserting new lines before source line ROW,
|
||||
print them.
|
||||
|
||||
They are printed on lines of their own, before the source line
|
||||
itself, with a leading '+'. */
|
||||
|
||||
void
|
||||
layout::print_leading_fixits (int row)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
|
||||
{
|
||||
const fixit_hint *hint = m_fixit_hints[i];
|
||||
|
||||
if (!hint->ends_with_newline_p ())
|
||||
/* Not a newline fixit; print it in print_trailing_fixits. */
|
||||
continue;
|
||||
|
||||
gcc_assert (hint->insertion_p ());
|
||||
|
||||
if (hint->affects_line_p (m_exploc.file, row))
|
||||
{
|
||||
/* Printing the '+' with normal colorization
|
||||
and the inserted line with "insert" colorization
|
||||
helps them stand out from each other, and from
|
||||
the surrounding text. */
|
||||
m_colorizer.set_normal_text ();
|
||||
pp_character (m_pp, '+');
|
||||
m_colorizer.set_fixit_insert ();
|
||||
/* Print all but the trailing newline of the fix-it hint.
|
||||
We have to print the newline separately to avoid
|
||||
getting additional pp prefixes printed. */
|
||||
for (size_t i = 0; i < hint->get_length () - 1; i++)
|
||||
pp_character (m_pp, hint->get_string ()[i]);
|
||||
m_colorizer.set_normal_text ();
|
||||
pp_newline (m_pp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Subroutine of layout::print_trailing_fixits.
|
||||
|
||||
Determine if the annotation line printed for LINE contained
|
||||
the exact range from START_COLUMN to FINISH_COLUMN. */
|
||||
|
|
@ -1208,15 +1244,21 @@ layout::annotation_line_showed_range_p (int line, int start_column,
|
|||
|
||||
/* If there are any fixit hints on source line ROW, print them.
|
||||
They are printed in order, attempting to combine them onto lines, but
|
||||
starting new lines if necessary. */
|
||||
starting new lines if necessary.
|
||||
Fix-it hints that insert new lines are handled separately,
|
||||
in layout::print_leading_fixits. */
|
||||
|
||||
void
|
||||
layout::print_any_fixits (int row)
|
||||
layout::print_trailing_fixits (int row)
|
||||
{
|
||||
int column = 0;
|
||||
for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
|
||||
{
|
||||
const fixit_hint *hint = m_fixit_hints[i];
|
||||
|
||||
if (hint->ends_with_newline_p ())
|
||||
continue;
|
||||
|
||||
if (hint->affects_line_p (m_exploc.file, row))
|
||||
{
|
||||
/* For now we assume each fixit hint can only touch one line. */
|
||||
|
|
@ -1416,6 +1458,27 @@ layout::show_ruler (int max_column) const
|
|||
pp_newline (m_pp);
|
||||
}
|
||||
|
||||
/* Print leading fix-its (for new lines inserted before the source line)
|
||||
then the source line, followed by an annotation line
|
||||
consisting of any caret/underlines, then any fixits.
|
||||
If the source line can't be read, print nothing. */
|
||||
void
|
||||
layout::print_line (int row)
|
||||
{
|
||||
int line_width;
|
||||
const char *line = location_get_source_line (m_exploc.file, row,
|
||||
&line_width);
|
||||
if (!line)
|
||||
return;
|
||||
|
||||
line_bounds lbounds;
|
||||
print_leading_fixits (row);
|
||||
print_source_line (row, line, line_width, &lbounds);
|
||||
if (should_print_annotation_line_p (row))
|
||||
print_annotation_line (row, lbounds);
|
||||
print_trailing_fixits (row);
|
||||
}
|
||||
|
||||
} /* End of anonymous namespace. */
|
||||
|
||||
/* Print the physical source code corresponding to the location of
|
||||
|
|
@ -1460,18 +1523,7 @@ diagnostic_show_locus (diagnostic_context * context,
|
|||
}
|
||||
int last_line = line_span->get_last_line ();
|
||||
for (int row = line_span->get_first_line (); row <= last_line; row++)
|
||||
{
|
||||
/* Print the source line, followed by an annotation line
|
||||
consisting of any caret/underlines, then any fixits.
|
||||
If the source line can't be read, print nothing. */
|
||||
line_bounds lbounds;
|
||||
if (layout.print_source_line (row, &lbounds))
|
||||
{
|
||||
if (layout.should_print_annotation_line_p (row))
|
||||
layout.print_annotation_line (row, lbounds);
|
||||
layout.print_any_fixits (row);
|
||||
}
|
||||
}
|
||||
layout.print_line (row);
|
||||
}
|
||||
|
||||
pp_set_prefix (context->printer, saved_prefix);
|
||||
|
|
@ -2101,8 +2153,7 @@ test_fixit_consolidation (const line_table_case &case_)
|
|||
}
|
||||
}
|
||||
|
||||
/* Insertion fix-it hint: adding a "break;" on a line by itself.
|
||||
This will fail, as newlines aren't yet supported. */
|
||||
/* Insertion fix-it hint: adding a "break;" on a line by itself. */
|
||||
|
||||
static void
|
||||
test_fixit_insert_containing_newline (const line_table_case &case_)
|
||||
|
|
@ -2119,32 +2170,97 @@ test_fixit_insert_containing_newline (const line_table_case &case_)
|
|||
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");
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Add a "break;" on a line by itself before line 3 i.e. before
|
||||
column 1 of line 3. */
|
||||
{
|
||||
rich_location richloc (line_table, case_loc);
|
||||
richloc.add_fixit_insert_before (line_start, " break;\n");
|
||||
test_diagnostic_context dc;
|
||||
diagnostic_show_locus (&dc, &richloc, DK_ERROR);
|
||||
ASSERT_STREQ ("\n"
|
||||
"+ break;\n"
|
||||
" case 'b':\n"
|
||||
" ^~~~~~~~~\n",
|
||||
pp_formatted_text (dc.printer));
|
||||
}
|
||||
|
||||
/* Verify that attempts to add text with a newline fail when the
|
||||
insertion point is *not* at the start of a line. */
|
||||
{
|
||||
rich_location richloc (line_table, case_loc);
|
||||
richloc.add_fixit_insert_before (case_start, "break;\n");
|
||||
ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
|
||||
test_diagnostic_context dc;
|
||||
diagnostic_show_locus (&dc, &richloc, DK_ERROR);
|
||||
ASSERT_STREQ ("\n"
|
||||
" case 'b':\n"
|
||||
" ^~~~~~~~~\n",
|
||||
pp_formatted_text (dc.printer));
|
||||
}
|
||||
}
|
||||
|
||||
/* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
|
||||
of the file, where the fix-it is printed in a different line-span
|
||||
to the primary range of the diagnostic. */
|
||||
|
||||
static void
|
||||
test_fixit_insert_containing_newline_2 (const line_table_case &case_)
|
||||
{
|
||||
/* Create a tempfile and write some text to it.
|
||||
.........................0000000001111111.
|
||||
.........................1234567890123456. */
|
||||
const char *old_content = ("test (int ch)\n" /* line 1. */
|
||||
"{\n" /* line 2. */
|
||||
" putchar (ch);\n" /* line 3. */
|
||||
"}\n"); /* line 4. */
|
||||
|
||||
temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
|
||||
line_table_test ltt (case_);
|
||||
|
||||
const line_map_ordinary *ord_map = linemap_check_ordinary
|
||||
(linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
|
||||
linemap_line_start (line_table, 1, 100);
|
||||
|
||||
/* The primary range is the "putchar" token. */
|
||||
location_t putchar_start
|
||||
= linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
|
||||
location_t putchar_finish
|
||||
= linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
|
||||
location_t putchar_loc
|
||||
= make_location (putchar_start, putchar_start, putchar_finish);
|
||||
rich_location richloc (line_table, putchar_loc);
|
||||
|
||||
/* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
|
||||
location_t file_start
|
||||
= linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
|
||||
richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
|
||||
|
||||
if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
|
||||
return;
|
||||
|
||||
test_diagnostic_context dc;
|
||||
diagnostic_show_locus (&dc, &richloc, DK_ERROR);
|
||||
ASSERT_STREQ ("\n"
|
||||
" case 'b':\n"
|
||||
" ^~~~~~~~~\n",
|
||||
"FILENAME:1:1:\n"
|
||||
"+#include <stdio.h>\n"
|
||||
" test (int ch)\n"
|
||||
"FILENAME:3:2:\n"
|
||||
" putchar (ch);\n"
|
||||
" ^~~~~~~\n",
|
||||
pp_formatted_text (dc.printer));
|
||||
}
|
||||
|
||||
/* Replacement fix-it hint containing a newline.
|
||||
This will fail, as newlines aren't yet supported. */
|
||||
This will fail, as newlines are only supported when inserting at the
|
||||
beginning of a line. */
|
||||
|
||||
static void
|
||||
test_fixit_replace_containing_newline (const line_table_case &case_)
|
||||
|
|
@ -2167,7 +2283,7 @@ 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
|
||||
/* Arbitrary newlines are not yet supported within fix-it hints, so
|
||||
the fix-it should not be displayed. */
|
||||
ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
|
||||
|
||||
|
|
@ -2199,6 +2315,7 @@ diagnostic_show_locus_c_tests ()
|
|||
for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
|
||||
for_each_line_table_case (test_fixit_consolidation);
|
||||
for_each_line_table_case (test_fixit_insert_containing_newline);
|
||||
for_each_line_table_case (test_fixit_insert_containing_newline_2);
|
||||
for_each_line_table_case (test_fixit_replace_containing_newline);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,23 +86,52 @@ class edited_file
|
|||
private:
|
||||
bool print_content (pretty_printer *pp);
|
||||
void print_diff (pretty_printer *pp, bool show_filenames);
|
||||
void print_diff_hunk (pretty_printer *pp, int start_of_hunk,
|
||||
int end_of_hunk);
|
||||
void print_diff_line (pretty_printer *pp, char prefix_char,
|
||||
const char *line, int line_size);
|
||||
int print_diff_hunk (pretty_printer *pp, int old_start_of_hunk,
|
||||
int old_end_of_hunk, int new_start_of_hunk);
|
||||
edited_line *get_line (int line);
|
||||
edited_line *get_or_insert_line (int line);
|
||||
int get_num_lines (bool *missing_trailing_newline);
|
||||
|
||||
int get_effective_line_count (int old_start_of_hunk,
|
||||
int old_end_of_hunk);
|
||||
|
||||
void print_run_of_changed_lines (pretty_printer *pp,
|
||||
int start_of_run,
|
||||
int end_of_run);
|
||||
|
||||
const char *m_filename;
|
||||
typed_splay_tree<int, edited_line *> m_edited_lines;
|
||||
int m_num_lines;
|
||||
};
|
||||
|
||||
/* A line added before an edited_line. */
|
||||
|
||||
class added_line
|
||||
{
|
||||
public:
|
||||
added_line (const char *content, int len)
|
||||
: m_content (xstrndup (content, len)), m_len (len) {}
|
||||
~added_line () { free (m_content); }
|
||||
|
||||
const char *get_content () const { return m_content; }
|
||||
int get_len () const { return m_len; }
|
||||
|
||||
private:
|
||||
char *m_content;
|
||||
int m_len;
|
||||
};
|
||||
|
||||
/* The state of one edited line within an edited_file.
|
||||
As well as the current content of the line, it contains a record of
|
||||
the changes, so that further changes can be applied in the correct
|
||||
place. */
|
||||
place.
|
||||
|
||||
When handling fix-it hints containing newlines, new lines are added
|
||||
as added_line predecessors to an edited_line. Hence it's possible
|
||||
for an "edited_line" to not actually have been changed, but to merely
|
||||
be a placeholder for the lines added before it. This can be tested
|
||||
for with actuall_edited_p, and has a slight effect on how diff hunks
|
||||
are generated. */
|
||||
|
||||
class edited_line
|
||||
{
|
||||
|
|
@ -121,16 +150,25 @@ class edited_line
|
|||
const char *replacement_str,
|
||||
int replacement_len);
|
||||
|
||||
int get_effective_line_count () const;
|
||||
|
||||
/* Has the content of this line actually changed, or are we merely
|
||||
recording predecessor added_lines? */
|
||||
bool actually_edited_p () const { return m_line_events.length () > 0; }
|
||||
|
||||
void print_content (pretty_printer *pp) const;
|
||||
void print_diff_lines (pretty_printer *pp) const;
|
||||
|
||||
private:
|
||||
void ensure_capacity (int len);
|
||||
void ensure_terminated ();
|
||||
void print_content (pretty_printer *pp) const;
|
||||
|
||||
int m_line_num;
|
||||
char *m_content;
|
||||
int m_len;
|
||||
int m_alloc_sz;
|
||||
auto_vec <line_event> m_line_events;
|
||||
auto_vec <added_line *> m_predecessors;
|
||||
};
|
||||
|
||||
/* Class for representing edit events that have occurred on one line of
|
||||
|
|
@ -160,6 +198,12 @@ class line_event
|
|||
int m_delta;
|
||||
};
|
||||
|
||||
/* Forward decls. */
|
||||
|
||||
static void
|
||||
print_diff_line (pretty_printer *pp, char prefix_char,
|
||||
const char *line, int line_size);
|
||||
|
||||
/* Implementation of class edit_context. */
|
||||
|
||||
/* edit_context's ctor. */
|
||||
|
|
@ -375,7 +419,7 @@ edited_file::print_content (pretty_printer *pp)
|
|||
{
|
||||
edited_line *el = get_line (line_num);
|
||||
if (el)
|
||||
pp_string (pp, el->get_content ());
|
||||
el->print_content (pp);
|
||||
else
|
||||
{
|
||||
int len;
|
||||
|
|
@ -417,6 +461,10 @@ edited_file::print_diff (pretty_printer *pp, bool show_filenames)
|
|||
|
||||
const int context_lines = 3;
|
||||
|
||||
/* Track new line numbers minus old line numbers. */
|
||||
|
||||
int line_delta = 0;
|
||||
|
||||
while (el)
|
||||
{
|
||||
int start_of_hunk = el->get_line_num ();
|
||||
|
|
@ -432,39 +480,53 @@ edited_file::print_diff (pretty_printer *pp, bool show_filenames)
|
|||
= m_edited_lines.successor (el->get_line_num ());
|
||||
if (!next_el)
|
||||
break;
|
||||
if (el->get_line_num () + context_lines
|
||||
|
||||
int end_of_printed_hunk = el->get_line_num () + context_lines;
|
||||
if (!el->actually_edited_p ())
|
||||
end_of_printed_hunk--;
|
||||
|
||||
if (end_of_printed_hunk
|
||||
>= next_el->get_line_num () - context_lines)
|
||||
el = next_el;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
int end_of_hunk = el->get_line_num ();
|
||||
end_of_hunk += context_lines;
|
||||
if (!el->actually_edited_p ())
|
||||
end_of_hunk--;
|
||||
if (end_of_hunk > line_count)
|
||||
end_of_hunk = line_count;
|
||||
|
||||
print_diff_hunk (pp, start_of_hunk, end_of_hunk);
|
||||
|
||||
int new_start_of_hunk = start_of_hunk + line_delta;
|
||||
line_delta += print_diff_hunk (pp, start_of_hunk, end_of_hunk,
|
||||
new_start_of_hunk);
|
||||
el = m_edited_lines.successor (el->get_line_num ());
|
||||
}
|
||||
}
|
||||
|
||||
/* 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.
|
||||
NEW_START_OF_HUNK is a line number in the edited version of the file.
|
||||
Return the change in the line count within the hunk. */
|
||||
|
||||
void
|
||||
edited_file::print_diff_hunk (pretty_printer *pp, int start_of_hunk,
|
||||
int end_of_hunk)
|
||||
int
|
||||
edited_file::print_diff_hunk (pretty_printer *pp, int old_start_of_hunk,
|
||||
int old_end_of_hunk, int new_start_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;
|
||||
int new_num_lines
|
||||
= get_effective_line_count (old_start_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)
|
||||
|
|
@ -475,34 +537,8 @@ edited_file::print_diff_hunk (pretty_printer *pp, int start_of_hunk,
|
|||
while (get_line (line_num))
|
||||
line_num++;
|
||||
const int last_changed_line_in_run = line_num - 1;
|
||||
|
||||
/* Show old version of lines. */
|
||||
pp_string (pp, colorize_start (pp_show_color (pp),
|
||||
"diff-delete"));
|
||||
for (line_num = first_changed_line_in_run;
|
||||
line_num <= last_changed_line_in_run;
|
||||
line_num++)
|
||||
{
|
||||
int line_len;
|
||||
const char *old_line
|
||||
= location_get_source_line (m_filename, line_num, &line_len);
|
||||
print_diff_line (pp, '-', old_line, line_len);
|
||||
}
|
||||
pp_string (pp, colorize_stop (pp_show_color (pp)));
|
||||
|
||||
/* Show new version of lines. */
|
||||
pp_string (pp, colorize_start (pp_show_color (pp),
|
||||
"diff-insert"));
|
||||
for (line_num = first_changed_line_in_run;
|
||||
line_num <= last_changed_line_in_run;
|
||||
line_num++)
|
||||
{
|
||||
edited_line *el_in_run = get_line (line_num);
|
||||
gcc_assert (el_in_run);
|
||||
print_diff_line (pp, '+', el_in_run->get_content (),
|
||||
el_in_run->get_len ());
|
||||
}
|
||||
pp_string (pp, colorize_stop (pp_show_color (pp)));
|
||||
print_run_of_changed_lines (pp, first_changed_line_in_run,
|
||||
last_changed_line_in_run);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -514,15 +550,59 @@ edited_file::print_diff_hunk (pretty_printer *pp, int start_of_hunk,
|
|||
line_num++;
|
||||
}
|
||||
}
|
||||
|
||||
return new_num_lines - old_num_lines;
|
||||
}
|
||||
|
||||
/* Subroutine of edited_file::print_diff_hunk: given a run of lines
|
||||
from START_OF_RUN to END_OF_RUN that all have edited_line instances,
|
||||
print the diff to PP. */
|
||||
|
||||
void
|
||||
edited_file::print_run_of_changed_lines (pretty_printer *pp,
|
||||
int start_of_run,
|
||||
int end_of_run)
|
||||
{
|
||||
/* Show old version of lines. */
|
||||
pp_string (pp, colorize_start (pp_show_color (pp),
|
||||
"diff-delete"));
|
||||
for (int line_num = start_of_run;
|
||||
line_num <= end_of_run;
|
||||
line_num++)
|
||||
{
|
||||
edited_line *el_in_run = get_line (line_num);
|
||||
gcc_assert (el_in_run);
|
||||
if (el_in_run->actually_edited_p ())
|
||||
{
|
||||
int line_len;
|
||||
const char *old_line
|
||||
= location_get_source_line (m_filename, line_num, &line_len);
|
||||
print_diff_line (pp, '-', old_line, line_len);
|
||||
}
|
||||
}
|
||||
pp_string (pp, colorize_stop (pp_show_color (pp)));
|
||||
|
||||
/* Show new version of lines. */
|
||||
pp_string (pp, colorize_start (pp_show_color (pp),
|
||||
"diff-insert"));
|
||||
for (int line_num = start_of_run;
|
||||
line_num <= end_of_run;
|
||||
line_num++)
|
||||
{
|
||||
edited_line *el_in_run = get_line (line_num);
|
||||
gcc_assert (el_in_run);
|
||||
el_in_run->print_diff_lines (pp);
|
||||
}
|
||||
pp_string (pp, colorize_stop (pp_show_color (pp)));
|
||||
}
|
||||
|
||||
/* Print one line within a diff, starting with PREFIX_CHAR,
|
||||
followed by the LINE of content, of length LEN. LINE is
|
||||
not necessarily 0-terminated. Print a trailing newline. */
|
||||
|
||||
void
|
||||
edited_file::print_diff_line (pretty_printer *pp, char prefix_char,
|
||||
const char *line, int len)
|
||||
static void
|
||||
print_diff_line (pretty_printer *pp, char prefix_char,
|
||||
const char *line, int len)
|
||||
{
|
||||
pp_character (pp, prefix_char);
|
||||
for (int i = 0; i < len; i++)
|
||||
|
|
@ -530,6 +610,27 @@ edited_file::print_diff_line (pretty_printer *pp, char prefix_char,
|
|||
pp_character (pp, '\n');
|
||||
}
|
||||
|
||||
/* Determine the number of lines that will be present after
|
||||
editing for the range of lines from OLD_START_OF_HUNK to
|
||||
OLD_END_OF_HUNK inclusive. */
|
||||
|
||||
int
|
||||
edited_file::get_effective_line_count (int old_start_of_hunk,
|
||||
int old_end_of_hunk)
|
||||
{
|
||||
int line_count = 0;
|
||||
for (int old_line_num = old_start_of_hunk; old_line_num <= old_end_of_hunk;
|
||||
old_line_num++)
|
||||
{
|
||||
edited_line *el = get_line (old_line_num);
|
||||
if (el)
|
||||
line_count += el->get_effective_line_count ();
|
||||
else
|
||||
line_count++;
|
||||
}
|
||||
return line_count;
|
||||
}
|
||||
|
||||
/* Get the state of LINE within the file, or NULL if it is untouched. */
|
||||
|
||||
edited_line *
|
||||
|
|
@ -591,7 +692,8 @@ edited_file::get_num_lines (bool *missing_trailing_newline)
|
|||
edited_line::edited_line (const char *filename, int line_num)
|
||||
: m_line_num (line_num),
|
||||
m_content (NULL), m_len (0), m_alloc_sz (0),
|
||||
m_line_events ()
|
||||
m_line_events (),
|
||||
m_predecessors ()
|
||||
{
|
||||
const char *line = location_get_source_line (filename, line_num,
|
||||
&m_len);
|
||||
|
|
@ -606,7 +708,12 @@ edited_line::edited_line (const char *filename, int line_num)
|
|||
|
||||
edited_line::~edited_line ()
|
||||
{
|
||||
unsigned i;
|
||||
added_line *pred;
|
||||
|
||||
free (m_content);
|
||||
FOR_EACH_VEC_ELT (m_predecessors, i, pred)
|
||||
delete pred;
|
||||
}
|
||||
|
||||
/* A callback for deleting edited_line *, for use as a
|
||||
|
|
@ -643,6 +750,17 @@ edited_line::apply_fixit (int start_column,
|
|||
const char *replacement_str,
|
||||
int replacement_len)
|
||||
{
|
||||
/* Handle newlines. They will only ever be at the end of the
|
||||
replacement text, thanks to the filtering in rich_location. */
|
||||
if (replacement_len > 1)
|
||||
if (replacement_str[replacement_len - 1] == '\n')
|
||||
{
|
||||
/* Stash in m_predecessors, stripping off newline. */
|
||||
m_predecessors.safe_push (new added_line (replacement_str,
|
||||
replacement_len - 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
start_column = get_effective_column (start_column);
|
||||
next_column = get_effective_column (next_column);
|
||||
|
||||
|
|
@ -689,6 +807,57 @@ edited_line::apply_fixit (int start_column,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Determine the number of lines that will be present after
|
||||
editing for this line. Typically this is just 1, but
|
||||
if newlines have been added before this line, they will
|
||||
also be counted. */
|
||||
|
||||
int
|
||||
edited_line::get_effective_line_count () const
|
||||
{
|
||||
return m_predecessors.length () + 1;
|
||||
}
|
||||
|
||||
/* Subroutine of edited_file::print_content.
|
||||
Print this line and any new lines added before it, to PP. */
|
||||
|
||||
void
|
||||
edited_line::print_content (pretty_printer *pp) const
|
||||
{
|
||||
unsigned i;
|
||||
added_line *pred;
|
||||
FOR_EACH_VEC_ELT (m_predecessors, i, pred)
|
||||
{
|
||||
pp_string (pp, pred->get_content ());
|
||||
pp_newline (pp);
|
||||
}
|
||||
pp_string (pp, m_content);
|
||||
}
|
||||
|
||||
/* Subroutine of edited_file::print_run_of_changed_lines for
|
||||
printing diff hunks to PP.
|
||||
Print the '+' line for this line, and any newlines added
|
||||
before it.
|
||||
Note that if this edited_line was actually edited, the '-'
|
||||
line has already been printed. If it wasn't, then we merely
|
||||
have a placeholder edited_line for adding newlines to, and
|
||||
we need to print a ' ' line for the edited_line as we haven't
|
||||
printed it yet. */
|
||||
|
||||
void
|
||||
edited_line::print_diff_lines (pretty_printer *pp) const
|
||||
{
|
||||
unsigned i;
|
||||
added_line *pred;
|
||||
FOR_EACH_VEC_ELT (m_predecessors, i, pred)
|
||||
print_diff_line (pp, '+', pred->get_content (),
|
||||
pred->get_len ());
|
||||
if (actually_edited_p ())
|
||||
print_diff_line (pp, '+', m_content, m_len);
|
||||
else
|
||||
print_diff_line (pp, ' ', m_content, m_len);
|
||||
}
|
||||
|
||||
/* Ensure that the buffer for m_content is at least large enough to hold
|
||||
a string of length LEN and its 0-terminator, doubling on repeated
|
||||
allocations. */
|
||||
|
|
@ -967,6 +1136,57 @@ 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. */
|
||||
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);
|
||||
}
|
||||
|
||||
/* Test applying a "replace" fixit that grows the affected line. */
|
||||
|
||||
static void
|
||||
|
|
@ -1057,6 +1277,44 @@ 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 = ");
|
||||
|
||||
/* Newlines are only supported within fix-it hints that
|
||||
are at the start of lines (for entirely new lines), hence
|
||||
this fix-it should not be displayed. */
|
||||
ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* Test applying a "remove" fixit. */
|
||||
|
||||
static void
|
||||
|
|
@ -1204,6 +1462,32 @@ change_line (edit_context &edit, int line_num)
|
|||
return loc;
|
||||
}
|
||||
|
||||
/* Subroutine of test_applying_fixits_multiple_lines.
|
||||
Add the text "INSERTED\n" in front of the given line. */
|
||||
|
||||
static location_t
|
||||
insert_line (edit_context &edit, int line_num)
|
||||
{
|
||||
const line_map_ordinary *ord_map
|
||||
= LINEMAPS_LAST_ORDINARY_MAP (line_table);
|
||||
const int column = 1;
|
||||
location_t loc =
|
||||
linemap_position_for_line_and_column (line_table, ord_map,
|
||||
line_num, column);
|
||||
|
||||
expanded_location exploc = expand_location (loc);
|
||||
if (loc <= LINE_MAP_MAX_LOCATION_WITH_COLS)
|
||||
{
|
||||
ASSERT_EQ (line_num, exploc.line);
|
||||
ASSERT_EQ (column, exploc.column);
|
||||
}
|
||||
|
||||
rich_location insert (line_table, loc);
|
||||
insert.add_fixit_insert_before ("INSERTED\n");
|
||||
edit.add_fixits (&insert);
|
||||
return loc;
|
||||
}
|
||||
|
||||
/* Test of editing multiple lines within a long file,
|
||||
to ensure that diffs are generated as expected. */
|
||||
|
||||
|
|
@ -1229,6 +1513,7 @@ test_applying_fixits_multiple_lines (const line_table_case &case_)
|
|||
change_line (edit, 2);
|
||||
change_line (edit, 3);
|
||||
change_line (edit, 4);
|
||||
insert_line (edit, 5);
|
||||
|
||||
/* A run of nearby lines, within the contextual limit. */
|
||||
change_line (edit, 150);
|
||||
|
|
@ -1240,7 +1525,7 @@ test_applying_fixits_multiple_lines (const line_table_case &case_)
|
|||
|
||||
/* Verify diff. */
|
||||
auto_free <char *> diff = edit.generate_diff (false);
|
||||
ASSERT_STREQ ("@@ -1,7 +1,7 @@\n"
|
||||
ASSERT_STREQ ("@@ -1,7 +1,8 @@\n"
|
||||
" line 1\n"
|
||||
"-line 2\n"
|
||||
"-line 3\n"
|
||||
|
|
@ -1248,10 +1533,11 @@ test_applying_fixits_multiple_lines (const line_table_case &case_)
|
|||
"+CHANGED: line 2\n"
|
||||
"+CHANGED: line 3\n"
|
||||
"+CHANGED: line 4\n"
|
||||
"+INSERTED\n"
|
||||
" line 5\n"
|
||||
" line 6\n"
|
||||
" line 7\n"
|
||||
"@@ -147,10 +147,10 @@\n"
|
||||
"@@ -147,10 +148,10 @@\n"
|
||||
" line 147\n"
|
||||
" line 148\n"
|
||||
" line 149\n"
|
||||
|
|
@ -1510,8 +1796,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);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,16 @@
|
|||
2017-05-02 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* gcc.dg/plugin/diagnostic-test-show-locus-bw.c
|
||||
(test_fixit_insert_newline): New function.
|
||||
* gcc.dg/plugin/diagnostic-test-show-locus-color.c
|
||||
(test_fixit_insert_newline): New function.
|
||||
* gcc.dg/plugin/diagnostic-test-show-locus-generate-patch.c
|
||||
(test_fixit_insert_newline): New function.
|
||||
* gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c
|
||||
(test_fixit_insert_newline): New function.
|
||||
* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
|
||||
(test_show_locus): Handle test_fixit_insert_newline.
|
||||
|
||||
2017-05-02 Bin Cheng <bin.cheng@arm.com>
|
||||
|
||||
* g++.dg/tree-ssa/ivopts-3.C: Adjust test string.
|
||||
|
|
|
|||
|
|
@ -249,3 +249,23 @@ void test_many_nested_locations (void)
|
|||
MOLLIT ANIM ID EST LABORUM
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
/* Unit test for rendering of fix-it hints that add new lines. */
|
||||
|
||||
void test_fixit_insert_newline (void)
|
||||
{
|
||||
#if 0
|
||||
switch (op)
|
||||
{
|
||||
case 'a':
|
||||
x = a;
|
||||
case 'b': /* { dg-warning "newline insertion" } */
|
||||
x = b;
|
||||
}
|
||||
/* { dg-begin-multiline-output "" }
|
||||
+ break;
|
||||
case 'b':
|
||||
^~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,3 +193,23 @@ void test_fixit_replace (void)
|
|||
{ dg-end-multiline-output "" } */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Unit test for rendering of fix-it hints that add new lines. */
|
||||
|
||||
void test_fixit_insert_newline (void)
|
||||
{
|
||||
#if 0
|
||||
switch (op)
|
||||
{
|
||||
case 'a':
|
||||
x = a;
|
||||
case 'b': /* { dg-warning "newline insertion" } */
|
||||
x = b;
|
||||
}
|
||||
/* { dg-begin-multiline-output "" }
|
||||
+[32m[K break;[m[K
|
||||
[01;35m[Kcase 'b'[m[K:
|
||||
[01;35m[K^~~~~~~~[m[K
|
||||
{ dg-end-multiline-output "" } */
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,20 @@ void test_fixit_replace (void)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Unit test for rendering of fix-it hints that add new lines. */
|
||||
|
||||
void test_fixit_insert_newline (void)
|
||||
{
|
||||
#if 0
|
||||
switch (op)
|
||||
{
|
||||
case 'a':
|
||||
x = a;
|
||||
case 'b': /* { dg-warning "newline insertion" } */
|
||||
x = b;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Verify the output from -fdiagnostics-generate-patch.
|
||||
We expect a header, containing the filename. This is the absolute path,
|
||||
|
|
@ -74,4 +87,12 @@ void test_fixit_replace (void)
|
|||
#endif
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
{
|
||||
case 'a':
|
||||
x = a;
|
||||
+ break;
|
||||
case 'b':
|
||||
x = b;
|
||||
}
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
|
|
|||
|
|
@ -39,3 +39,19 @@ void test_fixit_replace (void)
|
|||
/* { dg-regexp "fix-it:.*\\{38:3-38:21\\}:.*" } */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Unit test for rendering of fix-it hints that add new lines. */
|
||||
|
||||
void test_fixit_insert_newline (void)
|
||||
{
|
||||
#if 0
|
||||
switch (op)
|
||||
{
|
||||
case 'a':
|
||||
x = a;
|
||||
case 'b': /* { dg-warning "newline insertion" } */
|
||||
x = b;
|
||||
}
|
||||
/* { dg-regexp "fix-it:.*\\{52:1-52:1\\}:.*\\n" } */
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,6 +268,18 @@ test_show_locus (function *fun)
|
|||
warning_at_rich_loc (&richloc, 0, "example of insertion hints");
|
||||
}
|
||||
|
||||
if (0 == strcmp (fnname, "test_fixit_insert_newline"))
|
||||
{
|
||||
const int line = fnstart_line + 6;
|
||||
location_t line_start = get_loc (line, 0);
|
||||
location_t case_start = get_loc (line, 4);
|
||||
location_t case_finish = get_loc (line, 11);
|
||||
location_t case_loc = make_location (case_start, case_start, case_finish);
|
||||
rich_location richloc (line_table, case_loc);
|
||||
richloc.add_fixit_insert_before (line_start, " break;\n");
|
||||
warning_at_rich_loc (&richloc, 0, "example of newline insertion hint");
|
||||
}
|
||||
|
||||
if (0 == strcmp (fnname, "test_fixit_remove"))
|
||||
{
|
||||
const int line = fnstart_line + 2;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,14 @@
|
|||
2017-05-02 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* include/line-map.h (class rich_location): Update description of
|
||||
newline handling.
|
||||
(class fixit_hint): Likewise.
|
||||
(fixit_hint::ends_with_newline_p): New decl.
|
||||
* line-map.c (rich_location::maybe_add_fixit): Support newlines
|
||||
in fix-it hints that are insertions of single lines at the start
|
||||
of a line. Don't consolidate into such fix-it hints.
|
||||
(fixit_hint::ends_with_newline_p): New method.
|
||||
|
||||
2017-05-01 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* include/line-map.h (source_range::intersects_line_p): Delete.
|
||||
|
|
|
|||
|
|
@ -1551,7 +1551,11 @@ class fixit_hint;
|
|||
|
||||
Attempts to add a fix-it hint within a macro expansion will fail.
|
||||
|
||||
We do not yet support newlines in fix-it text; attempts to do so will fail.
|
||||
There is only limited support for newline characters in fix-it hints:
|
||||
only hints with newlines which insert an entire new line are permitted,
|
||||
inserting at the start of a line, and finishing with a newline
|
||||
(with no interior newline characters). Other attempts to add
|
||||
fix-it hints containing newline characters will fail.
|
||||
|
||||
The rich_location API handles these failures gracefully, so that
|
||||
diagnostics can attempt to add fix-it hints without each needing
|
||||
|
|
@ -1690,7 +1694,13 @@ protected:
|
|||
[start, next_loc)
|
||||
Insertions have start == next_loc: "replace" the empty string at the
|
||||
start location with the new string.
|
||||
Deletions are replacement with the empty string. */
|
||||
Deletions are replacement with the empty string.
|
||||
|
||||
There is only limited support for newline characters in fix-it hints
|
||||
as noted above in the comment for class rich_location.
|
||||
A fixit_hint instance can have at most one newline character; if
|
||||
present, the newline character must be the final character of
|
||||
the content (preventing e.g. fix-its that split a pre-existing line). */
|
||||
|
||||
class fixit_hint
|
||||
{
|
||||
|
|
@ -1712,6 +1722,8 @@ class fixit_hint
|
|||
|
||||
bool insertion_p () const { return m_start == m_next_loc; }
|
||||
|
||||
bool ends_with_newline_p () const;
|
||||
|
||||
private:
|
||||
/* We don't use source_range here since, unlike most places,
|
||||
this is a half-open/half-closed range:
|
||||
|
|
|
|||
|
|
@ -2325,16 +2325,44 @@ rich_location::maybe_add_fixit (source_location start,
|
|||
if (reject_impossible_fixit (next_loc))
|
||||
return;
|
||||
|
||||
/* We do not yet support newlines within fix-it hints. */
|
||||
if (strchr (new_content, '\n'))
|
||||
const char *newline = strchr (new_content, '\n');
|
||||
if (newline)
|
||||
{
|
||||
stop_supporting_fixits ();
|
||||
return;
|
||||
/* For now, we can only support insertion of whole lines
|
||||
i.e. starts at start of line, and the newline is at the end of
|
||||
the insertion point. */
|
||||
|
||||
/* It must be an insertion, not a replacement/deletion. */
|
||||
if (start != next_loc)
|
||||
{
|
||||
stop_supporting_fixits ();
|
||||
return;
|
||||
}
|
||||
|
||||
/* The insertion must be at the start of a line. */
|
||||
expanded_location exploc_start
|
||||
= linemap_client_expand_location_to_spelling_point (start);
|
||||
if (exploc_start.column != 1)
|
||||
{
|
||||
stop_supporting_fixits ();
|
||||
return;
|
||||
}
|
||||
|
||||
/* The newline must be at end of NEW_CONTENT.
|
||||
We could eventually split up fix-its at newlines if we wanted
|
||||
to allow more generality (e.g. to allow adding multiple lines
|
||||
with one add_fixit call. */
|
||||
if (newline[1] != '\0')
|
||||
{
|
||||
stop_supporting_fixits ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Consolidate neighboring fixits. */
|
||||
/* Consolidate neighboring fixits.
|
||||
Don't consolidate into newline-insertion fixits. */
|
||||
fixit_hint *prev = get_last_fixit_hint ();
|
||||
if (prev)
|
||||
if (prev && !prev->ends_with_newline_p ())
|
||||
if (prev->maybe_append (start, next_loc, new_content))
|
||||
return;
|
||||
|
||||
|
|
@ -2398,3 +2426,13 @@ fixit_hint::maybe_append (source_location start,
|
|||
m_bytes[m_len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return true iff this hint's content ends with a newline. */
|
||||
|
||||
bool
|
||||
fixit_hint::ends_with_newline_p () const
|
||||
{
|
||||
if (m_len == 0)
|
||||
return false;
|
||||
return m_bytes[m_len - 1] == '\n';
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue