const expanded_location *caret_exploc);
bool contains_point (int row, int column) const;
+ bool intersects_line_p (int row) const;
layout_point m_start;
layout_point m_finish;
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, const rich_location *richloc);
+ void print_any_fixits (int row);
void show_ruler (int max_column) const;
private:
+ bool validate_fixit_hint_p (const fixit_hint *hint);
+
void calculate_line_spans ();
void print_newline ();
colorizer m_colorizer;
bool m_colorize_source_p;
auto_vec <layout_range> m_layout_ranges;
+ auto_vec <const fixit_hint *> m_fixit_hints;
auto_vec <line_span> m_line_spans;
int m_x_offset;
};
return column <= m_finish.m_column;
}
+/* Does this layout_range contain any part of line ROW? */
+
+bool
+layout_range::intersects_line_p (int row) const
+{
+ gcc_assert (m_start.m_line <= m_finish.m_line);
+ if (row < m_start.m_line)
+ return false;
+ if (row > m_finish.m_line)
+ return false;
+ return true;
+}
+
#if CHECKING_P
-/* A helper function for testing layout_range::contains_point. */
+/* A helper function for testing layout_range. */
static layout_range
make_range (int start_line, int start_col, int end_line, int end_col)
&start_exploc);
}
-/* Selftests for layout_range::contains_point. */
+/* Selftests for layout_range::contains_point and
+ layout_range::intersects_line_p. */
-/* Selftest for layout_range::contains_point where the layout_range
+/* Selftest for layout_range, where the layout_range
is a range with start==end i.e. a single point. */
static void
-test_range_contains_point_for_single_point ()
+test_layout_range_for_single_point ()
{
layout_range point = make_range (7, 10, 7, 10);
+ /* Tests for layout_range::contains_point. */
+
/* Before the line. */
ASSERT_FALSE (point.contains_point (6, 1));
/* After the line. */
ASSERT_FALSE (point.contains_point (8, 1));
+
+ /* Tests for layout_range::intersects_line_p. */
+ ASSERT_FALSE (point.intersects_line_p (6));
+ ASSERT_TRUE (point.intersects_line_p (7));
+ ASSERT_FALSE (point.intersects_line_p (8));
}
-/* Selftest for layout_range::contains_point where the layout_range
+/* Selftest for layout_range, where the layout_range
is the single-line range shown as "Example A" above. */
static void
-test_range_contains_point_for_single_line ()
+test_layout_range_for_single_line ()
{
layout_range example_a = make_range (2, 22, 2, 38);
+ /* Tests for layout_range::contains_point. */
+
/* Before the line. */
ASSERT_FALSE (example_a.contains_point (1, 1));
/* After the line. */
ASSERT_FALSE (example_a.contains_point (2, 39));
+
+ /* Tests for layout_range::intersects_line_p. */
+ ASSERT_FALSE (example_a.intersects_line_p (1));
+ ASSERT_TRUE (example_a.intersects_line_p (2));
+ ASSERT_FALSE (example_a.intersects_line_p (3));
}
-/* Selftest for layout_range::contains_point where the layout_range
+/* Selftest for layout_range, where the layout_range
is the multi-line range shown as "Example B" above. */
static void
-test_range_contains_point_for_multiple_lines ()
+test_layout_range_for_multiple_lines ()
{
layout_range example_b = make_range (3, 14, 5, 8);
+ /* Tests for layout_range::contains_point. */
+
/* Before first line. */
ASSERT_FALSE (example_b.contains_point (1, 1));
/* After the line. */
ASSERT_FALSE (example_b.contains_point (6, 1));
+
+ /* Tests for layout_range::intersects_line_p. */
+ ASSERT_FALSE (example_b.intersects_line_p (2));
+ ASSERT_TRUE (example_b.intersects_line_p (3));
+ ASSERT_TRUE (example_b.intersects_line_p (4));
+ ASSERT_TRUE (example_b.intersects_line_p (5));
+ ASSERT_FALSE (example_b.intersects_line_p (6));
}
#endif /* #if CHECKING_P */
/* Constructor for class layout.
Filter the ranges from the rich_location to those that we can
- sanely print, populating m_layout_ranges.
+ sanely print, populating m_layout_ranges and m_fixit_hints.
Determine the range of lines that we will print, splitting them
up into an ordered list of disjoint spans of contiguous line numbers.
Determine m_x_offset, to ensure that the primary caret
m_colorizer (context, diagnostic_kind),
m_colorize_source_p (context->colorize_source_p),
m_layout_ranges (richloc->get_num_locations ()),
+ m_fixit_hints (richloc->get_num_fixit_hints ()),
m_line_spans (1 + richloc->get_num_locations ()),
m_x_offset (0)
{
m_layout_ranges.safe_push (ri);
}
+ /* Populate m_fixit_hints, filtering to only those that are in the
+ same file. */
+ for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
+ {
+ const fixit_hint *hint = richloc->get_fixit_hint (i);
+ if (validate_fixit_hint_p (hint))
+ m_fixit_hints.safe_push (hint);
+ }
+
/* Populate m_line_spans. */
calculate_line_spans ();
}
}
+ /* Otherwise, use the location of the first fixit-hint present within
+ the line_span. */
+ for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
+ {
+ const fixit_hint *hint = m_fixit_hints[i];
+ location_t loc = hint->get_start_loc ();
+ expanded_location exploc = expand_location (loc);
+ if (line_span->contains_line_p (exploc.line))
+ return exploc;
+ }
+
/* It should not be possible to have a line span that didn't
- contain any of the layout_range instances. */
+ contain any of the layout_range or fixit_hint instances. */
gcc_unreachable ();
return m_exploc;
}
+/* Determine if HINT is meaningful to print within this layout. */
+
+bool
+layout::validate_fixit_hint_p (const fixit_hint *hint)
+{
+ switch (hint->get_kind ())
+ {
+ case fixit_hint::INSERT:
+ {
+ const fixit_insert *insert = static_cast <const fixit_insert *> (hint);
+ location_t loc = insert->get_location ();
+ if (LOCATION_FILE (loc) != m_exploc.file)
+ return false;
+ }
+ break;
+
+ case fixit_hint::REPLACE:
+ {
+ const fixit_replace *replace
+ = static_cast <const fixit_replace *> (hint);
+ source_range src_range = replace->get_range ();
+ if (LOCATION_FILE (src_range.m_start) != m_exploc.file)
+ return false;
+ if (LOCATION_FILE (src_range.m_finish) != m_exploc.file)
+ return false;
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ return true;
+}
+
+/* Determine the range of lines affected by HINT.
+ This assumes that HINT has already been filtered by
+ validate_fixit_hint_p, and so affects the correct source file. */
+
+static line_span
+get_line_span_for_fixit_hint (const fixit_hint *hint)
+{
+ gcc_assert (hint);
+ switch (hint->get_kind ())
+ {
+ case fixit_hint::INSERT:
+ {
+ const fixit_insert *insert = static_cast <const fixit_insert *> (hint);
+ location_t loc = insert->get_location ();
+ int line = LOCATION_LINE (loc);
+ return line_span (line, line);
+ }
+ break;
+
+ case fixit_hint::REPLACE:
+ {
+ const fixit_replace *replace
+ = static_cast <const fixit_replace *> (hint);
+ source_range src_range = replace->get_range ();
+ return line_span (LOCATION_LINE (src_range.m_start),
+ LOCATION_LINE (src_range.m_finish));
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
/* We want to print the pertinent source code at a diagnostic. The
rich_location can contain multiple locations. This will have been
filtered into m_exploc (the caret for the primary location) and
lr->m_finish.m_line));
}
+ /* Also add spans for any fix-it hints, in case they cover other lines. */
+ for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
+ {
+ const fixit_hint *hint = m_fixit_hints[i];
+ gcc_assert (hint);
+ tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
+ }
+
/* Sort them. */
tmp_spans.qsort(line_span::comparator);
return true;
}
+/* Determine if we should print an annotation line for ROW.
+ i.e. if any of m_layout_ranges contains ROW. */
+
+bool
+layout::should_print_annotation_line_p (int row) const
+{
+ layout_range *range;
+ int i;
+ FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
+ if (range->intersects_line_p (row))
+ return true;
+ return false;
+}
+
/* Print a line consisting of the caret/underlines for the given
source line. */
return false;
}
-/* If there are any fixit hints on source line ROW within RICHLOC, print them.
+/* 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. */
void
-layout::print_any_fixits (int row, const rich_location *richloc)
+layout::print_any_fixits (int row)
{
int column = 0;
- for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
+ for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
{
- fixit_hint *hint = richloc->get_fixit_hint (i);
+ const fixit_hint *hint = m_fixit_hints[i];
if (hint->affects_line_p (m_exploc.file, row))
{
/* For now we assume each fixit hint can only touch one line. */
{
case fixit_hint::INSERT:
{
- fixit_insert *insert = static_cast <fixit_insert *> (hint);
+ const fixit_insert *insert
+ = static_cast <const fixit_insert *> (hint);
/* This assumes the insertion just affects one line. */
int start_column
= LOCATION_COLUMN (insert->get_location ());
case fixit_hint::REPLACE:
{
- fixit_replace *replace = static_cast <fixit_replace *> (hint);
+ const fixit_replace *replace
+ = static_cast <const fixit_replace *> (hint);
source_range src_range = replace->get_range ();
int line = LOCATION_LINE (src_range.m_start);
int start_column = LOCATION_COLUMN (src_range.m_start);
line_bounds lbounds;
if (layout.print_source_line (row, &lbounds))
{
- layout.print_annotation_line (row, lbounds);
- layout.print_any_fixits (row, richloc);
+ if (layout.should_print_annotation_line_p (row))
+ layout.print_annotation_line (row, lbounds);
+ layout.print_any_fixits (row);
}
}
}
{
diagnostic_initialize (this, 0);
show_caret = true;
+ show_column = true;
+ start_span = start_span_cb;
}
~test_diagnostic_context ()
{
diagnostic_finish (this);
}
+
+ /* Implementation of diagnostic_start_span_fn, hiding the
+ real filename (to avoid printing the names of tempfiles). */
+ static void
+ start_span_cb (diagnostic_context *context, expanded_location exploc)
+ {
+ exploc.file = "FILENAME";
+ default_diagnostic_start_span_fn (context, exploc);
+ }
};
/* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
test_one_liner_many_fixits ();
}
+/* Verify that we print fixits even if they only affect lines
+ outside those covered by the ranges in the rich_location. */
+
+static void
+test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
+{
+ /* Create a tempfile and write some text to it.
+ ...000000000111111111122222222223333333333.
+ ...123456789012345678901234567890123456789. */
+ const char *content
+ = ("struct point { double x; double y; };\n" /* line 1. */
+ "struct point origin = {x: 0.0,\n" /* line 2. */
+ " y\n" /* line 3. */
+ "\n" /* line 4. */
+ "\n" /* line 5. */
+ " : 0.0};\n"); /* line 6. */
+ temp_source_file tmp (SELFTEST_LOCATION, ".c", 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);
+
+ const location_t final_line_end
+ = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
+
+ /* Don't attempt to run the tests if column data might be unavailable. */
+ if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
+ return;
+
+ /* A pair of tests for modernizing the initializers to C99-style. */
+
+ /* The one-liner case (line 2). */
+ {
+ test_diagnostic_context dc;
+ const location_t x
+ = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
+ 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_replace (colon, "=");
+ diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+ ASSERT_STREQ ("\n"
+ " struct point origin = {x: 0.0,\n"
+ " ^\n"
+ " .=\n",
+ pp_formatted_text (dc.printer));
+ }
+
+ /* The multiline case. The caret for the rich_location is on line 6;
+ verify that insertion fixit on line 3 is still printed (and that
+ span starts are printed due to the gap between the span at line 3
+ and that at line 6). */
+ {
+ test_diagnostic_context dc;
+ const location_t y
+ = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
+ 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_replace (colon, "=");
+ diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+ ASSERT_STREQ ("\n"
+ "FILENAME:3:24:\n"
+ " y\n"
+ " .\n"
+ "FILENAME:6:25:\n"
+ " : 0.0};\n"
+ " ^\n"
+ " =\n",
+ pp_formatted_text (dc.printer));
+ }
+}
+
+
/* Verify that fix-it hints are appropriately consolidated.
If any fix-it hints in a rich_location involve locations beyond
void
diagnostic_show_locus_c_tests ()
{
- test_range_contains_point_for_single_point ();
- test_range_contains_point_for_single_line ();
- test_range_contains_point_for_multiple_lines ();
+ test_layout_range_for_single_point ();
+ test_layout_range_for_single_line ();
+ test_layout_range_for_multiple_lines ();
test_get_line_width_without_trailing_whitespace ();
test_diagnostic_show_locus_unknown_location ();
for_each_line_table_case (test_diagnostic_show_locus_one_liner);
+ for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
for_each_line_table_case (test_fixit_consolidation);
}