void print_source_line (linenum_type row, const char *line, int line_width,
line_bounds *lbounds_out);
bool should_print_annotation_line_p (linenum_type row) const;
+ void start_annotation_line () const;
void print_annotation_line (linenum_type row, const line_bounds lbounds);
void print_trailing_fixits (linenum_type row);
int last_non_ws);
void
- move_to_column (int *column, int dest_column);
+ move_to_column (int *column, int dest_column, bool add_left_margin);
private:
diagnostic_context *m_context;
expanded_location m_exploc;
colorizer m_colorizer;
bool m_colorize_source_p;
+ bool m_show_line_numbers_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_linenum_width;
int m_x_offset;
};
return hint_a->get_start_loc () - hint_b->get_start_loc ();
}
+/* Get the number of digits in the decimal representation
+ of VALUE. */
+
+static int
+num_digits (int value)
+{
+ /* Perhaps simpler to use log10 for this, but doing it this way avoids
+ using floating point. */
+ gcc_assert (value >= 0);
+
+ if (value == 0)
+ return 1;
+
+ int digits = 0;
+ while (value > 0)
+ {
+ digits++;
+ value /= 10;
+ }
+ return digits;
+}
+
+
+#if CHECKING_P
+
+/* Selftest for num_digits. */
+
+static void
+test_num_digits ()
+{
+ ASSERT_EQ (1, num_digits (0));
+ ASSERT_EQ (1, num_digits (9));
+ ASSERT_EQ (2, num_digits (10));
+ ASSERT_EQ (2, num_digits (99));
+ ASSERT_EQ (3, num_digits (100));
+ ASSERT_EQ (3, num_digits (999));
+ ASSERT_EQ (4, num_digits (1000));
+ ASSERT_EQ (4, num_digits (9999));
+ ASSERT_EQ (5, num_digits (10000));
+ ASSERT_EQ (5, num_digits (99999));
+ ASSERT_EQ (6, num_digits (100000));
+ ASSERT_EQ (6, num_digits (999999));
+ ASSERT_EQ (7, num_digits (1000000));
+ ASSERT_EQ (7, num_digits (9999999));
+ ASSERT_EQ (8, num_digits (10000000));
+ ASSERT_EQ (8, num_digits (99999999));
+}
+
+#endif /* #if CHECKING_P */
+
/* Implementation of class layout. */
/* Constructor for class layout.
m_exploc (richloc->get_expanded_location (0)),
m_colorizer (context, diagnostic_kind),
m_colorize_source_p (context->colorize_source_p),
+ m_show_line_numbers_p (context->show_line_numbers_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_linenum_width (0),
m_x_offset (0)
{
for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
/* Populate m_line_spans. */
calculate_line_spans ();
+ /* Determine m_linenum_width. */
+ gcc_assert (m_line_spans.length () > 0);
+ const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
+ int highest_line = last_span->m_last_line;
+ if (highest_line < 0)
+ highest_line = 0;
+ m_linenum_width = num_digits (highest_line);
+
/* Adjust m_x_offset.
Center the primary caret to fit in max_width; all columns
will be adjusted accordingly. */
{
size_t right_margin = CARET_LINE_MARGIN;
size_t column = m_exploc.column;
+ if (m_show_line_numbers_p)
+ column += m_linenum_width + 2;
right_margin = MIN (line.length () - column, right_margin);
right_margin = max_width - right_margin;
if (line.length () >= max_width && column > right_margin)
line_width);
line += m_x_offset;
- pp_space (m_pp);
+ if (m_show_line_numbers_p)
+ {
+ int width = num_digits (row);
+ for (int i = 0; i < m_linenum_width - width; i++)
+ pp_space (m_pp);
+ pp_printf (m_pp, "%i | ", row);
+ }
+ else
+ pp_space (m_pp);
int first_non_ws = INT_MAX;
int last_non_ws = 0;
int column;
return false;
}
+/* Begin an annotation line. If m_show_line_numbers_p, print the left
+ margin, which is empty for annotation lines. Otherwise, do nothing. */
+
+void
+layout::start_annotation_line () const
+{
+ if (m_show_line_numbers_p)
+ {
+ for (int i = 0; i < m_linenum_width; i++)
+ pp_space (m_pp);
+ pp_string (m_pp, " |");
+ }
+}
+
/* Print a line consisting of the caret/underlines for the given
source line. */
int x_bound = get_x_bound_for_row (row, m_exploc.column,
lbounds.m_last_non_ws);
+ start_annotation_line ();
pp_space (m_pp);
+
for (int column = 1 + m_x_offset; column < x_bound; column++)
{
bool in_range_p;
helps them stand out from each other, and from
the surrounding text. */
m_colorizer.set_normal_text ();
+ start_annotation_line ();
pp_character (m_pp, '+');
m_colorizer.set_fixit_insert ();
/* Print all but the trailing newline of the fix-it hint.
correction *c;
int column = m_x_offset;
+ if (!corrections.m_corrections.is_empty ())
+ start_annotation_line ();
+
FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
{
/* For now we assume each fixit hint can only touch one line. */
{
/* This assumes the insertion just affects one line. */
int start_column = c->m_printed_columns.start;
- move_to_column (&column, start_column);
+ move_to_column (&column, start_column, true);
m_colorizer.set_fixit_insert ();
pp_string (m_pp, c->m_text);
m_colorizer.set_normal_text ();
finish_column)
|| c->m_len == 0)
{
- move_to_column (&column, start_column);
+ move_to_column (&column, start_column, true);
m_colorizer.set_fixit_delete ();
for (; column <= finish_column; column++)
pp_character (m_pp, '-');
a new line) if we have actual replacement text. */
if (c->m_len > 0)
{
- move_to_column (&column, start_column);
+ move_to_column (&column, start_column, true);
m_colorizer.set_fixit_insert ();
pp_string (m_pp, c->m_text);
m_colorizer.set_normal_text ();
}
/* Add a trailing newline, if necessary. */
- move_to_column (&column, 0);
+ move_to_column (&column, 0, false);
}
/* Disable any colorization and emit a newline. */
/* Given *COLUMN as an x-coordinate, print spaces to position
successive output at DEST_COLUMN, printing a newline if necessary,
- and updating *COLUMN. */
+ and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
+ left margin after any newline. */
void
-layout::move_to_column (int *column, int dest_column)
+layout::move_to_column (int *column, int dest_column, bool add_left_margin)
{
/* Start a new line if we need to. */
if (*column > dest_column)
{
print_newline ();
+ if (add_left_margin)
+ start_annotation_line ();
*column = m_x_offset;
}
/* Hundreds. */
if (max_column > 99)
{
+ start_annotation_line ();
pp_space (m_pp);
for (int column = 1 + m_x_offset; column <= max_column; column++)
if (column % 10 == 0)
}
/* Tens. */
+ start_annotation_line ();
pp_space (m_pp);
for (int column = 1 + m_x_offset; column <= max_column; column++)
if (column % 10 == 0)
pp_newline (m_pp);
/* Units. */
+ start_annotation_line ();
pp_space (m_pp);
for (int column = 1 + m_x_offset; column <= max_column; column++)
pp_character (m_pp, '0' + (column % 10));
pp_formatted_text (dc.printer));
}
+/* Verify that line numbers are correctly printed for the case of
+ a multiline range in which the width of the line numbers changes
+ (e.g. from "9" to "10"). */
+
+static void
+test_line_numbers_multiline_range ()
+{
+ /* Create a tempfile and write some text to it. */
+ pretty_printer pp;
+ for (int i = 0; i < 20; i++)
+ /* .........0000000001111111.
+ .............1234567890123456. */
+ pp_printf (&pp, "this is line %i\n", i + 1);
+ temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
+ line_table_test ltt;
+
+ 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);
+
+ /* Create a multi-line location, starting at the "line" of line 9, with
+ a caret on the "is" of line 10, finishing on the "this" line 11. */
+
+ location_t start
+ = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
+ location_t caret
+ = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
+ location_t finish
+ = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
+ location_t loc = make_location (caret, start, finish);
+
+ test_diagnostic_context dc;
+ dc.show_line_numbers_p = true;
+ gcc_rich_location richloc (loc);
+ diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+ ASSERT_STREQ ("\n"
+ " 9 | this is line 9\n"
+ " | ~~~~~~\n"
+ "10 | this is line 10\n"
+ " | ~~~~~^~~~~~~~~~\n"
+ "11 | this is line 11\n"
+ " | ~~~~ \n",
+ pp_formatted_text (dc.printer));
+}
+
/* Run all of the selftests within this file. */
void
diagnostic_show_locus_c_tests ()
{
test_line_span ();
+ test_num_digits ();
test_layout_range_for_single_point ();
test_layout_range_for_single_line ();
for_each_line_table_case (test_fixit_insert_containing_newline_2);
for_each_line_table_case (test_fixit_replace_containing_newline);
for_each_line_table_case (test_fixit_deletion_affecting_newline);
+
+ test_line_numbers_multiline_range ();
}
} // namespace selftest
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret -fdiagnostics-show-line-numbers" } */
+
+/* This is a collection of unittests for diagnostic_show_locus;
+ see the overview in diagnostic_plugin_test_show_locus.c.
+
+ In particular, note the discussion of why we need a very long line here:
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+ and that we can't use macros in this file. */
+
+void test_simple (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+14 | myvar = myvar.x;
+ | ~~~~~^~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_multiline (void)
+{
+#if 0
+ x = (first_function ()
+ + second_function ()); /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+26 | x = (first_function ()
+ | ~~~~~~~~~~~~~~~~~
+27 | + second_function ());
+ | ^ ~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_very_wide_line (void)
+{
+#if 0
+ float f = foo * bar; /* { dg-warning "95: test" } */
+/* { dg-begin-multiline-output "" }
+ | 0 0 0 0 0 0 1
+ | 4 5 6 7 8 9 0
+ | 0123456789012345678901234567890123456789012345678901234567890123456789
+41 | float f = foo * bar;
+ | ~~~~^~~~~
+ | bar * foo
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+/* Unit test for rendering of insertion fixit hints
+ (example taken from PR 62316). */
+
+void test_fixit_insert (void)
+{
+#if 0
+ int a[2][2] = { 0, 1 , 2, 3 }; /* { dg-warning "insertion hints" } */
+/* { dg-begin-multiline-output "" }
+59 | int a[2][2] = { 0, 1 , 2, 3 };
+ | ^~~~
+ | { }
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+/* Unit test for rendering of "remove" fixit hints. */
+
+void test_fixit_remove (void)
+{
+#if 0
+ int a;; /* { dg-warning "example of a removal hint" } */
+/* { dg-begin-multiline-output "" }
+73 | int a;;
+ | ^
+ | -
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+/* Unit test for rendering of "replace" fixit hints. */
+
+void test_fixit_replace (void)
+{
+#if 0
+ gtk_widget_showall (dlg); /* { dg-warning "example of a replacement hint" } */
+/* { dg-begin-multiline-output "" }
+87 | gtk_widget_showall (dlg);
+ | ^~~~~~~~~~~~~~~~~~
+ | gtk_widget_show_all
+ { 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 "" }
+ |+ break;
+106 | case 'b':
+ | ^~~~~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}