summaryrefslogtreecommitdiff
path: root/gcc/diagnostic-show-locus.c
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2015-11-06 19:50:50 +0000
committerDavid Malcolm <dmalcolm@gcc.gnu.org>2015-11-06 19:50:50 +0000
commit8a64515099e64564542cbd09be7c9a21c2f580f3 (patch)
treede618a78e7240cd1dec5c9b663ed1464c2775915 /gcc/diagnostic-show-locus.c
parent277ec793cbb5053626a75bcfdb82af03fc735b3e (diff)
downloadgcc-8a64515099e64564542cbd09be7c9a21c2f580f3.tar.gz
Reimplement diagnostic_show_locus, introducing rich_location classes
gcc/ChangeLog: * diagnostic-color.c (color_dict): Eliminate "caret"; add "range1" and "range2". (parse_gcc_colors): Update comment to describe default GCC_COLORS. * diagnostic-core.h (warning_at_rich_loc): New declaration. (error_at_rich_loc): New declaration. (permerror_at_rich_loc): New declaration. (inform_at_rich_loc): New declaration. * diagnostic-show-locus.c (adjust_line): Delete. (struct point_state): New struct. (class colorizer): New class. (class layout_point): New class. (class layout_range): New class. (struct line_bounds): New. (class layout): New class. (colorizer::colorizer): New ctor. (colorizer::~colorizer): New dtor. (layout::layout): New ctor. (layout::print_source_line): New method. (layout::print_annotation_line): New method. (layout::get_state_at_point): New method. (layout::get_x_bound_for_row): New method. (diagnostic_show_locus): Reimplement in terms of class layout. (diagnostic_print_caret_line): Delete. * diagnostic.c (diagnostic_initialize): Replace MAX_LOCATIONS_PER_MESSAGE with rich_location::MAX_RANGES. (diagnostic_set_info_translated): Convert param from location_t to rich_location *. Eliminate calls to set_location on the message in favor of storing the rich_location ptr there. (diagnostic_set_info): Convert param from location_t to rich_location *. (diagnostic_build_prefix): Break out array into... (diagnostic_kind_color): New variable. (diagnostic_get_color_for_kind): New function. (diagnostic_report_diagnostic): Colorize the option_text using the color for the severity. (diagnostic_append_note): Update for change in signature of diagnostic_set_info. (diagnostic_append_note_at_rich_loc): New function. (emit_diagnostic): Update for change in signature of diagnostic_set_info. (inform): Likewise. (inform_at_rich_loc): New function. (inform_n): Update for change in signature of diagnostic_set_info. (warning): Likewise. (warning_at): Likewise. (warning_at_rich_loc): New function. (warning_n): Update for change in signature of diagnostic_set_info. (pedwarn): Likewise. (permerror): Likewise. (permerror_at_rich_loc): New function. (error): Update for change in signature of diagnostic_set_info. (error_n): Likewise. (error_at): Likewise. (error_at_rich_loc): New function. (sorry): Update for change in signature of diagnostic_set_info. (fatal_error): Likewise. (internal_error): Likewise. (internal_error_no_backtrace): Likewise. (source_range::debug): New function. * diagnostic.h (struct diagnostic_info): Eliminate field "override_column". Add field "richloc". (struct diagnostic_context): Add field "colorize_source_p". (diagnostic_override_column): Delete. (diagnostic_set_info): Convert param from location_t to rich_location *. (diagnostic_set_info_translated): Likewise. (diagnostic_append_note_at_rich_loc): New function. (diagnostic_num_locations): New function. (diagnostic_expand_location): Get the location from the rich_location. (diagnostic_print_caret_line): Delete. (diagnostic_get_color_for_kind): New declaration. * genmatch.c (linemap_client_expand_location_to_spelling_point): New. (error_cb): Update for change in signature of "error" callback. (fatal_at): Likewise. (warning_at): Likewise. * input.c (linemap_client_expand_location_to_spelling_point): New. * pretty-print.c (text_info::set_range): New method. (text_info::get_location): New method. * pretty-print.h (MAX_LOCATIONS_PER_MESSAGE): Eliminate this macro. (struct text_info): Eliminate "locations" array in favor of "m_richloc", a rich_location *. (textinfo::set_location): Add a "caret_p" param, and reimplement in terms of a call to set_range. (textinfo::get_location): Eliminate inline implementation in favor of an out-of-line reimplementation. (textinfo::set_range): New method. * rtl-error.c (diagnostic_for_asm): Update for change in signature of diagnostic_set_info. * tree-diagnostic.c (default_tree_printer): Update for new "caret_p" param for textinfo::set_location. * tree-pretty-print.c (percent_K_format): Likewise. gcc/c-family/ChangeLog: * c-common.c (c_cpp_error): Convert parameter from location_t to rich_location *. Eliminate the "column_override" parameter and the call to diagnostic_override_column. Update the "done_lexing" clause to set range 0 on the rich_location, rather than overwriting a location_t. * c-common.h (c_cpp_error): Convert parameter from location_t to rich_location *. Eliminate the "column_override" parameter. gcc/c/ChangeLog: * c-decl.c (warn_defaults_to): Update for change in signature of diagnostic_set_info. * c-errors.c (pedwarn_c99): Likewise. (pedwarn_c90): Likewise. * c-objc-common.c (c_tree_printer): Update for new "caret_p" param for textinfo::set_location. gcc/cp/ChangeLog: * error.c (cp_printer): Update for new "caret_p" param for textinfo::set_location. (pedwarn_cxx98): Update for change in signature of diagnostic_set_info. gcc/fortran/ChangeLog: * cpp.c (cb_cpp_error): Convert parameter from location_t to rich_location *. Eliminate the "column_override" parameter. * error.c (gfc_warning): Update for change in signature of diagnostic_set_info. (gfc_format_decoder): Update handling of %C/%L for changes to struct text_info. (gfc_diagnostic_starter): Use richloc when determining whether to print one locus or two. When handling a location that will involve a call to diagnostic_show_locus, only attempt to print the locus for the primary location, and don't call into diagnostic_print_caret_line. (gfc_warning_now_at): Update for change in signature of diagnostic_set_info. (gfc_warning_now): Likewise. (gfc_error_now): Likewise. (gfc_fatal_error): Likewise. (gfc_error): Likewise. (gfc_internal_error): Likewise. gcc/testsuite/ChangeLog: * gcc.dg/plugin/diagnostic-test-show-locus-bw.c: New file. * gcc.dg/plugin/diagnostic-test-show-locus-color.c: New file. * gcc.dg/plugin/diagnostic_plugin_test_show_locus.c: New file. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add the above. * lib/gcc-dg.exp: Load multiline.exp. libcpp/ChangeLog: * errors.c (cpp_diagnostic): Update for change in signature of "error" callback. (cpp_diagnostic_with_line): Likewise, calling override_column on the rich_location. * include/cpplib.h (struct cpp_callbacks): Within "error" callback, convert param from source_location to rich_location *, and drop column_override param. * include/line-map.h (struct source_range): New struct. (struct location_range): New struct. (class rich_location): New class. (linemap_client_expand_location_to_spelling_point): New declaration. * line-map.c (rich_location::rich_location): New ctors. (rich_location::lazily_expand_location): New method. (rich_location::override_column): New method. (rich_location::add_range): New methods. (rich_location::set_range): New method. From-SVN: r229884
Diffstat (limited to 'gcc/diagnostic-show-locus.c')
-rw-r--r--gcc/diagnostic-show-locus.c755
1 files changed, 655 insertions, 100 deletions
diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 147a2b8b71d..22203cdbaa3 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -36,131 +36,686 @@ along with GCC; see the file COPYING3. If not see
# include <sys/ioctl.h>
#endif
-/* If LINE is longer than MAX_WIDTH, and COLUMN is not smaller than
- MAX_WIDTH by some margin, then adjust the start of the line such
- that the COLUMN is smaller than MAX_WIDTH minus the margin. The
- margin is either CARET_LINE_MARGIN characters or the difference
- between the column and the length of the line, whatever is smaller.
- The length of LINE is given by LINE_WIDTH. */
-static const char *
-adjust_line (const char *line, int line_width,
- int max_width, int *column_p)
-{
- int right_margin = CARET_LINE_MARGIN;
- int column = *column_p;
-
- gcc_checking_assert (line_width >= column);
- right_margin = MIN (line_width - column, right_margin);
- right_margin = max_width - right_margin;
- if (line_width >= max_width && column > right_margin)
+/* Classes for rendering source code and diagnostics, within an
+ anonymous namespace.
+ The work is done by "class layout", which embeds and uses
+ "class colorizer" and "class layout_range" to get things done. */
+
+namespace {
+
+/* The state at a given point of the source code, assuming that we're
+ in a range: which range are we in, and whether we should draw a caret at
+ this point. */
+
+struct point_state
+{
+ int range_idx;
+ bool draw_caret_p;
+};
+
+/* A class to inject colorization codes when printing the diagnostic locus.
+
+ It has one kind of colorization for each of:
+ - normal text
+ - range 0 (the "primary location")
+ - range 1
+ - range 2
+
+ The class caches the lookup of the color codes for the above.
+
+ The class also has responsibility for tracking which of the above is
+ active, filtering out unnecessary changes. This allows
+ layout::print_source_line and layout::print_annotation_line
+ to simply request a colorization code for *every* character they print,
+ via this class, and have the filtering be done for them here. */
+
+class colorizer
+{
+ public:
+ colorizer (diagnostic_context *context,
+ const diagnostic_info *diagnostic);
+ ~colorizer ();
+
+ void set_range (int range_idx) { set_state (range_idx); }
+ void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
+
+ private:
+ void set_state (int state);
+ void begin_state (int state);
+ void finish_state (int state);
+
+ private:
+ static const int STATE_NORMAL_TEXT = -1;
+
+ diagnostic_context *m_context;
+ const diagnostic_info *m_diagnostic;
+ int m_current_state;
+ const char *m_caret_cs;
+ const char *m_caret_ce;
+ const char *m_range1_cs;
+ const char *m_range2_cs;
+ const char *m_range_ce;
+};
+
+/* A point within a layout_range; similar to an expanded_location,
+ but after filtering on file. */
+
+class layout_point
+{
+ public:
+ layout_point (const expanded_location &exploc)
+ : m_line (exploc.line),
+ m_column (exploc.column) {}
+
+ int m_line;
+ int m_column;
+};
+
+/* A class for use by "class layout" below: a filtered location_range. */
+
+class layout_range
+{
+ public:
+ layout_range (const location_range *loc_range);
+
+ bool contains_point (int row, int column) const;
+
+ layout_point m_start;
+ layout_point m_finish;
+ bool m_show_caret_p;
+ layout_point m_caret;
+};
+
+/* A struct for use by layout::print_source_line for telling
+ layout::print_annotation_line the extents of the source line that
+ it printed, so that underlines can be clipped appropriately. */
+
+struct line_bounds
+{
+ int m_first_non_ws;
+ int m_last_non_ws;
+};
+
+/* A class to control the overall layout when printing a diagnostic.
+
+ The layout is determined within the constructor.
+ It is then printed by repeatedly calling the "print_source_line"
+ and "print_annotation_line" methods.
+
+ We assume we have disjoint ranges. */
+
+class layout
+{
+ public:
+ layout (diagnostic_context *context,
+ const diagnostic_info *diagnostic);
+
+ int get_first_line () const { return m_first_line; }
+ int get_last_line () const { return m_last_line; }
+
+ bool print_source_line (int row, line_bounds *lbounds_out);
+ void print_annotation_line (int row, const line_bounds lbounds);
+
+ private:
+ bool
+ get_state_at_point (/* Inputs. */
+ int 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,
+ int last_non_ws);
+
+ private:
+ diagnostic_context *m_context;
+ pretty_printer *m_pp;
+ diagnostic_t m_diagnostic_kind;
+ expanded_location m_exploc;
+ colorizer m_colorizer;
+ bool m_colorize_source_p;
+ auto_vec <layout_range> m_layout_ranges;
+ int m_first_line;
+ int m_last_line;
+ int m_x_offset;
+};
+
+/* Implementation of "class colorizer". */
+
+/* The constructor for "colorizer". Lookup and store color codes for the
+ different kinds of things we might need to print. */
+
+colorizer::colorizer (diagnostic_context *context,
+ const diagnostic_info *diagnostic) :
+ m_context (context),
+ m_diagnostic (diagnostic),
+ m_current_state (STATE_NORMAL_TEXT)
+{
+ m_caret_ce = colorize_stop (pp_show_color (context->printer));
+ m_range1_cs = colorize_start (pp_show_color (context->printer), "range1");
+ m_range2_cs = colorize_start (pp_show_color (context->printer), "range2");
+ m_range_ce = colorize_stop (pp_show_color (context->printer));
+}
+
+/* The destructor for "colorize". If colorization is on, print a code to
+ turn it off. */
+
+colorizer::~colorizer ()
+{
+ finish_state (m_current_state);
+}
+
+/* Update state, printing color codes if necessary if there's a state
+ change. */
+
+void
+colorizer::set_state (int new_state)
+{
+ if (m_current_state != new_state)
{
- line += column - right_margin;
- *column_p = right_margin;
+ finish_state (m_current_state);
+ m_current_state = new_state;
+ begin_state (new_state);
}
- return line;
}
-/* Print the physical source line corresponding to the location of
- this diagnostic, and a caret indicating the precise column. This
- function only prints two caret characters if the two locations
- given by DIAGNOSTIC are on the same line according to
- diagnostic_same_line(). */
+/* Turn on any colorization for STATE. */
+
void
-diagnostic_show_locus (diagnostic_context * context,
- const diagnostic_info *diagnostic)
+colorizer::begin_state (int state)
{
- if (!context->show_caret
- || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
- || diagnostic_location (diagnostic, 0) == context->last_location)
- return;
+ switch (state)
+ {
+ case STATE_NORMAL_TEXT:
+ break;
- context->last_location = diagnostic_location (diagnostic, 0);
- expanded_location s0 = diagnostic_expand_location (diagnostic, 0);
- expanded_location s1 = { };
- /* Zero-initialized. This is checked later by diagnostic_print_caret_line. */
+ case 0:
+ /* Make range 0 be the same color as the "kind" text
+ (error vs warning vs note). */
+ pp_string
+ (m_context->printer,
+ colorize_start (pp_show_color (m_context->printer),
+ diagnostic_get_color_for_kind (m_diagnostic->kind)));
+ break;
- if (diagnostic_location (diagnostic, 1) > BUILTINS_LOCATION)
- s1 = diagnostic_expand_location (diagnostic, 1);
+ case 1:
+ pp_string (m_context->printer, m_range1_cs);
+ break;
- diagnostic_print_caret_line (context, s0, s1,
- context->caret_chars[0],
- context->caret_chars[1]);
+ case 2:
+ pp_string (m_context->printer, m_range2_cs);
+ break;
+
+ default:
+ /* We don't expect more than 3 ranges per diagnostic. */
+ gcc_unreachable ();
+ break;
+ }
}
-/* Print (part) of the source line given by xloc1 with caret1 pointing
- at the column. If xloc2.column != 0 and it fits within the same
- line as xloc1 according to diagnostic_same_line (), then caret2 is
- printed at xloc2.colum. Otherwise, the caller has to set up things
- to print a second caret line for xloc2. */
+/* Turn off any colorization for STATE. */
+
void
-diagnostic_print_caret_line (diagnostic_context * context,
- expanded_location xloc1,
- expanded_location xloc2,
- char caret1, char caret2)
-{
- if (!diagnostic_same_line (context, xloc1, xloc2))
- /* This will mean ignore xloc2. */
- xloc2.column = 0;
- else if (xloc1.column == xloc2.column)
- xloc2.column++;
-
- int cmax = MAX (xloc1.column, xloc2.column);
+colorizer::finish_state (int state)
+{
+ switch (state)
+ {
+ case STATE_NORMAL_TEXT:
+ break;
+
+ case 0:
+ pp_string (m_context->printer, m_caret_ce);
+ break;
+
+ default:
+ /* Within a range. */
+ gcc_assert (state > 0);
+ pp_string (m_context->printer, m_range_ce);
+ break;
+ }
+}
+
+/* Implementation of class layout_range. */
+
+/* The constructor for class layout_range.
+ Initialize various layout_point fields from expanded_location
+ equivalents; we've already filtered on file. */
+
+layout_range::layout_range (const location_range *loc_range)
+: m_start (loc_range->m_start),
+ m_finish (loc_range->m_finish),
+ m_show_caret_p (loc_range->m_show_caret_p),
+ m_caret (loc_range->m_caret)
+{
+}
+
+/* Is (column, row) within the given range?
+ We've already filtered on the file.
+
+ Ranges are closed (both limits are within the range).
+
+ Example A: a single-line range:
+ start: (col=22, line=2)
+ finish: (col=38, line=2)
+
+ |00000011111111112222222222333333333344444444444
+ |34567890123456789012345678901234567890123456789
+--+-----------------------------------------------
+01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
+03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+ Example B: a multiline range with
+ start: (col=14, line=3)
+ finish: (col=08, line=5)
+
+ |00000011111111112222222222333333333344444444444
+ |34567890123456789012345678901234567890123456789
+--+-----------------------------------------------
+01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
+04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
+05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+--+-----------------------------------------------
+
+ Legend:
+ - 'b' indicates a point *before* the range
+ - 'S' indicates the start of the range
+ - 'w' indicates a point within the range
+ - 'F' indicates the finish of the range (which is
+ within it).
+ - 'a' indicates a subsequent point *after* the range. */
+
+bool
+layout_range::contains_point (int row, int column) const
+{
+ gcc_assert (m_start.m_line <= m_finish.m_line);
+ /* ...but the equivalent isn't true for the columns;
+ consider example B in the comment above. */
+
+ if (row < m_start.m_line)
+ /* Points before the first line of the range are
+ outside it (corresponding to line 01 in example A
+ and lines 01 and 02 in example B above). */
+ return false;
+
+ if (row == m_start.m_line)
+ /* On same line as start of range (corresponding
+ to line 02 in example A and line 03 in example B). */
+ {
+ if (column < m_start.m_column)
+ /* Points on the starting line of the range, but
+ before the column in which it begins. */
+ return false;
+
+ if (row < m_finish.m_line)
+ /* This is a multiline range; the point
+ is within it (corresponds to line 03 in example B
+ from column 14 onwards) */
+ return true;
+ else
+ {
+ /* This is a single-line range. */
+ gcc_assert (row == m_finish.m_line);
+ return column <= m_finish.m_column;
+ }
+ }
+
+ /* The point is in a line beyond that containing the
+ start of the range: lines 03 onwards in example A,
+ and lines 04 onwards in example B. */
+ gcc_assert (row > m_start.m_line);
+
+ if (row > m_finish.m_line)
+ /* The point is beyond the final line of the range
+ (lines 03 onwards in example A, and lines 06 onwards
+ in example B). */
+ return false;
+
+ if (row < m_finish.m_line)
+ {
+ /* The point is in a line that's fully within a multiline
+ range (e.g. line 04 in example B). */
+ gcc_assert (m_start.m_line < m_finish.m_line);
+ return true;
+ }
+
+ gcc_assert (row == m_finish.m_line);
+
+ return column <= m_finish.m_column;
+}
+
+/* Given a source line LINE of length LINE_WIDTH, determine the width
+ without any trailing whitespace. */
+
+static int
+get_line_width_without_trailing_whitespace (const char *line, int line_width)
+{
+ int result = line_width;
+ while (result > 0)
+ {
+ char ch = line[result - 1];
+ if (ch == ' ' || ch == '\t')
+ result--;
+ else
+ break;
+ }
+ gcc_assert (result >= 0);
+ gcc_assert (result <= line_width);
+ gcc_assert (result == 0 ||
+ (line[result - 1] != ' '
+ && line[result -1] != '\t'));
+ return result;
+}
+
+/* Implementation of class layout. */
+
+/* Constructor for class layout.
+
+ Filter the ranges from the rich_location to those that we can
+ sanely print, populating m_layout_ranges.
+ Determine the range of lines that we will print.
+ Determine m_x_offset, to ensure that the primary caret
+ will fit within the max_width provided by the diagnostic_context. */
+
+layout::layout (diagnostic_context * context,
+ const diagnostic_info *diagnostic)
+: m_context (context),
+ m_pp (context->printer),
+ m_diagnostic_kind (diagnostic->kind),
+ m_exploc (diagnostic->richloc->lazily_expand_location ()),
+ m_colorizer (context, diagnostic),
+ m_colorize_source_p (context->colorize_source_p),
+ m_layout_ranges (rich_location::MAX_RANGES),
+ m_first_line (m_exploc.line),
+ m_last_line (m_exploc.line),
+ m_x_offset (0)
+{
+ rich_location *richloc = diagnostic->richloc;
+ for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
+ {
+ /* This diagnostic printer can only cope with "sufficiently sane" ranges.
+ Ignore any ranges that are awkward to handle. */
+ location_range *loc_range = richloc->get_range (idx);
+
+ /* If any part of the range isn't in the same file as the primary
+ location of this diagnostic, ignore the range. */
+ if (loc_range->m_start.file != m_exploc.file)
+ continue;
+ if (loc_range->m_finish.file != m_exploc.file)
+ continue;
+ if (loc_range->m_show_caret_p)
+ if (loc_range->m_caret.file != m_exploc.file)
+ continue;
+
+ /* Passed all the tests; add the range to m_layout_ranges so that
+ it will be printed. */
+ layout_range ri (loc_range);
+ m_layout_ranges.safe_push (ri);
+
+ /* Update m_first_line/m_last_line if necessary. */
+ if (loc_range->m_start.line < m_first_line)
+ m_first_line = loc_range->m_start.line;
+ if (loc_range->m_finish.line > m_last_line)
+ m_last_line = loc_range->m_finish.line;
+ }
+
+ /* 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 (xloc1.file, xloc1.line,
+ const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
&line_width);
- if (line == NULL || cmax > line_width)
- return;
+ if (line && m_exploc.column <= line_width)
+ {
+ int right_margin = CARET_LINE_MARGIN;
+ int column = m_exploc.column;
+ right_margin = MIN (line_width - column, right_margin);
+ right_margin = max_width - right_margin;
+ if (line_width >= max_width && column > right_margin)
+ m_x_offset = column - right_margin;
+ gcc_assert (m_x_offset >= 0);
+ }
+}
- /* Center the interesting part of the source line to fit in
- max_width, and adjust all columns accordingly. */
- int max_width = context->caret_max_width;
- int offset = (int) cmax;
- line = adjust_line (line, line_width, max_width, &offset);
- offset -= cmax;
- cmax += offset;
- xloc1.column += offset;
- if (xloc2.column)
- xloc2.column += offset;
-
- /* Print the source line. */
- pp_newline (context->printer);
- const char *saved_prefix = pp_get_prefix (context->printer);
- pp_set_prefix (context->printer, NULL);
- pp_space (context->printer);
- while (max_width > 0 && line_width > 0)
+/* 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. */
+
+bool
+layout::print_source_line (int row, line_bounds *lbounds_out)
+{
+ int line_width;
+ const char *line = location_get_source_line (m_exploc.file, row,
+ &line_width);
+ if (!line)
+ return false;
+
+ line += m_x_offset;
+
+ m_colorizer.set_normal_text ();
+
+ /* We will stop printing the source line at any trailing
+ whitespace. */
+ line_width = get_line_width_without_trailing_whitespace (line,
+ line_width);
+
+ pp_space (m_pp);
+ int first_non_ws = INT_MAX;
+ int last_non_ws = 0;
+ int column;
+ for (column = 1 + m_x_offset; column <= line_width; column++)
{
+ /* Assuming colorization is enabled for the caret and underline
+ characters, we may also colorize the associated characters
+ within the source line.
+
+ For frontends that generate range information, we color the
+ associated characters in the source line the same as the
+ carets and underlines in the annotation line, to make it easier
+ for the reader to see the pertinent code.
+
+ For frontends that only generate carets, we don't colorize the
+ characters above them, since this would look strange (e.g.
+ colorizing just the first character in a token). */
+ if (m_colorize_source_p)
+ {
+ bool in_range_p;
+ point_state state;
+ in_range_p = get_state_at_point (row, column,
+ 0, INT_MAX,
+ &state);
+ if (in_range_p)
+ m_colorizer.set_range (state.range_idx);
+ else
+ m_colorizer.set_normal_text ();
+ }
char c = *line == '\t' ? ' ' : *line;
if (c == '\0')
c = ' ';
- pp_character (context->printer, c);
- max_width--;
- line_width--;
+ if (c != ' ')
+ {
+ last_non_ws = column;
+ if (first_non_ws == INT_MAX)
+ first_non_ws = column;
+ }
+ pp_character (m_pp, c);
line++;
}
- pp_newline (context->printer);
+ pp_newline (m_pp);
+
+ lbounds_out->m_first_non_ws = first_non_ws;
+ lbounds_out->m_last_non_ws = last_non_ws;
+ return true;
+}
+
+/* Print a line consisting of the caret/underlines for the given
+ source line. */
- /* Print the caret under the line. */
- const char *caret_cs, *caret_ce;
- caret_cs = colorize_start (pp_show_color (context->printer), "caret");
- caret_ce = colorize_stop (pp_show_color (context->printer));
- int cmin = xloc2.column
- ? MIN (xloc1.column, xloc2.column) : xloc1.column;
- int caret_min = cmin == xloc1.column ? caret1 : caret2;
- int caret_max = cmin == xloc1.column ? caret2 : caret1;
-
- /* cmin is >= 1, but we indent with an extra space at the start like
- we did above. */
+void
+layout::print_annotation_line (int row, const line_bounds lbounds)
+{
+ int x_bound = get_x_bound_for_row (row, m_exploc.column,
+ lbounds.m_last_non_ws);
+
+ pp_space (m_pp);
+ for (int column = 1 + m_x_offset; column < x_bound; column++)
+ {
+ bool in_range_p;
+ point_state state;
+ in_range_p = get_state_at_point (row, column,
+ lbounds.m_first_non_ws,
+ lbounds.m_last_non_ws,
+ &state);
+ if (in_range_p)
+ {
+ /* Within a range. Draw either the caret or an underline. */
+ m_colorizer.set_range (state.range_idx);
+ if (state.draw_caret_p)
+ /* Draw the caret. */
+ pp_character (m_pp, m_context->caret_chars[state.range_idx]);
+ else
+ pp_character (m_pp, '~');
+ }
+ else
+ {
+ /* Not in a range. */
+ m_colorizer.set_normal_text ();
+ pp_character (m_pp, ' ');
+ }
+ }
+ pp_newline (m_pp);
+}
+
+/* Return true if (ROW/COLUMN) is within a range of the layout.
+ If it returns true, OUT_STATE is written to, with the
+ range index, and whether we should draw the caret at
+ (ROW/COLUMN) (as opposed to an underline). */
+
+bool
+layout::get_state_at_point (/* Inputs. */
+ int row, int column,
+ int first_non_ws, int last_non_ws,
+ /* Outputs. */
+ point_state *out_state)
+{
+ layout_range *range;
int i;
- for (i = 0; i < cmin; i++)
- pp_space (context->printer);
- pp_printf (context->printer, "%s%c%s", caret_cs, caret_min, caret_ce);
+ FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
+ {
+ 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 (row == range->m_caret.m_line
+ && column == range->m_caret.m_column)
+ out_state->draw_caret_p = range->m_show_caret_p;
- if (xloc2.column)
+ /* Within a multiline range, don't display any underline
+ in any leading or trailing whitespace on a line.
+ We do display carets, however. */
+ if (!out_state->draw_caret_p)
+ if (column < first_non_ws || column > last_non_ws)
+ return false;
+
+ /* We are within a range. */
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Helper function for use by layout::print_line when printing the
+ annotation line under the source line.
+ Get the column beyond the rightmost one that could contain a caret or
+ range marker, given that we stop rendering at trailing whitespace.
+ ROW is the source line within the given file.
+ CARET_COLUMN is the column of range 0's caret.
+ LAST_NON_WS_COLUMN is the last column containing a non-whitespace
+ character of source (as determined when printing the source line). */
+
+int
+layout::get_x_bound_for_row (int row, int caret_column,
+ int last_non_ws_column)
+{
+ int result = caret_column + 1;
+
+ layout_range *range;
+ int i;
+ FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
{
- for (i++; i < cmax; i++)
- pp_space (context->printer);
- pp_printf (context->printer, "%s%c%s", caret_cs, caret_max, caret_ce);
+ if (row >= range->m_start.m_line)
+ {
+ if (range->m_finish.m_line == row)
+ {
+ /* On the final line within a range; ensure that
+ we render up to the end of the range. */
+ if (result <= range->m_finish.m_column)
+ result = range->m_finish.m_column + 1;
+ }
+ else if (row < range->m_finish.m_line)
+ {
+ /* Within a multiline range; ensure that we render up to the
+ last non-whitespace column. */
+ if (result <= last_non_ws_column)
+ result = last_non_ws_column + 1;
+ }
+ }
}
+
+ return result;
+}
+
+} /* End of anonymous namespace. */
+
+/* Print the physical source code corresponding to the location of
+ this diagnostic, with additional annotations. */
+
+void
+diagnostic_show_locus (diagnostic_context * context,
+ const diagnostic_info *diagnostic)
+{
+ if (!context->show_caret
+ || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
+ || diagnostic_location (diagnostic, 0) == context->last_location)
+ return;
+
+ context->last_location = diagnostic_location (diagnostic, 0);
+
+ pp_newline (context->printer);
+
+ const char *saved_prefix = pp_get_prefix (context->printer);
+ pp_set_prefix (context->printer, NULL);
+
+ {
+ layout layout (context, diagnostic);
+ int last_line = layout.get_last_line ();
+ for (int row = layout.get_first_line ();
+ row <= last_line;
+ row++)
+ {
+ /* Print the source line, followed by an annotation line
+ consisting of any caret/underlines. If the source line can't
+ be read, print nothing. */
+ line_bounds lbounds;
+ if (layout.print_source_line (row, &lbounds))
+ layout.print_annotation_line (row, lbounds);
+ }
+
+ /* The closing scope here leads to the dtor for layout and thus
+ colorizer being called here, which affects the precise
+ place where colorization is turned off in the unittest
+ for colorized output. */
+ }
+
pp_set_prefix (context->printer, saved_prefix);
- pp_needs_newline (context->printer) = true;
}