Mercurial > hg > CbC > CbC_gcc
diff gcc/diagnostic-show-locus.c @ 131:84e7813d76e9
gcc-8.2
author | mir3636 |
---|---|
date | Thu, 25 Oct 2018 07:37:49 +0900 |
parents | 04ced10e8804 |
children | 1830386684a0 |
line wrap: on
line diff
--- a/gcc/diagnostic-show-locus.c Fri Oct 27 22:46:09 2017 +0900 +++ b/gcc/diagnostic-show-locus.c Thu Oct 25 07:37:49 2018 +0900 @@ -1,5 +1,5 @@ /* Diagnostic subroutines for printing source-code - Copyright (C) 1999-2017 Free Software Foundation, Inc. + Copyright (C) 1999-2018 Free Software Foundation, Inc. Contributed by Gabriel Dos Reis <gdr@codesourcery.com> This file is part of GCC. @@ -29,6 +29,7 @@ #include "diagnostic-color.h" #include "gcc-rich-location.h" #include "selftest.h" +#include "selftest-diagnostic.h" #ifdef HAVE_TERMIOS_H # include <termios.h> @@ -114,7 +115,7 @@ : m_line (exploc.line), m_column (exploc.column) {} - int m_line; + linenum_type m_line; int m_column; }; @@ -125,16 +126,20 @@ public: layout_range (const expanded_location *start_exploc, const expanded_location *finish_exploc, - bool show_caret_p, - const expanded_location *caret_exploc); - - bool contains_point (int row, int column) const; - bool intersects_line_p (int row) const; + enum range_display_kind range_display_kind, + const expanded_location *caret_exploc, + unsigned original_idx, + const range_label *label); + + bool contains_point (linenum_type row, int column) const; + bool intersects_line_p (linenum_type row) const; layout_point m_start; layout_point m_finish; - bool m_show_caret_p; + enum range_display_kind m_range_display_kind; layout_point m_caret; + unsigned m_original_idx; + const range_label *m_label; }; /* A struct for use by layout::print_source_line for telling @@ -171,16 +176,52 @@ { const line_span *ls1 = (const line_span *)p1; const line_span *ls2 = (const line_span *)p2; - int first_line_diff = (int)ls1->m_first_line - (int)ls2->m_first_line; - if (first_line_diff) - return first_line_diff; - return (int)ls1->m_last_line - (int)ls2->m_last_line; + int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line); + if (first_line_cmp) + return first_line_cmp; + return compare (ls1->m_last_line, ls2->m_last_line); } linenum_type m_first_line; linenum_type m_last_line; }; +#if CHECKING_P + +/* Selftests for line_span. */ + +static void +test_line_span () +{ + line_span line_one (1, 1); + ASSERT_EQ (1, line_one.get_first_line ()); + ASSERT_EQ (1, line_one.get_last_line ()); + ASSERT_FALSE (line_one.contains_line_p (0)); + ASSERT_TRUE (line_one.contains_line_p (1)); + ASSERT_FALSE (line_one.contains_line_p (2)); + + line_span lines_1_to_3 (1, 3); + ASSERT_EQ (1, lines_1_to_3.get_first_line ()); + ASSERT_EQ (3, lines_1_to_3.get_last_line ()); + ASSERT_TRUE (lines_1_to_3.contains_line_p (1)); + ASSERT_TRUE (lines_1_to_3.contains_line_p (3)); + + ASSERT_EQ (0, line_span::comparator (&line_one, &line_one)); + ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0); + ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0); + + /* A linenum > 2^31. */ + const linenum_type LARGEST_LINE = 0xffffffff; + line_span largest_line (LARGEST_LINE, LARGEST_LINE); + ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ()); + ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ()); + + ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0); + ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0); +} + +#endif /* #if CHECKING_P */ + /* A class to control the overall layout when printing a diagnostic. The layout is determined within the constructor. @@ -197,27 +238,31 @@ diagnostic_t diagnostic_kind); bool maybe_add_location_range (const location_range *loc_range, + unsigned original_idx, bool restrict_to_current_line_spans); int get_num_line_spans () const { return m_line_spans.length (); } const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; } + void print_gap_in_line_numbering (); bool print_heading_for_line_span_index_p (int line_span_idx) const; expanded_location get_expanded_location (const line_span *) const; - void print_line (int row); + void print_line (linenum_type row); private: - bool will_show_line_p (int row) const; - void print_leading_fixits (int row); - void print_source_line (int row, const char *line, int line_width, + bool will_show_line_p (linenum_type row) const; + void print_leading_fixits (linenum_type row); + void print_source_line (linenum_type 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, + bool should_print_annotation_line_p (linenum_type row) const; + void start_annotation_line (char margin_char = ' ') const; + void print_annotation_line (linenum_type row, const line_bounds lbounds); + void print_any_labels (linenum_type row); + void print_trailing_fixits (linenum_type row); + + bool annotation_line_showed_range_p (linenum_type line, int start_column, int finish_column) const; void show_ruler (int max_column) const; @@ -229,29 +274,31 @@ bool get_state_at_point (/* Inputs. */ - int row, int column, + linenum_type row, int column, int first_non_ws, int last_non_ws, /* Outputs. */ point_state *out_state); int - get_x_bound_for_row (int row, int caret_column, + get_x_bound_for_row (linenum_type row, int caret_column, 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; pretty_printer *m_pp; - diagnostic_t m_diagnostic_kind; location_t m_primary_loc; expanded_location m_exploc; colorizer m_colorizer; bool m_colorize_source_p; + bool m_show_labels_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; }; @@ -367,12 +414,16 @@ layout_range::layout_range (const expanded_location *start_exploc, const expanded_location *finish_exploc, - bool show_caret_p, - const expanded_location *caret_exploc) + enum range_display_kind range_display_kind, + const expanded_location *caret_exploc, + unsigned original_idx, + const range_label *label) : m_start (*start_exploc), m_finish (*finish_exploc), - m_show_caret_p (show_caret_p), - m_caret (*caret_exploc) + m_range_display_kind (range_display_kind), + m_caret (*caret_exploc), + m_original_idx (original_idx), + m_label (label) { } @@ -416,7 +467,7 @@ - 'a' indicates a subsequent point *after* the range. */ bool -layout_range::contains_point (int row, int column) const +layout_range::contains_point (linenum_type row, int column) const { gcc_assert (m_start.m_line <= m_finish.m_line); /* ...but the equivalent isn't true for the columns; @@ -477,7 +528,7 @@ /* Does this layout_range contain any part of line ROW? */ bool -layout_range::intersects_line_p (int row) const +layout_range::intersects_line_p (linenum_type row) const { gcc_assert (m_start.m_line <= m_finish.m_line); if (row < m_start.m_line) @@ -498,8 +549,8 @@ = {"test.c", start_line, start_col, NULL, false}; const expanded_location finish_exploc = {"test.c", end_line, end_col, NULL, false}; - return layout_range (&start_exploc, &finish_exploc, false, - &start_exploc); + return layout_range (&start_exploc, &finish_exploc, SHOW_RANGE_WITHOUT_CARET, + &start_exploc, 0, NULL); } /* Selftests for layout_range::contains_point and @@ -638,7 +689,7 @@ while (result > 0) { char ch = line[result - 1]; - if (ch == ' ' || ch == '\t') + if (ch == ' ' || ch == '\t' || ch == '\r') result--; else break; @@ -647,7 +698,8 @@ gcc_assert (result <= line_width); gcc_assert (result == 0 || (line[result - 1] != ' ' - && line[result -1] != '\t')); + && line[result -1] != '\t' + && line[result -1] != '\r')); return result; } @@ -672,9 +724,11 @@ assert_eq ("", 0); assert_eq (" ", 0); assert_eq ("\t", 0); + assert_eq ("\r", 0); assert_eq ("hello world", 11); assert_eq ("hello world ", 11); assert_eq ("hello world \t\t ", 11); + assert_eq ("hello world\r", 11); } #endif /* #if CHECKING_P */ @@ -765,6 +819,56 @@ 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. @@ -781,14 +885,16 @@ diagnostic_t diagnostic_kind) : m_context (context), m_pp (context->printer), - m_diagnostic_kind (diagnostic_kind), m_primary_loc (richloc->get_range (0)->m_loc), m_exploc (richloc->get_expanded_location (0)), m_colorizer (context, diagnostic_kind), m_colorize_source_p (context->colorize_source_p), + m_show_labels_p (context->show_labels_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++) @@ -796,7 +902,7 @@ /* This diagnostic printer can only cope with "sufficiently sane" ranges. Ignore any ranges that are awkward to handle. */ const location_range *loc_range = richloc->get_range (idx); - maybe_add_location_range (loc_range, false); + maybe_add_location_range (loc_range, idx, false); } /* Populate m_fixit_hints, filtering to only those that are in the @@ -814,20 +920,34 @@ /* 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); + /* If we're showing jumps in the line-numbering, allow at least 3 chars. */ + if (m_line_spans.length () > 1) + m_linenum_width = MAX (m_linenum_width, 3); + /* If there's a minimum margin width, apply it (subtracting 1 for the space + after the line number. */ + m_linenum_width = MAX (m_linenum_width, context->min_margin_width - 1); + /* Adjust m_x_offset. Center the primary caret to fit in max_width; all columns will be adjusted accordingly. */ - int max_width = m_context->caret_max_width; - int line_width; - const char *line = location_get_source_line (m_exploc.file, m_exploc.line, - &line_width); - if (line && m_exploc.column <= line_width) + size_t max_width = m_context->caret_max_width; + char_span line = location_get_source_line (m_exploc.file, m_exploc.line); + if (line && (size_t)m_exploc.column <= line.length ()) { - int right_margin = CARET_LINE_MARGIN; - int column = m_exploc.column; - right_margin = MIN (line_width - column, right_margin); + 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_width >= max_width && column > right_margin) + if (line.length () >= max_width && column > right_margin) m_x_offset = column - right_margin; gcc_assert (m_x_offset >= 0); } @@ -839,6 +959,9 @@ /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to those that we can sanely print. + ORIGINAL_IDX is the index of LOC_RANGE within its rich_location, + (for use as extrinsic state by label ranges FIXME). + If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also filtered against this layout instance's current line spans: it will only be added if the location is fully within the lines @@ -848,6 +971,7 @@ bool layout::maybe_add_location_range (const location_range *loc_range, + unsigned original_idx, bool restrict_to_current_line_spans) { gcc_assert (loc_range); @@ -872,13 +996,13 @@ return false; if (finish.file != m_exploc.file) return false; - if (loc_range->m_show_caret_p) + if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET) if (caret.file != m_exploc.file) return false; /* Sanitize the caret location for non-primary ranges. */ if (m_layout_ranges.length () > 0) - if (loc_range->m_show_caret_p) + if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET) if (!compatible_locations_p (loc_range->m_loc, m_primary_loc)) /* Discard any non-primary ranges that can't be printed sanely relative to the primary location. */ @@ -886,7 +1010,8 @@ /* Everything is now known to be in the correct source file, but it may require further sanitization. */ - layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret); + layout_range ri (&start, &finish, loc_range->m_range_display_kind, &caret, + original_idx, loc_range->m_label); /* If we have a range that finishes before it starts (perhaps from something built via macro expansion), printing the @@ -922,7 +1047,7 @@ return false; if (!will_show_line_p (finish.line)) return false; - if (loc_range->m_show_caret_p) + if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET) if (!will_show_line_p (caret.line)) return false; } @@ -936,7 +1061,7 @@ /* Return true iff ROW is within one of the line spans for this layout. */ bool -layout::will_show_line_p (int row) const +layout::will_show_line_p (linenum_type row) const { for (int line_span_idx = 0; line_span_idx < get_num_line_spans (); line_span_idx++) @@ -948,6 +1073,20 @@ return false; } +/* Print a line showing a gap in the line numbers, for showing the boundary + between two line spans. */ + +void +layout::print_gap_in_line_numbering () +{ + gcc_assert (m_show_line_numbers_p); + + for (int i = 0; i < m_linenum_width + 1; i++) + pp_character (m_pp, '.'); + + pp_newline (m_pp); +} + /* Return true iff we should print a heading when starting the line span with the given index. */ @@ -1030,7 +1169,16 @@ get_line_span_for_fixit_hint (const fixit_hint *hint) { gcc_assert (hint); - return line_span (LOCATION_LINE (hint->get_start_loc ()), + + int start_line = LOCATION_LINE (hint->get_start_loc ()); + + /* For line-insertion fix-it hints, add the previous line to the + span, to give the user more context on the proposed change. */ + if (hint->ends_with_newline_p ()) + if (start_line > 1) + start_line--; + + return line_span (start_line, LOCATION_LINE (hint->get_next_loc ())); } @@ -1045,21 +1193,34 @@ This function populates m_line_spans with an ordered, disjoint list of the line spans of interest. - For example, if the primary caret location is on line 7, with ranges - covering lines 5-6 and lines 9-12: + Printing a gap between line spans takes one line, so, when printing + line numbers, we allow a gap of up to one line between spans when + merging, since it makes more sense to print the source line rather than a + "gap-in-line-numbering" line. When not printing line numbers, it's + better to be more explicit about what's going on, so keeping them as + separate spans is preferred. + + For example, if the primary range is on lines 8-10, with secondary ranges + covering lines 5-6 and lines 13-15: 004 - 005 |RANGE 0 - 006 |RANGE 0 - 007 |PRIMARY CARET - 008 - 009 |RANGE 1 - 010 |RANGE 1 - 011 |RANGE 1 - 012 |RANGE 1 - 013 - - then we want two spans: lines 5-7 and lines 9-12. */ + 005 |RANGE 1 + 006 |RANGE 1 + 007 + 008 |PRIMARY RANGE + 009 |PRIMARY CARET + 010 |PRIMARY RANGE + 011 + 012 + 013 |RANGE 2 + 014 |RANGE 2 + 015 |RANGE 2 + 016 + + With line numbering on, we want two spans: lines 5-10 and lines 13-15. + + With line numbering off (with span headers), we want three spans: lines 5-6, + lines 8-10, and lines 13-15. */ void layout::calculate_line_spans () @@ -1099,7 +1260,8 @@ line_span *current = &m_line_spans[m_line_spans.length () - 1]; const line_span *next = &tmp_spans[i]; gcc_assert (next->m_first_line >= current->m_first_line); - if (next->m_first_line <= current->m_last_line + 1) + const int merger_distance = m_show_line_numbers_p ? 1 : 0; + if (next->m_first_line <= current->m_last_line + 1 + merger_distance) { /* We can merge them. */ if (next->m_last_line > current->m_last_line) @@ -1134,7 +1296,7 @@ is its width. */ void -layout::print_source_line (int row, const char *line, int line_width, +layout::print_source_line (linenum_type row, const char *line, int line_width, line_bounds *lbounds_out) { m_colorizer.set_normal_text (); @@ -1145,7 +1307,15 @@ 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; @@ -1175,8 +1345,8 @@ else m_colorizer.set_normal_text (); } - char c = *line == '\t' ? ' ' : *line; - if (c == '\0') + char c = *line; + if (c == '\0' || c == '\t' || c == '\r') c = ' '; if (c != ' ') { @@ -1197,26 +1367,51 @@ i.e. if any of m_layout_ranges contains ROW. */ bool -layout::should_print_annotation_line_p (int row) const +layout::should_print_annotation_line_p (linenum_type row) const { layout_range *range; int i; FOR_EACH_VEC_ELT (m_layout_ranges, i, range) - if (range->intersects_line_p (row)) - return true; + { + if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE) + return false; + if (range->intersects_line_p (row)) + return true; + } 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 (char margin_char) const +{ + if (m_show_line_numbers_p) + { + /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3 + of it, right-aligned, padded with spaces. */ + int i; + for (i = 0; i < m_linenum_width - 3; i++) + pp_space (m_pp); + for (; i < m_linenum_width; i++) + pp_character (m_pp, margin_char); + pp_string (m_pp, " |"); + } +} + /* Print a line consisting of the caret/underlines for the given source line. */ void -layout::print_annotation_line (int row, const line_bounds lbounds) +layout::print_annotation_line (linenum_type row, const line_bounds lbounds) { 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; @@ -1252,6 +1447,180 @@ print_newline (); } +/* Implementation detail of layout::print_any_labels. + + A label within the given row of source. */ + +struct line_label +{ + line_label (int state_idx, int column, label_text text) + : m_state_idx (state_idx), m_column (column), + m_text (text), m_length (strlen (text.m_buffer)), + m_label_line (0) + {} + + /* Sorting is primarily by column, then by state index. */ + static int comparator (const void *p1, const void *p2) + { + const line_label *ll1 = (const line_label *)p1; + const line_label *ll2 = (const line_label *)p2; + int column_cmp = compare (ll1->m_column, ll2->m_column); + if (column_cmp) + return column_cmp; + return compare (ll1->m_state_idx, ll2->m_state_idx); + } + + int m_state_idx; + int m_column; + label_text m_text; + size_t m_length; + int m_label_line; +}; + +/* Print any labels in this row. */ +void +layout::print_any_labels (linenum_type row) +{ + int i; + auto_vec<line_label> labels; + + /* Gather the labels that are to be printed into "labels". */ + { + layout_range *range; + FOR_EACH_VEC_ELT (m_layout_ranges, i, range) + { + /* Most ranges don't have labels, so reject this first. */ + if (range->m_label == NULL) + continue; + + /* The range's caret must be on this line. */ + if (range->m_caret.m_line != row) + continue; + + /* Reject labels that aren't fully visible due to clipping + by m_x_offset. */ + if (range->m_caret.m_column <= m_x_offset) + continue; + + label_text text; + text = range->m_label->get_text (range->m_original_idx); + + /* Allow for labels that return NULL from their get_text + implementation (so e.g. such labels can control their own + visibility). */ + if (text.m_buffer == NULL) + continue; + + labels.safe_push (line_label (i, range->m_caret.m_column, text)); + } + } + + /* Bail out if there are no labels on this row. */ + if (labels.length () == 0) + return; + + /* Sort them. */ + labels.qsort(line_label::comparator); + + /* Figure out how many "label lines" we need, and which + one each label is printed in. + + For example, if the labels aren't too densely packed, + we can fit them on the same line, giving two "label lines": + + foo + bar + ~~~ ~~~ + | | : label line 0 + l0 l1 : label line 1 + + If they would touch each other or overlap, then we need + additional "label lines": + + foo + bar + ~~~ ~~~ + | | : label line 0 + | label 1 : label line 1 + label 0 : label line 2 + + Place the final label on label line 1, and work backwards, adding + label lines as needed. + + If multiple labels are at the same place, put them on separate + label lines: + + foo + bar + ^ : label line 0 + | : label line 1 + label 1 : label line 2 + label 0 : label line 3. */ + + int max_label_line = 1; + { + int next_column = INT_MAX; + line_label *label; + FOR_EACH_VEC_ELT_REVERSE (labels, i, label) + { + /* Would this label "touch" or overlap the next label? */ + if (label->m_column + label->m_length >= (size_t)next_column) + max_label_line++; + + label->m_label_line = max_label_line; + next_column = label->m_column; + } + } + + /* Print the "label lines". For each label within the line, print + either a vertical bar ('|') for the labels that are lower down, or the + labels themselves once we've reached their line. */ + { + /* Keep track of in which column we last printed a vertical bar. + This allows us to suppress duplicate vertical bars for the case + where multiple labels are on one column. */ + int last_vbar = 0; + for (int label_line = 0; label_line <= max_label_line; label_line++) + { + start_annotation_line (); + pp_space (m_pp); + int column = 1 + m_x_offset; + line_label *label; + FOR_EACH_VEC_ELT (labels, i, label) + { + if (label_line > label->m_label_line) + /* We've printed all the labels for this label line. */ + break; + + if (label_line == label->m_label_line) + { + gcc_assert (column <= label->m_column); + move_to_column (&column, label->m_column, true); + m_colorizer.set_range (label->m_state_idx); + pp_string (m_pp, label->m_text.m_buffer); + m_colorizer.set_normal_text (); + column += label->m_length; + } + else if (label->m_column != last_vbar) + { + gcc_assert (column <= label->m_column); + move_to_column (&column, label->m_column, true); + m_colorizer.set_range (label->m_state_idx); + pp_character (m_pp, '|'); + m_colorizer.set_normal_text (); + last_vbar = column; + column++; + } + } + print_newline (); + } + } + + /* Clean up. */ + { + line_label *label; + FOR_EACH_VEC_ELT (labels, i, label) + label->m_text.maybe_free (); + } +} + /* If there are any fixit hints inserting new lines before source line ROW, print them. @@ -1259,7 +1628,7 @@ itself, with a leading '+'. */ void -layout::print_leading_fixits (int row) +layout::print_leading_fixits (linenum_type row) { for (unsigned int i = 0; i < m_fixit_hints.length (); i++) { @@ -1278,6 +1647,7 @@ 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. @@ -1297,7 +1667,7 @@ the exact range from START_COLUMN to FINISH_COLUMN. */ bool -layout::annotation_line_showed_range_p (int line, int start_column, +layout::annotation_line_showed_range_p (linenum_type line, int start_column, int finish_column) const { layout_range *range; @@ -1443,26 +1813,6 @@ } } -/* A struct capturing the bounds of a buffer, to allow for run-time - bounds-checking in a checked build. */ - -struct char_span -{ - char_span (const char *ptr, size_t n_elts) : m_ptr (ptr), m_n_elts (n_elts) {} - - char_span subspan (int offset, int n_elts) - { - gcc_assert (offset >= 0); - gcc_assert (offset < (int)m_n_elts); - gcc_assert (n_elts >= 0); - gcc_assert (offset + n_elts <= (int)m_n_elts); - return char_span (m_ptr + offset, n_elts); - } - - const char *m_ptr; - size_t m_n_elts; -}; - /* A correction on a particular line. This describes a plan for how to print one or more fixit_hint instances that affected the line, potentially consolidating hints @@ -1494,9 +1844,9 @@ void overwrite (int dst_offset, const char_span &src_span) { gcc_assert (dst_offset >= 0); - gcc_assert (dst_offset + src_span.m_n_elts < m_alloc_sz); - memcpy (m_text + dst_offset, src_span.m_ptr, - src_span.m_n_elts); + gcc_assert (dst_offset + src_span.length () < m_alloc_sz); + memcpy (m_text + dst_offset, src_span.get_buffer (), + src_span.length ()); } /* If insert, then start: the column before which the text @@ -1548,7 +1898,7 @@ struct line_corrections { - line_corrections (const char *filename, int row) + line_corrections (const char *filename, linenum_type row) : m_filename (filename), m_row (row) {} ~line_corrections (); @@ -1556,7 +1906,7 @@ void add_hint (const fixit_hint *hint); const char *m_filename; - int m_row; + linenum_type m_row; auto_vec <correction *> m_corrections; }; @@ -1587,7 +1937,9 @@ source_line::source_line (const char *filename, int line) { - chars = location_get_source_line (filename, line, &width); + char_span span = location_get_source_line (filename, line); + chars = span.get_buffer (); + width = span.length (); } /* Add HINT to the corrections for this line. @@ -1670,7 +2022,7 @@ in layout::print_leading_fixits. */ void -layout::print_trailing_fixits (int row) +layout::print_trailing_fixits (linenum_type row) { /* Build a list of correction instances for the line, potentially consolidating hints (for the sake of readability). */ @@ -1690,7 +2042,10 @@ /* Now print the corrections. */ unsigned i; correction *c; - int column = 0; + int column = m_x_offset; + + if (!corrections.m_corrections.is_empty ()) + start_annotation_line (); FOR_EACH_VEC_ELT (corrections.m_corrections, i, c) { @@ -1699,7 +2054,7 @@ { /* 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 (); @@ -1717,7 +2072,7 @@ 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, '-'); @@ -1728,7 +2083,7 @@ 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 (); @@ -1738,7 +2093,7 @@ } /* Add a trailing newline, if necessary. */ - move_to_column (&column, 0); + move_to_column (&column, 0, false); } /* Disable any colorization and emit a newline. */ @@ -1757,7 +2112,7 @@ bool layout::get_state_at_point (/* Inputs. */ - int row, int column, + linenum_type row, int column, int first_non_ws, int last_non_ws, /* Outputs. */ point_state *out_state) @@ -1766,13 +2121,18 @@ int i; FOR_EACH_VEC_ELT (m_layout_ranges, i, range) { + if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE) + /* Bail out early, so that such ranges don't affect underlining or + source colorization. */ + continue; + if (range->contains_point (row, column)) { out_state->range_idx = i; /* Are we at the range's caret? is it visible? */ out_state->draw_caret_p = false; - if (range->m_show_caret_p + if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET && row == range->m_caret.m_line && column == range->m_caret.m_column) out_state->draw_caret_p = true; @@ -1802,7 +2162,7 @@ character of source (as determined when printing the source line). */ int -layout::get_x_bound_for_row (int row, int caret_column, +layout::get_x_bound_for_row (linenum_type row, int caret_column, int last_non_ws_column) { int result = caret_column + 1; @@ -1835,16 +2195,19 @@ /* 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 (); - *column = 0; + if (add_left_margin) + start_annotation_line (); + *column = m_x_offset; } while (*column < dest_column) @@ -1863,9 +2226,10 @@ /* Hundreds. */ if (max_column > 99) { + start_annotation_line (); pp_space (m_pp); for (int column = 1 + m_x_offset; column <= max_column; column++) - if (0 == column % 10) + if (column % 10 == 0) pp_character (m_pp, '0' + (column / 100) % 10); else pp_space (m_pp); @@ -1873,15 +2237,17 @@ } /* Tens. */ + start_annotation_line (); pp_space (m_pp); for (int column = 1 + m_x_offset; column <= max_column; column++) - if (0 == column % 10) + if (column % 10 == 0) pp_character (m_pp, '0' + (column / 10) % 10); else pp_space (m_pp); 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)); @@ -1893,19 +2259,19 @@ consisting of any caret/underlines, then any fixits. If the source line can't be read, print nothing. */ void -layout::print_line (int row) +layout::print_line (linenum_type row) { - int line_width; - const char *line = location_get_source_line (m_exploc.file, row, - &line_width); + char_span line = location_get_source_line (m_exploc.file, row); if (!line) return; line_bounds lbounds; print_leading_fixits (row); - print_source_line (row, line, line_width, &lbounds); + print_source_line (row, line.get_buffer (), line.length (), &lbounds); if (should_print_annotation_line_p (row)) print_annotation_line (row, lbounds); + if (m_show_labels_p) + print_any_labels (row); print_trailing_fixits (row); } @@ -1925,11 +2291,11 @@ layout layout (global_dc, this, DK_ERROR); location_range loc_range; loc_range.m_loc = loc; - loc_range.m_show_caret_p = false; - if (!layout.maybe_add_location_range (&loc_range, true)) + loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET; + if (!layout.maybe_add_location_range (&loc_range, 0, true)) return false; - add_range (loc, false); + add_range (loc); return true; } @@ -1960,7 +2326,7 @@ context->last_location = loc; - const char *saved_prefix = pp_get_prefix (context->printer); + char *saved_prefix = pp_take_prefix (context->printer); pp_set_prefix (context->printer, NULL); layout layout (context, richloc, diagnostic_kind); @@ -1968,13 +2334,26 @@ line_span_idx++) { const line_span *line_span = layout.get_line_span (line_span_idx); - if (layout.print_heading_for_line_span_index_p (line_span_idx)) + if (context->show_line_numbers_p) + { + /* With line numbers, we should show whenever the line-numbering + "jumps". */ + if (line_span_idx > 0) + layout.print_gap_in_line_numbering (); + } + else { - expanded_location exploc = layout.get_expanded_location (line_span); - context->start_span (context, exploc); + /* Without line numbers, we print headings for some line spans. */ + if (layout.print_heading_for_line_span_index_p (line_span_idx)) + { + expanded_location exploc + = layout.get_expanded_location (line_span); + context->start_span (context, exploc); + } } - int last_line = line_span->get_last_line (); - for (int row = line_span->get_first_line (); row <= last_line; row++) + linenum_type last_line = line_span->get_last_line (); + for (linenum_type row = line_span->get_first_line (); + row <= last_line; row++) layout.print_line (row); } @@ -1987,34 +2366,6 @@ /* Selftests for diagnostic_show_locus. */ -/* Convenience subclass of diagnostic_context for testing - diagnostic_show_locus. */ - -class test_diagnostic_context : public diagnostic_context -{ - public: - test_diagnostic_context () - { - 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. */ static void @@ -2094,8 +2445,8 @@ dc.caret_chars[2] = 'C'; rich_location richloc (line_table, foo); - richloc.add_range (bar, true); - richloc.add_range (field, true); + richloc.add_range (bar, SHOW_RANGE_WITH_CARET); + richloc.add_range (field, SHOW_RANGE_WITH_CARET); diagnostic_show_locus (&dc, &richloc, DK_ERROR); ASSERT_STREQ ("\n" " foo = bar.field;\n" @@ -2216,7 +2567,7 @@ location_t finish = linemap_position_for_column (line_table, 15); rich_location richloc (line_table, equals); location_t field = make_location (start, start, finish); - richloc.add_range (field, false); + richloc.add_range (field); richloc.add_fixit_replace (field, "m_field"); diagnostic_show_locus (&dc, &richloc, DK_ERROR); /* The replacement range is indicated in the annotation line, @@ -2339,6 +2690,157 @@ pp_formatted_text (dc.printer)); } +/* Test of labeling the ranges within a rich_location. */ + +static void +test_one_liner_labels () +{ + location_t foo + = make_location (linemap_position_for_column (line_table, 1), + linemap_position_for_column (line_table, 1), + linemap_position_for_column (line_table, 3)); + location_t bar + = make_location (linemap_position_for_column (line_table, 7), + linemap_position_for_column (line_table, 7), + linemap_position_for_column (line_table, 9)); + location_t field + = make_location (linemap_position_for_column (line_table, 11), + linemap_position_for_column (line_table, 11), + linemap_position_for_column (line_table, 15)); + + /* Example where all the labels fit on one line. */ + { + text_range_label label0 ("0"); + text_range_label label1 ("1"); + text_range_label label2 ("2"); + gcc_rich_location richloc (foo, &label0); + richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); + richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); + + { + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " foo = bar.field;\n" + " ^~~ ~~~ ~~~~~\n" + " | | |\n" + " 0 1 2\n", + pp_formatted_text (dc.printer)); + } + + /* Verify that we can disable label-printing. */ + { + test_diagnostic_context dc; + dc.show_labels_p = false; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " foo = bar.field;\n" + " ^~~ ~~~ ~~~~~\n", + pp_formatted_text (dc.printer)); + } + } + + /* Example where the labels need extra lines. */ + { + text_range_label label0 ("label 0"); + text_range_label label1 ("label 1"); + text_range_label label2 ("label 2"); + gcc_rich_location richloc (foo, &label0); + richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); + richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); + + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " foo = bar.field;\n" + " ^~~ ~~~ ~~~~~\n" + " | | |\n" + " | | label 2\n" + " | label 1\n" + " label 0\n", + pp_formatted_text (dc.printer)); + } + + /* Example of boundary conditions: label 0 and 1 have just enough clearance, + but label 1 just touches label 2. */ + { + text_range_label label0 ("aaaaa"); + text_range_label label1 ("bbbb"); + text_range_label label2 ("c"); + gcc_rich_location richloc (foo, &label0); + richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); + richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); + + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " foo = bar.field;\n" + " ^~~ ~~~ ~~~~~\n" + " | | |\n" + " | | c\n" + " aaaaa bbbb\n", + pp_formatted_text (dc.printer)); + } + + /* Example of out-of-order ranges (thus requiring a sort). */ + { + text_range_label label0 ("0"); + text_range_label label1 ("1"); + text_range_label label2 ("2"); + gcc_rich_location richloc (field, &label0); + richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); + richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2); + + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " foo = bar.field;\n" + " ~~~ ~~~ ^~~~~\n" + " | | |\n" + " 2 1 0\n", + pp_formatted_text (dc.printer)); + } + + /* Ensure we don't ICE if multiple ranges with labels are on + the same point. */ + { + text_range_label label0 ("label 0"); + text_range_label label1 ("label 1"); + text_range_label label2 ("label 2"); + gcc_rich_location richloc (bar, &label0); + richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); + richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2); + + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " foo = bar.field;\n" + " ^~~\n" + " |\n" + " label 2\n" + " label 1\n" + " label 0\n", + pp_formatted_text (dc.printer)); + } + + /* Verify that a NULL result from range_label::get_text is + handled gracefully. */ + { + text_range_label label (NULL); + gcc_rich_location richloc (bar, &label); + + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " foo = bar.field;\n" + " ^~~\n", + pp_formatted_text (dc.printer)); + } + + /* TODO: example of formatted printing (needs to be in + gcc-rich-location.c due to Makefile.in issues). */ +} + /* Run the various one-liner tests. */ static void @@ -2375,6 +2877,7 @@ test_one_liner_fixit_validation_adhoc_locations (); test_one_liner_many_fixits_1 (); test_one_liner_many_fixits_2 (); + test_one_liner_labels (); } /* Verify that gcc_rich_location::add_location_if_nearby works. */ @@ -2517,6 +3020,29 @@ " =\n", pp_formatted_text (dc.printer)); } + + /* As above, but verify the behavior of multiple line spans + with line-numbering enabled. */ + { + 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_before (y, "."); + richloc.add_fixit_replace (colon, "="); + test_diagnostic_context dc; + dc.show_line_numbers_p = true; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " 3 | y\n" + " | .\n" + "......\n" + " 6 | : 0.0};\n" + " | ^\n" + " | =\n", + pp_formatted_text (dc.printer)); + } } @@ -2986,13 +3512,31 @@ { 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)); + + /* Without line numbers. */ + { + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " x = a;\n" + "+ break;\n" + " case 'b':\n" + " ^~~~~~~~~\n", + pp_formatted_text (dc.printer)); + } + + /* With line numbers. */ + { + test_diagnostic_context dc; + dc.show_line_numbers_p = true; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " 2 | x = a;\n" + " +++ |+ break;\n" + " 3 | case 'b':\n" + " | ^~~~~~~~~\n", + pp_formatted_text (dc.printer)); + } } /* Verify that attempts to add text with a newline fail when the @@ -3049,16 +3593,33 @@ if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS) return; - test_diagnostic_context dc; - diagnostic_show_locus (&dc, &richloc, DK_ERROR); - ASSERT_STREQ ("\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)); + { + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\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)); + } + + /* With line-numbering, the line spans are close enough to be + consolidated, since it makes little sense to skip line 2. */ + { + test_diagnostic_context dc; + dc.show_line_numbers_p = true; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ ("\n" + " +++ |+#include <stdio.h>\n" + " 1 | test (int ch)\n" + " 2 | {\n" + " 3 | putchar (ch);\n" + " | ^~~~~~~\n", + pp_formatted_text (dc.printer)); + } } /* Replacement fix-it hint containing a newline. @@ -3148,11 +3709,60 @@ 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; + dc.min_margin_width = 0; + 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 (); test_layout_range_for_multiple_lines (); @@ -3171,6 +3781,8 @@ 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