@@ -1255,6 +1255,7 @@ OBJS = \
fold-const.o \
function.o \
fwprop.o \
+ gcc-rich-location.o \
gcse.o \
gcse-common.o \
ggc-common.o \
@@ -1513,7 +1514,7 @@ OBJS = \
# Objects in libcommon.a, potentially used by all host binaries and with
# no target dependencies.
OBJS-libcommon = diagnostic.o diagnostic-color.o diagnostic-show-locus.o \
- pretty-print.o intl.o \
+ box-drawing.o pretty-print.o intl.o \
vec.o input.o version.o hash-table.o ggc-none.o
# Objects in libcommon-target.a, used by drivers and by the core
new file mode 100644
@@ -0,0 +1,99 @@
+/* Box-drawing, either using Unicode box-drawing chars, or as pure ASCII
+ Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "box-drawing.h"
+
+/* Global singleton instance. */
+box_drawing g_line_art;
+
+/* Strings for drawing the caret and ranges within diagnostics.
+ There are either UTF-8-encoded Unicode box-drawing characters,
+ or pure ASCII. */
+
+/* Initializer for box_drawing.
+ This isn't a constructor since the singleton instance is
+ statically-allocated. */
+void
+box_drawing::init (bool have_utf8)
+{
+ if (have_utf8)
+ {
+ /* For the caret, use:
+ U+25B2 BLACK UP-POINTING TRIANGLE
+ which in UTF-8 is: 0xE2 0x96 0xB2. */
+ default_caret = "\xE2\x96\xB2";
+
+ /* Underlining text ranges. */
+
+ /* The start of an underline: the south-western corner of a box:
+ U+2514 BOX DRAWINGS LIGHT UP AND RIGHT
+ in UTF-8: 0xE2 0x94 0x94. */
+ underline_start = "\xE2\x94\x94";
+
+ /* Within an underline:
+ U+2500 BOX DRAWINGS LIGHT HORIZONTAL
+ which in UTF-8 is: 0xE2 0x94 0x80. */
+ underline_hbar = "\xE2\x94\x80";
+
+ /* The end of an underline: the south-eastern corner of a box:
+ U+2518 BOX DRAWINGS LIGHT UP AND LEFT
+ UTF-8: 0xE2 0x94 0x98. */
+ underline_end = "\xE2\x94\x98";
+
+ /* Vertical margins (when showing captions for ranges). */
+
+ /* The top of a rmargin: the north-eastern corner of a box:
+ U+2510 BOX DRAWINGS LIGHT DOWN AND LEFT
+ UTF-8: 0xE2 0x94 0x90. */
+ rmargin_start = "\xE2\x94\x90";
+
+ /* For vertical lines, use:
+ U+2502 BOX DRAWINGS LIGHT VERTICAL
+ which in UTF-8 is: 0xE2 0x94 0x82. */
+ rmargin_vbar = "\xE2\x94\x82";
+
+ /* For the row within the rmargin containing the caption:
+ U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ UTF-8: 0xE2 0x94 0x9C. */
+ rmargin_caption_row = "\xE2\x94\x9C";
+
+ /* Unless its on the very last line, in which case:
+ U+2534 BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ UTF-8: 0xE2 0x94 0xB4. */
+ rmargin_caption_row_at_end = "\xE2\x94\xB4";
+
+ /* The end of a rmargin: the south-eastern corner of a box:
+ U+2518 BOX DRAWINGS LIGHT UP AND LEFT
+ UTF-8: 0xE2 0x94 0x98. */
+ rmargin_end = "\xE2\x94\x98";
+ }
+ else
+ {
+ default_caret = "^";
+ underline_start = underline_hbar = underline_end = "~";
+
+ rmargin_start = "|";
+ rmargin_vbar = "|";
+ rmargin_caption_row = rmargin_caption_row_at_end = "+";
+ rmargin_end = "|";
+ }
+}
new file mode 100644
@@ -0,0 +1,43 @@
+/* Box-drawing, either using Unicode box-drawing chars, or as pure ASCII
+ Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+/* Strings for drawing the caret and ranges within diagnostics.
+ There are either UTF-8-encoded Unicode box-drawing characters,
+ or pure ASCII. */
+
+class box_drawing
+{
+ public:
+ void init (bool have_utf8);
+
+ const char *default_caret;
+ const char *underline_start;
+ const char *underline_hbar;
+ const char *underline_end;
+
+ /* Right-hand-side margins. */
+ const char *rmargin_start;
+ const char *rmargin_vbar;
+ const char *rmargin_caption_row;
+ const char *rmargin_caption_row_at_end;
+ const char *rmargin_end;
+};
+
+/* Singleton instance. */
+extern box_drawing g_line_art;
@@ -10451,15 +10451,14 @@ c_option_controlling_cpp_error (int reason)
/* Callback from cpp_error for PFILE to print diagnostics from the
preprocessor. The diagnostic is of type LEVEL, with REASON set
to the reason code if LEVEL is represents a warning, at location
- LOCATION unless this is after lexing and the compiler's location
- should be used instead, with column number possibly overridden by
- COLUMN_OVERRIDE if not zero; MSG is the translated message and AP
+ RICHLOC unless this is after lexing and the compiler's location
+ should be used instead; MSG is the translated message and AP
the arguments. Returns true if a diagnostic was emitted, false
otherwise. */
bool
c_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
- location_t location, unsigned int column_override,
+ rich_location *richloc,
const char *msg, va_list *ap)
{
diagnostic_info diagnostic;
@@ -10500,11 +10499,11 @@ c_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
gcc_unreachable ();
}
if (done_lexing)
- location = input_location;
+ richloc->set_range (0,
+ source_range::from_location (input_location),
+ true);
diagnostic_set_info_translated (&diagnostic, msg, ap,
- location, dlevel);
- if (column_override)
- diagnostic_override_column (&diagnostic, column_override);
+ richloc, dlevel);
diagnostic_override_option_index (&diagnostic,
c_option_controlling_cpp_error (reason));
ret = report_diagnostic (&diagnostic);
@@ -981,9 +981,9 @@ extern void init_c_lex (void);
extern void c_cpp_builtins (cpp_reader *);
extern void c_cpp_builtins_optimize_pragma (cpp_reader *, tree, tree);
-extern bool c_cpp_error (cpp_reader *, int, int, location_t, unsigned int,
+extern bool c_cpp_error (cpp_reader *, int, int, rich_location *,
const char *, va_list *)
- ATTRIBUTE_GCC_DIAG(6,0);
+ ATTRIBUTE_GCC_DIAG(5,0);
extern int c_common_has_attribute (cpp_reader *);
extern bool parse_optimize_options (tree, bool);
@@ -5273,9 +5273,10 @@ warn_defaults_to (location_t location, int opt, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
flag_isoc99 ? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = opt;
report_diagnostic (&diagnostic);
@@ -42,13 +42,14 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool warned = false;
+ rich_location richloc (location);
va_start (ap, gmsgid);
/* If desired, issue the C99/C11 compat warning, which is more specific
than -pedantic. */
if (warn_c99_c11_compat > 0)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
(pedantic && !flag_isoc11)
? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = OPT_Wc99_c11_compat;
@@ -60,7 +61,7 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...)
/* For -pedantic outside C11, issue a pedwarn. */
else if (pedantic && !flag_isoc11)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
diagnostic.option_index = opt;
warned = report_diagnostic (&diagnostic);
}
@@ -80,6 +81,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, gmsgid);
/* Warnings such as -Wvla are the most specific ones. */
@@ -90,7 +92,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
goto out;
else if (opt_var > 0)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
(pedantic && !flag_isoc99)
? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = opt;
@@ -102,7 +104,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
specific than -pedantic. */
if (warn_c90_c99_compat > 0)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
(pedantic && !flag_isoc99)
? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = OPT_Wc90_c99_compat;
@@ -114,7 +116,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
/* For -pedantic outside C99, issue a pedwarn. */
else if (pedantic && !flag_isoc99)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
diagnostic.option_index = opt;
report_diagnostic (&diagnostic);
}
@@ -101,7 +101,7 @@ c_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
{
t = va_arg (*text->args_ptr, tree);
if (set_locus)
- text->set_location (0, DECL_SOURCE_LOCATION (t));
+ text->set_location (0, DECL_SOURCE_LOCATION (t), true);
}
switch (*spec)
@@ -3554,7 +3554,7 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
pp_string (pp, result);
if (set_locus && t != NULL)
- text->set_location (0, location_of (t));
+ text->set_location (0, location_of (t), true);
return true;
#undef next_tree
#undef next_tcode
@@ -3668,9 +3668,10 @@ pedwarn_cxx98 (location_t location, int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
(cxx_dialect == cxx98) ? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
@@ -164,7 +164,8 @@ static struct color_cap color_dict[] =
{ "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
7, false },
{ "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
- { "caret", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 5, false },
+ { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
+ { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
{ "locus", SGR_SEQ (COLOR_BOLD), 5, false },
{ "quote", SGR_SEQ (COLOR_BOLD), 5, false },
{ NULL, NULL, 0, false }
@@ -195,7 +196,7 @@ colorize_stop (bool show_color)
}
/* Parse GCC_COLORS. The default would look like:
- GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
+ GCC_COLORS='error=01;31:warning=01;35:note=01;36:range1=32:range2=34;locus=01:quote=01'
No character escaping is needed or supported. */
static bool
parse_gcc_colors (void)
@@ -63,18 +63,26 @@ extern bool warning_n (location_t, int, int, const char *, const char *, ...)
ATTRIBUTE_GCC_DIAG(4,6) ATTRIBUTE_GCC_DIAG(5,6);
extern bool warning_at (location_t, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
+extern bool warning_at_rich_loc (rich_location *, int, const char *, ...)
+ ATTRIBUTE_GCC_DIAG(3,4);
extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
extern void error_n (location_t, int, const char *, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
extern void error_at (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern void error_at_rich_loc (rich_location *, const char *, ...)
+ ATTRIBUTE_GCC_DIAG(2,3);
extern void fatal_error (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3)
ATTRIBUTE_NORETURN;
/* Pass one of the OPT_W* from options.h as the second parameter. */
extern bool pedwarn (location_t, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
extern bool permerror (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern bool permerror_at_rich_loc (rich_location *, const char *,
+ ...) ATTRIBUTE_GCC_DIAG(2,3);
extern void sorry (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
extern void inform (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern void inform_at_rich_loc (rich_location *, const char *,
+ ...) ATTRIBUTE_GCC_DIAG(2,3);
extern void inform_n (location_t, int, const char *, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
extern void verbatim (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see
#include "backtrace.h"
#include "diagnostic.h"
#include "diagnostic-color.h"
+#include "box-drawing.h"
#ifdef HAVE_TERMIOS_H
# include <termios.h>
@@ -36,6 +37,10 @@ along with GCC; see the file COPYING3. If not see
# include <sys/ioctl.h>
#endif
+static void
+diagnostic_print_ranges (diagnostic_context * context,
+ const diagnostic_info *diagnostic);
+
/* 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
@@ -60,11 +65,808 @@ adjust_line (const char *line, int line_width,
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(). */
+/* Is (column, row) within the given range?
+
+ 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
+location_range::contains_point (int row, int column) const
+{
+ gcc_assert (m_start.line <= m_finish.line);
+ /* ...but the equivalent isn't true for the columns;
+ consider example B in the comment above. */
+
+ if (row < m_start.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.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.column)
+ /* Points on the starting line of the range, but
+ before the column in which it begins. */
+ return false;
+
+ if (row < m_finish.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.line);
+ return column <= m_finish.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.line);
+
+ if (row > m_finish.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.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.line < m_finish.line);
+ return true;
+ }
+
+ gcc_assert (row == m_finish.line);
+
+ return column <= m_finish.column;
+}
+
+/* Return true if (ROW/COLUMN) is within a range of RICHLOC.
+ If it returns true, OUT_RANGE_IDX and OUT_DRAW_CARET_P are
+ written to, with the range index, and whether we should draw
+ the caret at (ROW/COLUMN) (as opposed to an underline). */
+
+static bool
+get_state_at_point (/* Inputs. */
+ int row, int column, rich_location *richloc,
+ int first_non_ws, int last_non_ws,
+ /* Outputs. */
+ int *out_range_idx,
+ bool *out_draw_caret_p)
+{
+ /* Within a multiline range, don't display any underline or caret
+ in any leading or trailing whitespace on a line. */
+ if (column < first_non_ws || column > last_non_ws)
+ return false;
+
+ for (rich_location::range_iter iter = richloc->iter_ranges ();
+ !iter.at_end ();
+ iter.next())
+ {
+ const location_range *range = *iter;
+
+ if (0)
+ fprintf (stderr,
+ "range ( (%i, %i), (%i, %i))->contains_point (%i, %i): %s\n",
+ range->m_start.line,
+ range->m_start.column,
+ range->m_finish.line,
+ range->m_finish.column,
+ row,
+ column,
+ range->contains_point (row, column) ? "true" : "false");
+
+ if (range->contains_point (row, column))
+ {
+ *out_range_idx = iter.index();
+
+ /* Are we at the range's caret? is it visible? */
+ *out_draw_caret_p = false;
+ if (row == range->m_start.line
+ && column == range->m_start.column)
+ *out_draw_caret_p = range->m_show_caret_p;
+
+ /* We are within a range. */
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Get the column beyond the rightmost one that could contain a caret or
+ range marker, given that we stop rendering at trailing whitespace. */
+
+static int
+get_x_bound_for_row (int row, int caret_column,
+ rich_location *richloc,
+ int last_non_ws)
+{
+ int result = caret_column + 1;
+
+ for (rich_location::range_iter iter = richloc->iter_ranges ();
+ !iter.at_end ();
+ iter.next())
+ {
+ const location_range *range = *iter;
+ if (row >= range->m_start.line)
+ {
+ if (range->m_finish.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.column)
+ result = range->m_finish.column + 1;
+ }
+ else if (row < range->m_finish.line)
+ {
+ /* Within a multiline range; ensure that we render up to the
+ last non-whitespace column. */
+ if (result <= last_non_ws)
+ result = last_non_ws + 1;
+ }
+ }
+ }
+
+ return result;
+}
+
+/* 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 per_range_info" to get things done. */
+
+namespace {
+
+/* A class to inject colorization codes when printing the diagnostic locus,
+ tracking state as it goes. */
+
+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 class for use by "class layout" below for capturing information on
+ how to display a specific range within a rich_location. */
+
+class per_range_info
+{
+ public:
+ per_range_info (int range_idx);
+
+ int get_range_index () const { return m_range_idx; }
+
+ void add_uniquely_captioned_row (int row);
+
+ int get_first_unique_row () const;
+ int get_last_unique_row () const;
+
+ bool contains_line (int line) const;
+
+ void determine_location_for_caption (const char *filename);
+
+ int get_caption_row () const { return m_caption_row; }
+ int get_caption_column () const { return m_caption_column; }
+
+ private:
+ int m_range_idx;
+ auto_vec<int> m_unique_rows;
+ int m_caption_row;
+ int m_caption_column;
+};
+
+/* A class to control the overall layout of a diagnostic.
+
+ The layout is determined within the constructor.
+ It is then printed by repeatedly calling the "print_line" method.
+ Each such call can print two lines: one for the source line itself,
+ and potentially an "annotation" line, containing carets/underlines.
+ Both such lines could be printed with a right-hand margin, containing
+ additional information.
+
+ For now, we just support the case of a single margin at once,
+ showing any captioned ranges that exclusively occupy their lines.
+
+ We also assume we have disjoint ranges. */
+
+class layout
+{
+ public:
+ layout (diagnostic_context *context,
+ const diagnostic_info *diagnostic);
+
+ void print_line (int row);
+
+ private:
+ enum print_line_kind {
+ PRINT_LINE_KIND_SOURCE,
+ PRINT_LINE_KIND_ANNOTATION};
+
+ private:
+ per_range_info *
+ get_any_range (int line);
+
+ void
+ print_any_margin (int line, int column, enum print_line_kind kind);
+
+ private:
+ diagnostic_context *m_context;
+ pretty_printer *m_pp;
+ diagnostic_t m_diagnostic_kind;
+ rich_location *m_richloc;
+ expanded_location m_exploc;
+ colorizer m_colorizer;
+ auto_vec <per_range_info> m_per_range_vec;
+};
+
+/* 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)
+ {
+ finish_state (m_current_state);
+ m_current_state = new_state;
+ begin_state (new_state);
+ }
+}
+
+/* Turn on any colorization for STATE. */
+
+void
+colorizer::begin_state (int state)
+{
+ switch (state)
+ {
+ case STATE_NORMAL_TEXT:
+ break;
+
+ 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;
+
+ case 1:
+ pp_string (m_context->printer, m_range1_cs);
+ break;
+
+ 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;
+ }
+}
+
+/* Turn off any colorization for STATE. */
+
+void
+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 per_range_info. */
+
+/* The constructor for class per_range_info. */
+
+per_range_info::per_range_info (int range_idx)
+: m_range_idx (range_idx),
+ m_unique_rows ()
+{
+}
+
+/* This range is the only range with a caption on row ROW; record it as
+ such. */
+
+void
+per_range_info::add_uniquely_captioned_row (int row)
+{
+ m_unique_rows.safe_push (row);
+}
+
+/* Locate the first row for which this range is the only one with a caption. */
+
+int
+per_range_info::get_first_unique_row () const
+{
+ gcc_assert (m_unique_rows.length () > 0);
+ return m_unique_rows[0];
+}
+
+/* Locate the last row for which this range is the only one with a caption. */
+
+int
+per_range_info::get_last_unique_row () const
+{
+ gcc_assert (m_unique_rows.length () > 0);
+
+ return m_unique_rows[m_unique_rows.length () - 1];
+}
+
+/* Is LINE uniquely captioned by this range? */
+
+bool
+per_range_info::contains_line (int line) const
+{
+ if (0 == m_unique_rows.length ())
+ return false;
+
+ return (line >= get_first_unique_row ()
+ && line <= get_last_unique_row ());
+}
+
+/* Given a source line LINE of length LINE_WIDTH, determin 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;
+}
+
+/* Determine which row to write this range's caption text in (if any). */
+
+void
+per_range_info::determine_location_for_caption (const char *filename)
+{
+ if (0 == m_unique_rows.length ())
+ return;
+
+ /* Determine the widest line, using it as the column in which to render
+ the caption (m_caption_column). */
+ int result = 0;
+ for (int row = get_first_unique_row ();
+ row <= get_last_unique_row ();
+ row++)
+ {
+ int line_width;
+ const char *line = location_get_source_line (filename, row, &line_width);
+ line_width = get_line_width_without_trailing_whitespace (line,
+ line_width);
+ if (result < line_width)
+ result = line_width + 1;
+ }
+ m_caption_column = result;
+
+ /* Determine which row to write the caption text in: the middle row
+ within the range, rounding down mathematically (and thus up
+ visually). */
+ m_caption_row = (get_first_unique_row () + get_last_unique_row ()) / 2;
+}
+
+/* Implementation of class layout. */
+
+/* Constructor for class layout. Populate m_per_range_vec, determining
+ for each range where to draw its caption (if any). */
+
+layout::layout (diagnostic_context * context,
+ const diagnostic_info *diagnostic)
+: m_context (context),
+ m_pp (context->printer),
+ m_diagnostic_kind (diagnostic->kind),
+ m_richloc (diagnostic->richloc),
+ m_exploc (m_richloc->lazily_expand_location ()),
+ m_colorizer (context, diagnostic),
+ m_per_range_vec ()
+{
+ for (rich_location::range_iter iter = m_richloc->iter_ranges ();
+ !iter.at_end ();
+ iter.next())
+ {
+ per_range_info ri (iter.index ());
+ m_per_range_vec.safe_push (ri);
+ }
+
+ /* For each row, determine if it has a unique captioned range. */
+ int last_line = m_richloc->get_last_line ();
+ for (int row = m_richloc->get_first_line ();
+ row <= last_line;
+ row++)
+ {
+ int num_captions_in_row = 0;
+ const location_range *first_caption = NULL;
+ int first_caption_idx = 0; /* silence "maybe-uninitialized" warning
+ (PR 67196). */
+ for (rich_location::range_iter iter = m_richloc->iter_ranges ();
+ !iter.at_end ();
+ iter.next())
+ {
+ const location_range *range = *iter;
+ if (range->m_caption)
+ {
+ if (row >= range->m_start.line
+ && row <= range->m_finish.line)
+ {
+ num_captions_in_row++;
+ if (!first_caption)
+ {
+ first_caption = range;
+ first_caption_idx = iter.index ();
+ }
+ }
+ }
+ }
+ if (first_caption && num_captions_in_row == 1)
+ m_per_range_vec[first_caption_idx].add_uniquely_captioned_row (row);
+ }
+
+ /* At this stage, each per_range_info now has a list of lines for which its
+ location_range uniquely captions that source line. */
+
+ /* Next, calculate the best position in which to draw each caption. */
+ per_range_info *ri;
+ int i;
+ FOR_EACH_VEC_ELT (m_per_range_vec, i, ri)
+ ri->determine_location_for_caption (m_richloc->get_range (i)->m_start.file);
+}
+
+/* Print text describing a line of source code.
+ This typically prints two lines:
+
+ (1) the source code itself, colorized at any ranges, and
+ (2) an annotation line containing any carets/underlines
+ describing the ranges.
+
+ Both lines (1) and (2) may contain a right-most margin containing a
+ vertical bar and a caption, describing a range. */
+
+void
+layout::print_line (int row)
+{
+ int line_width;
+ const char *line = location_get_source_line (m_exploc.file, row,
+ &line_width);
+ if (!line)
+ return;
+
+ m_colorizer.set_normal_text ();
+
+ /* Step 1: print the source code line. */
+
+ /* We will stop printing 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; column <= line_width; column++)
+ {
+ bool in_range_p;
+ int range_idx;
+ bool draw_caret_p;
+ in_range_p = get_state_at_point (row, column,
+ m_richloc,
+ 0, INT_MAX,
+ &range_idx, &draw_caret_p);
+ if (in_range_p)
+ m_colorizer.set_range (range_idx);
+ else
+ m_colorizer.set_normal_text ();
+ char c = *line == '\t' ? ' ' : *line;
+ if (c == '\0')
+ c = ' ';
+ if (c != ' ')
+ {
+ last_non_ws = column;
+ if (first_non_ws == INT_MAX)
+ first_non_ws = column;
+ }
+ pp_character (m_pp, c);
+ line++;
+ }
+ print_any_margin (row, column, PRINT_LINE_KIND_SOURCE);
+
+ pp_newline (m_pp);
+
+ /* Step 2: 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,
+ m_richloc,
+ last_non_ws);
+
+ pp_space (m_pp);
+ for (int column = 1; column < x_bound; column++)
+ {
+ bool in_range_p;
+ int range_idx;
+ bool draw_caret_p;
+ in_range_p = get_state_at_point (row, column,
+ m_richloc,
+ first_non_ws, last_non_ws,
+ &range_idx, &draw_caret_p);
+ if (in_range_p)
+ {
+ /* Within a range. Draw either the caret or an underline. */
+ m_colorizer.set_range (range_idx);
+ if (draw_caret_p)
+ /* Draw the caret. */
+ pp_string (m_pp, m_context->caret_chars[range_idx]);
+ else
+ {
+ /* Within a range, but not drawing the caret. Draw an underline
+ character. */
+ const location_range *range
+ = m_richloc->get_range (range_idx);
+ if (range->m_start.line != range->m_finish.line)
+ {
+ /* Underline multiline ranges using southwest/southeast
+ corners for the very first/last positions, and hbar
+ everywhere else. */
+ if (row == range->m_start.line
+ && column == range->m_start.column)
+ /* Start of multiline range: use a southwest corner for
+ the underline. */
+ pp_string (m_pp, g_line_art.underline_start);
+ else if (row == range->m_finish.line
+ && column == range->m_finish.column)
+ /* End of multiline range: use a southeast corner for
+ the underline. */
+ pp_string (m_pp, g_line_art.underline_end);
+ else
+ pp_string (m_pp, g_line_art.underline_hbar);
+ }
+ else
+ /* Render single-line ranges with the hbar character
+ throughout. */
+ pp_string (m_pp, g_line_art.underline_hbar);
+ }
+ }
+ else
+ {
+ /* Not in a range. */
+ m_colorizer.set_normal_text ();
+ pp_character (m_pp, ' ');
+ }
+ }
+ print_any_margin (row, x_bound, PRINT_LINE_KIND_ANNOTATION);
+ pp_newline (m_pp);
+}
+
+/* Given a line of source code, get the per_range_info for the first range
+ within it, or NULL if there are no ranges. */
+
+per_range_info *
+layout::get_any_range (int line)
+{
+ int i;
+ per_range_info *ri;
+ FOR_EACH_VEC_ELT (m_per_range_vec, i, ri)
+ if (ri->contains_line (line))
+ return ri;
+ return NULL;
+}
+
+/* Helper function for layout::print_line, to print the right-most margin
+ area (for both kinds of line, source and caret/underline). */
+
+void
+layout::print_any_margin (int line, int column, enum print_line_kind kind)
+{
+ /* Locate the range layout for this line, if any. */
+ per_range_info *ri = get_any_range (line);
+
+ /* If we're not in a range, there's no margin. */
+ if (!ri)
+ return;
+
+ /* Fill with whitespace to get to the appropriate y-coordinate for
+ the margin. */
+ while (column++ < ri->get_caption_column ())
+ pp_space (m_pp);
+
+ /* Colorize based on the range. */
+ int range_idx = ri->get_range_index ();
+ m_colorizer.set_range (range_idx);
+
+ /* Draw a vertical line, with corners pointing to the left,
+ (falling back to '|' if unicode box-drawing is not available),
+ printing any caption on the *annotation* line for the
+ appropriate source line*/
+
+ if (line == ri->get_caption_row ()
+ && kind == PRINT_LINE_KIND_ANNOTATION)
+ {
+ /* We are on the annotation line of the
+ appropriate source line to print the caption. */
+
+ /* Draw the line before the caption. */
+ pp_string (m_pp,
+ line == ri->get_last_unique_row ()
+ ? g_line_art.rmargin_caption_row_at_end
+ : g_line_art.rmargin_caption_row);
+
+ location_range *range
+ = m_richloc->get_range (ri->get_range_index ());
+ gcc_assert (range);
+
+ /* Display any caption. */
+ if (range->m_caption)
+ pp_string (m_pp, range->m_caption);
+ }
+ else if (line == ri->get_first_unique_row ()
+ && kind == PRINT_LINE_KIND_SOURCE)
+ /* Start of multiline range. */
+ pp_string (m_pp, g_line_art.rmargin_start);
+ else if (line == ri->get_last_unique_row ()
+ && kind == PRINT_LINE_KIND_ANNOTATION)
+ /* End of multiline range. */
+ pp_string (m_pp, g_line_art.rmargin_end);
+ else
+ /* A normal row: a vertical line. */
+ pp_string (m_pp, g_line_art.rmargin_vbar);
+}
+
+} /* End of anonymous namespace. */
+
+/* For debugging layout issues in diagnostic_show_locus and friends,
+ render a ruler giving column numbers (after the 1-column indent). */
+
+static void
+show_ruler (diagnostic_context *context, int max_width)
+{
+ /* Hundreds. */
+ if (max_width > 99)
+ {
+ pp_space (context->printer);
+ for (int column = 1; column < max_width; column++)
+ if (0 == column % 10)
+ pp_character (context->printer, '0' + (column / 100) % 10);
+ else
+ pp_space (context->printer);
+ pp_newline (context->printer);
+ }
+
+ /* Tens. */
+ pp_space (context->printer);
+ for (int column = 1; column < max_width; column++)
+ if (0 == column % 10)
+ pp_character (context->printer, '0' + (column / 10) % 10);
+ else
+ pp_space (context->printer);
+ pp_newline (context->printer);
+
+ /* Units. */
+ pp_space (context->printer);
+ for (int column = 1; column < max_width; column++)
+ pp_character (context->printer, '0' + (column % 10));
+ pp_newline (context->printer);
+}
+
+/* Print the physical source code corresponding to the location of
+ this diagnostic, with additional annotations.
+ If CONTEXT has set frontend_calls_diagnostic_print_caret_line_p,
+ the code is printed using diagnostic_print_caret_line; otherwise
+ it is printed using diagnostic_print_ranges. */
+
void
diagnostic_show_locus (diagnostic_context * context,
const diagnostic_info *diagnostic)
@@ -75,16 +877,25 @@ diagnostic_show_locus (diagnostic_context * context,
return;
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. */
- if (diagnostic_location (diagnostic, 1) > BUILTINS_LOCATION)
- s1 = diagnostic_expand_location (diagnostic, 1);
+ if (context->frontend_calls_diagnostic_print_caret_line_p)
+ {
+ /* The GCC 5 routine. */
+ expanded_location s0 = diagnostic_expand_location (diagnostic, 0);
+ expanded_location s1 = { };
+ /* Zero-initialized. This is checked later by
+ diagnostic_print_caret_line. */
+
+ if (diagnostic_num_locations (diagnostic) >= 2)
+ s1 = diagnostic->message.m_richloc->get_range (1)->m_start;
- diagnostic_print_caret_line (context, s0, s1,
- context->caret_chars[0],
- context->caret_chars[1]);
+ diagnostic_print_caret_line (context, s0, s1,
+ context->caret_chars[0],
+ context->caret_chars[1]);
+ }
+ else
+ /* The GCC 6 routine. */
+ diagnostic_print_ranges (context, diagnostic);
}
/* Print (part) of the source line given by xloc1 with caret1 pointing
@@ -96,7 +907,7 @@ void
diagnostic_print_caret_line (diagnostic_context * context,
expanded_location xloc1,
expanded_location xloc2,
- char caret1, char caret2)
+ const char *caret1, const char *caret2)
{
if (!diagnostic_same_line (context, xloc1, xloc2))
/* This will mean ignore xloc2. */
@@ -145,22 +956,52 @@ diagnostic_print_caret_line (diagnostic_context * context,
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;
+ const char *caret_min = cmin == xloc1.column ? caret1 : caret2;
+ const char *caret_max = cmin == xloc1.column ? caret2 : caret1;
/* cmin is >= 1, but we indent with an extra space at the start like
we did above. */
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);
+ pp_printf (context->printer, "%s%s%s", caret_cs, caret_min, caret_ce);
if (xloc2.column)
{
for (i++; i < cmax; i++)
pp_space (context->printer);
- pp_printf (context->printer, "%s%c%s", caret_cs, caret_max, caret_ce);
+ pp_printf (context->printer, "%s%s%s", caret_cs, caret_max, caret_ce);
}
pp_set_prefix (context->printer, saved_prefix);
pp_needs_newline (context->printer) = true;
}
+
+/* Print all source lines covered by the locations and any ranges
+ within DIAGNOSTIC, displaying one or more carets and zero or more
+ underlines as appropriate, potentially with captions. */
+
+static void
+diagnostic_print_ranges (diagnostic_context * context,
+ const diagnostic_info *diagnostic)
+{
+ int max_width = context->caret_max_width;
+
+ pp_newline (context->printer);
+
+ const char *saved_prefix = pp_get_prefix (context->printer);
+ pp_set_prefix (context->printer, NULL);
+
+ if (0)
+ show_ruler (context, max_width);
+
+ {
+ layout layout (context, diagnostic);
+ int last_line = diagnostic->richloc->get_last_line ();
+ for (int row = diagnostic->richloc->get_first_line ();
+ row <= last_line;
+ row++)
+ layout.print_line (row);
+ }
+
+ pp_set_prefix (context->printer, saved_prefix);
+}
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
#include "backtrace.h"
#include "diagnostic.h"
#include "diagnostic-color.h"
+#include "box-drawing.h"
#ifdef HAVE_TERMIOS_H
# include <termios.h>
@@ -145,8 +146,9 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
context->classify_diagnostic[i] = DK_UNSPECIFIED;
context->show_caret = false;
diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer));
- for (i = 0; i < MAX_LOCATIONS_PER_MESSAGE; i++)
- context->caret_chars[i] = '^';
+ gcc_assert (g_line_art.default_caret);
+ for (i = 0; i < rich_location::MAX_RANGES; i++)
+ context->caret_chars[i] = g_line_art.default_caret;
context->show_option_requested = false;
context->abort_on_error = false;
context->show_column = false;
@@ -235,16 +237,15 @@ diagnostic_finish (diagnostic_context *context)
translated. */
void
diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
- va_list *args, location_t location,
+ va_list *args, rich_location *richloc,
diagnostic_t kind)
{
+ gcc_assert (richloc);
diagnostic->message.err_no = errno;
diagnostic->message.args_ptr = args;
diagnostic->message.format_spec = msg;
- diagnostic->message.set_location (0, location);
- for (int i = 1; i < MAX_LOCATIONS_PER_MESSAGE; i++)
- diagnostic->message.set_location (i, UNKNOWN_LOCATION);
- diagnostic->override_column = 0;
+ diagnostic->message.m_richloc = richloc;
+ diagnostic->richloc = richloc;
diagnostic->kind = kind;
diagnostic->option_index = 0;
}
@@ -253,10 +254,27 @@ diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
translated. */
void
diagnostic_set_info (diagnostic_info *diagnostic, const char *gmsgid,
- va_list *args, location_t location,
+ va_list *args, rich_location *richloc,
diagnostic_t kind)
{
- diagnostic_set_info_translated (diagnostic, _(gmsgid), args, location, kind);
+ gcc_assert (richloc);
+ diagnostic_set_info_translated (diagnostic, _(gmsgid), args, richloc, kind);
+}
+
+static const char *const diagnostic_kind_color[] = {
+#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C),
+#include "diagnostic.def"
+#undef DEFINE_DIAGNOSTIC_KIND
+ NULL
+};
+
+/* Get a color name for diagnostics of type KIND
+ Result could be NULL. */
+
+const char *
+diagnostic_get_color_for_kind (diagnostic_t kind)
+{
+ return diagnostic_kind_color[kind];
}
/* Return a malloc'd string describing a location. The caller is
@@ -271,12 +289,6 @@ diagnostic_build_prefix (diagnostic_context *context,
#undef DEFINE_DIAGNOSTIC_KIND
"must-not-happen"
};
- static const char *const diagnostic_kind_color[] = {
-#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C),
-#include "diagnostic.def"
-#undef DEFINE_DIAGNOSTIC_KIND
- NULL
- };
gcc_assert (diagnostic->kind < DK_LAST_DIAGNOSTIC_KIND);
const char *text = _(diagnostic_kind_text[diagnostic->kind]);
@@ -775,10 +787,14 @@ diagnostic_report_diagnostic (diagnostic_context *context,
if (option_text)
{
+ const char *cs
+ = colorize_start (pp_show_color (context->printer),
+ diagnostic_kind_color[diagnostic->kind]);
+ const char *ce = colorize_stop (pp_show_color (context->printer));
diagnostic->message.format_spec
= ACONCAT ((diagnostic->message.format_spec,
" ",
- "[", option_text, "]",
+ "[", cs, option_text, ce, "]",
NULL));
free (option_text);
}
@@ -858,9 +874,40 @@ diagnostic_append_note (diagnostic_context *context,
diagnostic_info diagnostic;
va_list ap;
const char *saved_prefix;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_NOTE);
+ if (context->inhibit_notes_p)
+ {
+ va_end (ap);
+ return;
+ }
+ saved_prefix = pp_get_prefix (context->printer);
+ pp_set_prefix (context->printer,
+ diagnostic_build_prefix (context, &diagnostic));
+ pp_newline (context->printer);
+ pp_format (context->printer, &diagnostic.message);
+ pp_output_formatted_text (context->printer);
+ pp_destroy_prefix (context->printer);
+ pp_set_prefix (context->printer, saved_prefix);
+ diagnostic_show_locus (context, &diagnostic);
+ va_end (ap);
+}
+
+/* Same as diagnostic_append_note, but at RICHLOC. */
+
+void
+diagnostic_append_note_at_rich_loc (diagnostic_context *context,
+ rich_location *richloc,
+ const char * gmsgid, ...)
+{
+ diagnostic_info diagnostic;
+ va_list ap;
+ const char *saved_prefix;
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE);
if (context->inhibit_notes_p)
{
va_end (ap);
@@ -885,16 +932,17 @@ emit_diagnostic (diagnostic_t kind, location_t location, int opt,
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, gmsgid);
if (kind == DK_PERMERROR)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
permissive_error_kind (global_dc));
diagnostic.option_index = permissive_error_option (global_dc);
}
else {
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, kind);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, kind);
if (kind == DK_WARNING || kind == DK_PEDWARN)
diagnostic.option_index = opt;
}
@@ -911,9 +959,23 @@ inform (location_t location, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_NOTE);
+ report_diagnostic (&diagnostic);
+ va_end (ap);
+}
+
+/* Same as "inform", but at RICHLOC. */
+void
+inform_at_rich_loc (rich_location *richloc, const char *gmsgid, ...)
+{
+ diagnostic_info diagnostic;
+ va_list ap;
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -926,11 +988,12 @@ inform_n (location_t location, int n, const char *singular_gmsgid,
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, plural_gmsgid);
diagnostic_set_info_translated (&diagnostic,
ngettext (singular_gmsgid, plural_gmsgid, n),
- &ap, location, DK_NOTE);
+ &ap, &richloc, DK_NOTE);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -944,9 +1007,10 @@ warning (int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_WARNING);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
@@ -964,9 +1028,27 @@ warning_at (location_t location, int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_WARNING);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING);
+ diagnostic.option_index = opt;
+ ret = report_diagnostic (&diagnostic);
+ va_end (ap);
+ return ret;
+}
+
+/* Same as warning at, but using RICHLOC. */
+
+bool
+warning_at_rich_loc (rich_location *richloc, int opt, const char *gmsgid, ...)
+{
+ diagnostic_info diagnostic;
+ va_list ap;
+ bool ret;
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_WARNING);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
va_end (ap);
@@ -984,11 +1066,13 @@ warning_n (location_t location, int opt, int n, const char *singular_gmsgid,
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, plural_gmsgid);
diagnostic_set_info_translated (&diagnostic,
ngettext (singular_gmsgid, plural_gmsgid, n),
- &ap, location, DK_WARNING);
+ &ap, &richloc, DK_WARNING
+);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
va_end (ap);
@@ -1014,9 +1098,10 @@ pedwarn (location_t location, int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
va_end (ap);
@@ -1036,9 +1121,28 @@ permerror (location_t location, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
+ permissive_error_kind (global_dc));
+ diagnostic.option_index = permissive_error_option (global_dc);
+ ret = report_diagnostic (&diagnostic);
+ va_end (ap);
+ return ret;
+}
+
+/* Same as "permerror", but at RICHLOC. */
+
+bool
+permerror_at_rich_loc (rich_location *richloc, const char *gmsgid, ...)
+{
+ diagnostic_info diagnostic;
+ va_list ap;
+ bool ret;
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc,
permissive_error_kind (global_dc));
diagnostic.option_index = permissive_error_option (global_dc);
ret = report_diagnostic (&diagnostic);
@@ -1053,9 +1157,10 @@ error (const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ERROR);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -1068,11 +1173,12 @@ error_n (location_t location, int n, const char *singular_gmsgid,
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, plural_gmsgid);
diagnostic_set_info_translated (&diagnostic,
ngettext (singular_gmsgid, plural_gmsgid, n),
- &ap, location, DK_ERROR);
+ &ap, &richloc, DK_ERROR);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -1083,9 +1189,25 @@ error_at (location_t loc, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (loc);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_ERROR);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
+ report_diagnostic (&diagnostic);
+ va_end (ap);
+}
+
+/* Same as above, but use RICH_LOC. */
+
+void
+error_at_rich_loc (rich_location *rich_loc, const char *gmsgid, ...)
+{
+ diagnostic_info diagnostic;
+ va_list ap;
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, rich_loc,
+ DK_ERROR);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -1098,9 +1220,10 @@ sorry (const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_SORRY);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_SORRY);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -1121,9 +1244,10 @@ fatal_error (location_t loc, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (loc);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_FATAL);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_FATAL);
report_diagnostic (&diagnostic);
va_end (ap);
@@ -1139,9 +1263,10 @@ internal_error (const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE);
report_diagnostic (&diagnostic);
va_end (ap);
@@ -1156,9 +1281,10 @@ internal_error_no_backtrace (const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE_NOBT);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE_NOBT);
report_diagnostic (&diagnostic);
va_end (ap);
@@ -1222,3 +1348,11 @@ real_abort (void)
{
abort ();
}
+
+void
+source_range::debug (const char *msg) const
+{
+ rich_location richloc (m_start);
+ richloc.add_range (m_start, m_finish);
+ inform_at_rich_loc (&richloc, "%s", msg);
+}
@@ -29,10 +29,12 @@ along with GCC; see the file COPYING3. If not see
list in diagnostic.def. */
struct diagnostic_info
{
- /* Text to be formatted. It also contains the location(s) for this
- diagnostic. */
+ /* Text to be formatted. */
text_info message;
- unsigned int override_column;
+
+ /* The location at which the diagnostic is to be reported. */
+ rich_location *richloc;
+
/* Auxiliary data for client. */
void *x_data;
/* The kind of diagnostic it is about. */
@@ -107,7 +109,7 @@ struct diagnostic_context
int caret_max_width;
/* Characters used for caret diagnostics. */
- char caret_chars[MAX_LOCATIONS_PER_MESSAGE];
+ const char *caret_chars[rich_location::MAX_RANGES];
/* True if we should print the command line option which controls
each diagnostic, if known. */
@@ -185,6 +187,11 @@ struct diagnostic_context
int lock;
bool inhibit_notes_p;
+
+ /* Does the frontend make calls to diagnostic_print_caret_line?
+ If so, we fall back to the old implementation of
+ diagnostic_show_locus. */
+ bool frontend_calls_diagnostic_print_caret_line_p;
};
static inline void
@@ -256,10 +263,6 @@ extern diagnostic_context *global_dc;
#define report_diagnostic(D) diagnostic_report_diagnostic (global_dc, D)
-/* Override the column number to be used for reporting a
- diagnostic. */
-#define diagnostic_override_column(DI, COL) (DI)->override_column = (COL)
-
/* Override the option index to be used for reporting a
diagnostic. */
#define diagnostic_override_option_index(DI, OPTIDX) \
@@ -283,13 +286,17 @@ extern bool diagnostic_report_diagnostic (diagnostic_context *,
diagnostic_info *);
#ifdef ATTRIBUTE_GCC_DIAG
extern void diagnostic_set_info (diagnostic_info *, const char *, va_list *,
- location_t, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0);
+ rich_location *, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0);
extern void diagnostic_set_info_translated (diagnostic_info *, const char *,
- va_list *, location_t,
+ va_list *, rich_location *,
diagnostic_t)
ATTRIBUTE_GCC_DIAG(2,0);
extern void diagnostic_append_note (diagnostic_context *, location_t,
const char *, ...) ATTRIBUTE_GCC_DIAG(3,4);
+extern void diagnostic_append_note_at_rich_loc (diagnostic_context *,
+ rich_location *,
+ const char *, ...)
+ ATTRIBUTE_GCC_DIAG(3,4);
#endif
extern char *diagnostic_build_prefix (diagnostic_context *, const diagnostic_info *);
void default_diagnostic_starter (diagnostic_context *, diagnostic_info *);
@@ -310,6 +317,14 @@ diagnostic_location (const diagnostic_info * diagnostic, int which = 0)
return diagnostic->message.get_location (which);
}
+/* Return the number of locations to be printed in DIAGNOSTIC. */
+
+static inline unsigned int
+diagnostic_num_locations (const diagnostic_info * diagnostic)
+{
+ return diagnostic->message.m_richloc->get_num_locations ();
+}
+
/* Expand the location of this diagnostic. Use this function for
consistency. Parameter WHICH specifies which location. By default,
expand the first one. */
@@ -317,12 +332,7 @@ diagnostic_location (const diagnostic_info * diagnostic, int which = 0)
static inline expanded_location
diagnostic_expand_location (const diagnostic_info * diagnostic, int which = 0)
{
- expanded_location s
- = expand_location_to_spelling_point (diagnostic_location (diagnostic,
- which));
- if (which == 0 && diagnostic->override_column)
- s.column = diagnostic->override_column;
- return s;
+ return diagnostic->richloc->get_range (which)->m_start;
}
/* This is somehow the right-side margin of a caret line, that is, we
@@ -346,7 +356,11 @@ void
diagnostic_print_caret_line (diagnostic_context * context,
expanded_location xloc1,
expanded_location xloc2,
- char caret1, char caret2);
+ const char *caret1, const char *caret2);
+
+
+extern const char *
+diagnostic_get_color_for_kind (diagnostic_t kind);
/* Pure text formatting support functions. */
extern char *file_name_as_prefix (diagnostic_context *, const char *);
@@ -149,9 +149,9 @@ static void cb_include (cpp_reader *, source_location, const unsigned char *,
static void cb_ident (cpp_reader *, source_location, const cpp_string *);
static void cb_used_define (cpp_reader *, source_location, cpp_hashnode *);
static void cb_used_undef (cpp_reader *, source_location, cpp_hashnode *);
-static bool cb_cpp_error (cpp_reader *, int, int, location_t, unsigned int,
+static bool cb_cpp_error (cpp_reader *, int, int, rich_location *,
const char *, va_list *)
- ATTRIBUTE_GCC_DIAG(6,0);
+ ATTRIBUTE_GCC_DIAG(5,0);
void pp_dir_change (cpp_reader *, const char *);
static int dump_macro (cpp_reader *, cpp_hashnode *, void *);
@@ -1026,13 +1026,12 @@ cb_used_define (cpp_reader *pfile, source_location line ATTRIBUTE_UNUSED,
/* Callback from cpp_error for PFILE to print diagnostics from the
preprocessor. The diagnostic is of type LEVEL, with REASON set
to the reason code if LEVEL is represents a warning, at location
- LOCATION, with column number possibly overridden by COLUMN_OVERRIDE
- if not zero; MSG is the translated message and AP the arguments.
+ RICHLOC; MSG is the translated message and AP the arguments.
Returns true if a diagnostic was emitted, false otherwise. */
static bool
cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
- location_t location, unsigned int column_override,
+ rich_location *richloc,
const char *msg, va_list *ap)
{
diagnostic_info diagnostic;
@@ -1067,9 +1066,7 @@ cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
gcc_unreachable ();
}
diagnostic_set_info_translated (&diagnostic, msg, ap,
- location, dlevel);
- if (column_override)
- diagnostic_override_column (&diagnostic, column_override);
+ richloc, dlevel);
if (reason == CPP_W_WARNING_DIRECTIVE)
diagnostic_override_option_index (&diagnostic, OPT_Wcpp);
ret = report_diagnostic (&diagnostic);
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic.h"
#include "diagnostic-color.h"
#include "tree-diagnostic.h" /* tree_diagnostics_defaults */
+#include "box-drawing.h"
#include <new> /* For placement-new */
@@ -773,6 +774,7 @@ gfc_warning (int opt, const char *gmsgid, va_list ap)
va_copy (argp, ap);
diagnostic_info diagnostic;
+ rich_location rich_loc (UNKNOWN_LOCATION);
bool fatal_errors = global_dc->fatal_errors;
pretty_printer *pp = global_dc->printer;
output_buffer *tmp_buffer = pp->buffer;
@@ -787,7 +789,7 @@ gfc_warning (int opt, const char *gmsgid, va_list ap)
--werrorcount;
}
- diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION,
+ diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc,
DK_WARNING);
diagnostic.option_index = opt;
bool ret = report_diagnostic (&diagnostic);
@@ -938,10 +940,12 @@ gfc_format_decoder (pretty_printer *pp,
/* If location[0] != UNKNOWN_LOCATION means that we already
processed one of %C/%L. */
int loc_num = text->get_location (0) == UNKNOWN_LOCATION ? 0 : 1;
- text->set_location (loc_num,
- linemap_position_for_loc_and_offset (line_table,
- loc->lb->location,
- offset));
+ source_range range
+ = source_range::from_location (
+ linemap_position_for_loc_and_offset (line_table,
+ loc->lb->location,
+ offset));
+ text->set_range (loc_num, range, true);
pp_string (pp, result[loc_num]);
return true;
}
@@ -1075,7 +1079,7 @@ gfc_diagnostic_starter (diagnostic_context *context,
expanded_location s1 = diagnostic_expand_location (diagnostic);
expanded_location s2;
- bool one_locus = diagnostic_location (diagnostic, 1) == UNKNOWN_LOCATION;
+ bool one_locus = diagnostic->richloc->get_num_locations () < 2;
bool same_locus = false;
if (!one_locus)
@@ -1173,10 +1177,11 @@ gfc_warning_now_at (location_t loc, int opt, const char *gmsgid, ...)
{
va_list argp;
diagnostic_info diagnostic;
+ rich_location rich_loc (loc);
bool ret;
va_start (argp, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &argp, loc, DK_WARNING);
+ diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_WARNING);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
va_end (argp);
@@ -1190,10 +1195,11 @@ gfc_warning_now (int opt, const char *gmsgid, ...)
{
va_list argp;
diagnostic_info diagnostic;
+ rich_location rich_loc (UNKNOWN_LOCATION);
bool ret;
va_start (argp, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION,
+ diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc,
DK_WARNING);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
@@ -1209,11 +1215,12 @@ gfc_error_now (const char *gmsgid, ...)
{
va_list argp;
diagnostic_info diagnostic;
+ rich_location rich_loc (UNKNOWN_LOCATION);
error_buffer.flag = true;
va_start (argp, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR);
+ diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_ERROR);
report_diagnostic (&diagnostic);
va_end (argp);
}
@@ -1226,9 +1233,10 @@ gfc_fatal_error (const char *gmsgid, ...)
{
va_list argp;
diagnostic_info diagnostic;
+ rich_location rich_loc (UNKNOWN_LOCATION);
va_start (argp, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_FATAL);
+ diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_FATAL);
report_diagnostic (&diagnostic);
va_end (argp);
@@ -1291,6 +1299,7 @@ gfc_error (const char *gmsgid, va_list ap)
}
diagnostic_info diagnostic;
+ rich_location richloc (UNKNOWN_LOCATION);
bool fatal_errors = global_dc->fatal_errors;
pretty_printer *pp = global_dc->printer;
output_buffer *tmp_buffer = pp->buffer;
@@ -1306,7 +1315,7 @@ gfc_error (const char *gmsgid, va_list ap)
--errorcount;
}
- diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR);
+ diagnostic_set_info (&diagnostic, gmsgid, &argp, &richloc, DK_ERROR);
report_diagnostic (&diagnostic);
if (buffered_p)
@@ -1336,9 +1345,10 @@ gfc_internal_error (const char *gmsgid, ...)
{
va_list argp;
diagnostic_info diagnostic;
+ rich_location rich_loc (UNKNOWN_LOCATION);
va_start (argp, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ICE);
+ diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_ICE);
report_diagnostic (&diagnostic);
va_end (argp);
@@ -1470,8 +1480,9 @@ gfc_diagnostics_init (void)
diagnostic_starter (global_dc) = gfc_diagnostic_starter;
diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
diagnostic_format_decoder (global_dc) = gfc_format_decoder;
- global_dc->caret_chars[0] = '1';
- global_dc->caret_chars[1] = '2';
+ global_dc->caret_chars[0] = "1";
+ global_dc->caret_chars[1] = "2";
+ global_dc->frontend_calls_diagnostic_print_caret_line_p = true;
pp_warning_buffer = new (XNEW (output_buffer)) output_buffer ();
pp_warning_buffer->flush_p = false;
/* pp_error_buffer is statically allocated. This simplifies memory
@@ -1488,6 +1499,6 @@ gfc_diagnostics_finish (void)
defaults. */
diagnostic_starter (global_dc) = gfc_diagnostic_starter;
diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
- global_dc->caret_chars[0] = '^';
- global_dc->caret_chars[1] = '^';
+ global_dc->caret_chars[0] = g_line_art.default_caret;
+ global_dc->caret_chars[1] = g_line_art.default_caret;
}
new file mode 100644
@@ -0,0 +1,93 @@
+/* Implementation of gcc_rich_location class
+ Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+#include "tree-core.h"
+#include "tree.h"
+#include "diagnostic-core.h"
+#include "gcc-rich-location.h"
+#include "print-tree.h"
+#include "pretty-print.h"
+#include "intl.h"
+#include "cpplib.h"
+#include "diagnostic.h"
+
+/* Add a range covering [START, FINISH], with a caption given
+ by translating and formatting GMSGID and any variadic args. */
+
+void
+gcc_rich_location::add_range_with_caption (location_t start, location_t finish,
+ diagnostic_context *context,
+ const char *gmsgid, ...)
+{
+ gcc_assert (context);
+ gcc_assert (gmsgid);
+
+ va_list ap;
+ va_start (ap, gmsgid);
+
+ char *caption = expand_caption_va (context, gmsgid, &ap);
+ add_range (start, finish, caption, BUFFER_OWNERSHIP_GIVEN, false);
+
+ va_end (ap);
+}
+
+/* Translate and expand the given GMSGID and ARGS into a caption
+ (or NULL).
+
+ If non-NULL, ownership of the result transfers to the caller,
+ which must free it. */
+
+char *
+gcc_rich_location::expand_caption_va (diagnostic_context *context,
+ const char *gmsgid, va_list *args)
+{
+ gcc_assert (context);
+ gcc_assert (gmsgid);
+
+ /* Only bother if show-caret is enabled. */
+ if (!context->show_caret)
+ return NULL;
+
+ /* Format the text, and return a copy. */
+ pretty_printer * const pp = context->printer;
+ char *result;
+ text_info text;
+ text.err_no = errno;
+ text.args_ptr = args;
+ text.format_spec = G_(gmsgid);
+ pp_format (pp, &text);
+ pp_output_formatted_text (pp);
+ result = xstrdup (pp_formatted_text (pp));
+ pp_clear_output_area (pp);
+ return result;
+}
new file mode 100644
@@ -0,0 +1,54 @@
+/* Declarations relating to class gcc_rich_location
+ Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_RICH_LOCATION_H
+#define GCC_RICH_LOCATION_H
+
+/* A gcc_rich_location is libcpp's rich_location with additional
+ helper methods for working with gcc's types. */
+class gcc_rich_location : public rich_location
+{
+ public:
+ /* Constructors. */
+
+ /* Constructing from a location. */
+ gcc_rich_location (source_location loc) :
+ rich_location (loc) {}
+
+ /* Constructing from a source_range. */
+ gcc_rich_location (source_range src_range) :
+ rich_location (src_range) {}
+
+
+ /* Methods for adding additional details. */
+
+ void
+ add_range_with_caption (location_t start, location_t finish,
+ diagnostic_context *context,
+ const char *gmsgid, ...)
+ ATTRIBUTE_GCC_DIAG(5,6);
+
+ private:
+ static char *
+ expand_caption_va (diagnostic_context *context,
+ const char *gmsgid, va_list *args)
+ ATTRIBUTE_GCC_DIAG(2, 0);
+};
+
+#endif /* GCC_RICH_LOCATION_H */
@@ -53,14 +53,23 @@ unsigned verbose;
static struct line_maps *line_table;
+expanded_location
+linemap_client_expand_location_to_spelling_point (source_location loc)
+{
+ const struct line_map_ordinary *map;
+ loc = linemap_resolve_location (line_table, loc, LRK_SPELLING_LOCATION, &map);
+ return linemap_expand_location (line_table, map, loc);
+}
+
static bool
#if GCC_VERSION >= 4001
-__attribute__((format (printf, 6, 0)))
+__attribute__((format (printf, 5, 0)))
#endif
-error_cb (cpp_reader *, int errtype, int, source_location location,
- unsigned int, const char *msg, va_list *ap)
+error_cb (cpp_reader *, int errtype, int, rich_location *richloc,
+ const char *msg, va_list *ap)
{
const line_map_ordinary *map;
+ source_location location = richloc->get_loc ();
linemap_resolve_location (line_table, location, LRK_SPELLING_LOCATION, &map);
expanded_location loc = linemap_expand_location (line_table, map, location);
fprintf (stderr, "%s:%d:%d %s: ", loc.file, loc.line, loc.column,
@@ -102,9 +111,10 @@ __attribute__((format (printf, 2, 3)))
#endif
fatal_at (const cpp_token *tk, const char *msg, ...)
{
+ rich_location richloc (tk->src_loc);
va_list ap;
va_start (ap, msg);
- error_cb (NULL, CPP_DL_FATAL, 0, tk->src_loc, 0, msg, &ap);
+ error_cb (NULL, CPP_DL_FATAL, 0, &richloc, msg, &ap);
va_end (ap);
}
@@ -114,9 +124,10 @@ __attribute__((format (printf, 2, 3)))
#endif
fatal_at (source_location loc, const char *msg, ...)
{
+ rich_location richloc (loc);
va_list ap;
va_start (ap, msg);
- error_cb (NULL, CPP_DL_FATAL, 0, loc, 0, msg, &ap);
+ error_cb (NULL, CPP_DL_FATAL, 0, &richloc, msg, &ap);
va_end (ap);
}
@@ -126,9 +137,10 @@ __attribute__((format (printf, 2, 3)))
#endif
warning_at (const cpp_token *tk, const char *msg, ...)
{
+ rich_location richloc (tk->src_loc);
va_list ap;
va_start (ap, msg);
- error_cb (NULL, CPP_DL_WARNING, 0, tk->src_loc, 0, msg, &ap);
+ error_cb (NULL, CPP_DL_WARNING, 0, &richloc, msg, &ap);
va_end (ap);
}
@@ -138,9 +150,10 @@ __attribute__((format (printf, 2, 3)))
#endif
warning_at (source_location loc, const char *msg, ...)
{
+ rich_location richloc (loc);
va_list ap;
va_start (ap, msg);
- error_cb (NULL, CPP_DL_WARNING, 0, loc, 0, msg, &ap);
+ error_cb (NULL, CPP_DL_WARNING, 0, &richloc, msg, &ap);
va_end (ap);
}
@@ -751,6 +751,13 @@ expand_location_to_spelling_point (source_location loc)
return expand_location_1 (loc, /*expansion_point_p=*/false);
}
+expanded_location
+linemap_client_expand_location_to_spelling_point (source_location loc)
+{
+ return expand_location_to_spelling_point (loc);
+}
+
+
/* If LOCATION is in a system header and if it is a virtual location for
a token coming from the expansion of a macro, unwind it to the
location of the expansion point of the macro. Otherwise, just return
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
#include "intl.h"
+#include "box-drawing.h"
#ifdef HAVE_LANGINFO_CODESET
#include <langinfo.h>
@@ -86,6 +87,14 @@ gcc_init_libintl (void)
}
#endif
}
+
+ bool have_utf8 = false;
+#if defined HAVE_LANGINFO_CODESET
+ if (locale_utf8)
+ have_utf8 = true;
+#endif
+
+ g_line_art.init (have_utf8);
}
#if defined HAVE_WCHAR_H && defined HAVE_WORKING_MBSTOWCS && defined HAVE_WCSWIDTH
@@ -31,6 +31,27 @@ along with GCC; see the file COPYING3. If not see
#include <iconv.h>
#endif
+/* Overwrite the range within this text_info's rich_location.
+ For use e.g. when implementing "+" in client format decoders. */
+
+void
+text_info::set_range (unsigned int idx, source_range range, bool caret_p)
+{
+ gcc_checking_assert (m_richloc);
+ m_richloc->set_range (idx, range, caret_p);
+}
+
+location_t
+text_info::get_location (unsigned int index_of_location) const
+{
+ gcc_checking_assert (m_richloc);
+
+ if (index_of_location == 0)
+ return m_richloc->get_loc ();
+ else
+ return UNKNOWN_LOCATION;
+}
+
// Default construct an output buffer.
output_buffer::output_buffer ()
@@ -27,11 +27,6 @@ along with GCC; see the file COPYING3. If not see
/* Maximum number of format string arguments. */
#define PP_NL_ARGMAX 30
-/* Maximum number of locations associated to each message. If
- location 'i' is UNKNOWN_LOCATION, then location 'i+1' is not
- valid. */
-#define MAX_LOCATIONS_PER_MESSAGE 2
-
/* The type of a text to be formatted according a format specification
along with a list of things. */
struct text_info
@@ -40,21 +35,17 @@ struct text_info
va_list *args_ptr;
int err_no; /* for %m */
void **x_data;
+ rich_location *m_richloc;
- inline void set_location (unsigned int index_of_location, location_t loc)
+ inline void set_location (unsigned int idx, location_t loc, bool caret_p)
{
- gcc_checking_assert (index_of_location < MAX_LOCATIONS_PER_MESSAGE);
- this->locations[index_of_location] = loc;
+ source_range src_range;
+ src_range.m_start = loc;
+ src_range.m_finish = loc;
+ set_range (idx, src_range, caret_p);
}
-
- inline location_t get_location (unsigned int index_of_location) const
- {
- gcc_checking_assert (index_of_location < MAX_LOCATIONS_PER_MESSAGE);
- return this->locations[index_of_location];
- }
-
-private:
- location_t locations[MAX_LOCATIONS_PER_MESSAGE];
+ void set_range (unsigned int idx, source_range range, bool caret_p);
+ location_t get_location (unsigned int index_of_location) const;
};
/* How often diagnostics are prefixed by their locations:
@@ -69,9 +69,10 @@ diagnostic_for_asm (const rtx_insn *insn, const char *msg, va_list *args_ptr,
diagnostic_t kind)
{
diagnostic_info diagnostic;
+ rich_location richloc (location_for_asm (insn));
diagnostic_set_info (&diagnostic, msg, args_ptr,
- location_for_asm (insn), kind);
+ &richloc, kind);
report_diagnostic (&diagnostic);
}
new file mode 100644
@@ -0,0 +1,114 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret" } */
+
+/* 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 test1 (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test 1" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = myvar.x;
+ ~~~~~^~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test2 (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test 2" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = myvar.x;
+ ~~~~~^~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test3 (void)
+{
+#if 0
+ x = first_function () + second_function (); /* { dg-warning "test 3" } */
+
+/* { dg-begin-multiline-output "" }
+ x = first_function () + second_function ();
+ ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+
+void test4 (void)
+{
+#if 0
+ x = (first_function ()
+ + second_function ()); /* { dg-warning "test 4" } */
+
+/* { dg-begin-multiline-output "" }
+ x = (first_function ()|
+ ~~~~~~~~~~~~~~~~~+type 'float'
+ + second_function ()); |
+ ^ ~~~~~~~~~~~~~~~~~~ +type 'void'
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test5 (void)
+{
+#if 0
+ x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,
+ consectetur, adipiscing, elit,
+ sed, eiusmod, tempor,
+ incididunt, ut, labore, et,
+ dolore, magna, aliqua)
+ + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, /* { dg-warning "test 5" } */
+ amet, consectetur,
+ adipiscing, elit, sed,
+ eiusmod, tempor, incididunt,
+ ut, labore, et, dolore,
+ magna, aliqua));
+
+/* { dg-begin-multiline-output "" }
+ x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,|
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+ consectetur, adipiscing, elit, |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
+ sed, eiusmod, tempor, |
+ ~~~~~~~~~~~~~~~~~~~~~ +type 'float'
+ incididunt, ut, labore, et, |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
+ dolore, magna, aliqua) |
+ ~~~~~~~~~~~~~~~~~~~~~~ |
+ + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, |
+ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+ amet, consectetur, |
+ ~~~~~~~~~~~~~~~~~~ |
+ adipiscing, elit, sed, |
+ ~~~~~~~~~~~~~~~~~~~~~~ +type 'void'
+ eiusmod, tempor, incididunt, |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
+ ut, labore, et, dolore, |
+ ~~~~~~~~~~~~~~~~~~~~~~~ |
+ magna, aliqua)); |
+ ~~~~~~~~~~~~~~ |
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test6 (void)
+{
+#if 0
+ float f = 98.6f; /* { dg-warning "test 6" } */
+/* { dg-begin-multiline-output "" }
+ float f = 98.6f;
+ ^~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}
new file mode 100644
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret -fplugin-arg-diagnostic_plugin_test_show_locus-color" } */
+
+/* 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 test1 (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test 1" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = [32m[Kmyvar[m[K[01;35m[K.[m[K[34m[Kx[m[K;
+ [32m[K~~~~~[m[K[01;35m[K^[m[K[34m[K~
+[m[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test2 (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test 2" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = [32m[Kmyvar[m[K[01;35m[K.[m[K[34m[Kx[m[K;
+ [32m[K~~~~~[m[K[01;35m[K^[m[K[34m[K~
+[m[K
+ { dg-end-multiline-output "" } */
+#endif
+}
new file mode 100644
@@ -0,0 +1,58 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret -fplugin-arg-diagnostic_plugin_test_show_locus-force-utf8" } */
+
+/* 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 test1 (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test 1" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = myvar.x;
+ ─────▲─
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test2 (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test 2" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = myvar.x;
+ ─────▲─
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test3 (void)
+{
+#if 0
+ x = first_function () + second_function (); /* { dg-warning "test 3" } */
+
+/* { dg-begin-multiline-output "" }
+ x = first_function () + second_function ();
+ ───────────────── ▲ ──────────────────
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+/* TODO: missing test4 and test5 for now (regex issues). */
+
+void test6 (void)
+{
+#if 0
+ float f = 98.6f; /* { dg-warning "test 6" } */
+/* { dg-begin-multiline-output "" }
+ float f = 98.6f;
+ ▲────
+ { dg-end-multiline-output "" } */
+#endif
+}
new file mode 100644
@@ -0,0 +1,62 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret -fplugin-arg-diagnostic_plugin_test_show_locus-force-utf8 -fplugin-arg-diagnostic_plugin_test_show_locus-color" } */
+
+/* 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 test1 (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test 1" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = [32m[Kmyvar[m[K[01;35m[K.[m[K[34m[Kx[m[K;
+ [32m[K─────[m[K[01;35m[K▲[m[K[34m[K─
+[m[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test2 (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test 2" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = [32m[Kmyvar[m[K[01;35m[K.[m[K[34m[Kx[m[K;
+ [32m[K─────[m[K[01;35m[K▲[m[K[34m[K─
+[m[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test3 (void)
+{
+#if 0
+ x = first_function () + second_function (); /* { dg-warning "test 3" } */
+
+/* { dg-begin-multiline-output "" }
+ x = [32m[Kfirst_function ()[m[K [01;35m[K+[m[K [34m[Ksecond_function ()[m[K;
+ [32m[K─────────────────[m[K [01;35m[K▲[m[K [34m[K──────────────────
+[m[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+/* TODO: missing test4 and test5 for now (regex issues). */
+
+void test6 (void)
+{
+#if 0
+ float f = 98.6f; /* { dg-warning "test 6" } */
+/* { dg-begin-multiline-output "" }
+ float f = [01;35m[K98.6f[m[K;
+ [01;35m[K▲────
+[m[K
+ { dg-end-multiline-output "" } */
+#endif
+}
new file mode 100644
@@ -0,0 +1,361 @@
+/* { dg-options "-O" } */
+
+/* This plugin exercises the diagnostics-printing code.
+
+ The goal is to unit-test the range-printing code without needing any
+ correct range data within the compiler's IR. We can't use any real
+ diagnostics for this, so we have to fake it, hence this plugin.
+
+ There are four test files used with this code:
+
+ diagnostic-test-show-locus-ascii-bw.c
+ ..........................-ascii-color.c
+ ..........................-utf-8-bw.c
+ ..........................-utf-8-color.c
+
+ to exercise the different combinations of:
+
+ - ASCII vs UTF-8
+ - uncolored vs colored output.
+
+ by supplying plugin arguments to hack in the desired behavior:
+
+ -fplugin-arg-diagnostic_plugin_test_show_locus-force-utf8
+ -fplugin-arg-diagnostic_plugin_test_show_locus-color
+
+ The test files contain functions, but the body of each
+ function is disabled using the preprocessor. The plugin detects
+ the functions by name, and inject diagnostics within them, using
+ hard-coded locations relative to the top of each function.
+
+ The plugin uses a function "get_loc" below to map from line/column
+ numbers to source_location, and this relies on input_location being in
+ the same ordinary line_map as the locations in question. The plugin
+ runs after parsing, so input_location will be at the end of the file.
+
+ This need for all of the test code to be in a single ordinary line map
+ means that each test file needs to have a very long line near the top
+ (potentially to cover the extra byte-count of UTF-8 or colorized data),
+ to ensure that further very long lines don't start a new linemap.
+ This also means that we can't use macros in the test files. */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "gcc-rich-location.h"
+#include "print-tree.h"
+#include "box-drawing.h"
+
+/* FIXME: (dmalcolm)
+ This plugin is currently the only user of
+ gcc_rich_location::add_range_with_caption
+ As such, the symbol is present in libbackend.a, but not in "cc1",
+ and running the plugin fails with a linker error:
+ ./diagnostic_plugin_test_show_locus.so: undefined symbol: _ZN17gcc_rich_location22add_range_with_captionEjjP18diagnostic_contextPKcz
+ which c++filt tells us is:
+ ./diagnostic_plugin_test_show_locus.so: undefined symbol: gcc_rich_location::add_range_with_caption(unsigned int, unsigned int, diagnostic_context*, char const*, ...)
+
+ I've tried various workarounds (adding DEBUG_FUNCTION to the
+ method, taking its address), but can't seem to fix it that way.
+ So as a nasty workaround, the following material is copied&pasted
+ from gcc-rich-location.c: */
+
+void
+gcc_rich_location::add_range_with_caption (location_t start, location_t finish,
+ diagnostic_context *context,
+ const char *gmsgid, ...)
+{
+ gcc_assert (context);
+ gcc_assert (gmsgid);
+
+ va_list ap;
+ va_start (ap, gmsgid);
+
+ char *caption = expand_caption_va (context, gmsgid, &ap);
+ add_range (start, finish, caption, BUFFER_OWNERSHIP_GIVEN, false);
+
+ va_end (ap);
+}
+
+char *
+gcc_rich_location::expand_caption_va (diagnostic_context *context,
+ const char *gmsgid, va_list *args)
+{
+ gcc_assert (context);
+ gcc_assert (gmsgid);
+
+ /* Only bother if show-caret is enabled. */
+ if (!context->show_caret)
+ return NULL;
+
+ /* Format the text, and return a copy. */
+ pretty_printer * const pp = context->printer;
+ char *result;
+ text_info text;
+ text.err_no = errno;
+ text.args_ptr = args;
+ text.format_spec = G_(gmsgid);
+ pp_format (pp, &text);
+ pp_output_formatted_text (pp);
+ result = xstrdup (pp_formatted_text (pp));
+ pp_clear_output_area (pp);
+ return result;
+}
+
+/* FIXME: end of material taken from gcc-rich-location.c */
+
+int plugin_is_GPL_compatible;
+
+const pass_data pass_data_test_show_locus =
+{
+ GIMPLE_PASS, /* type */
+ "test_show_locus", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_test_show_locus : public gimple_opt_pass
+{
+public:
+ pass_test_show_locus(gcc::context *ctxt)
+ : gimple_opt_pass(pass_data_test_show_locus, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ bool gate (function *) { return true; }
+ virtual unsigned int execute (function *);
+
+}; // class pass_test_show_locus
+
+/* Given LINE_NUM and COL_NUM, generate a source_location in the
+ current file, relative to input_location. This relies on the
+ location being expressible in the same ordinary line_map as
+ input_location (which is typically at the end of the source file
+ when this is called). Hence the test files we compile with this
+ plugin must have an initial very long line (to avoid long lines
+ starting a new line map), and must not use macros.
+
+ COL_NUM uses the Emacs convention of 0-based column numbers. */
+
+static source_location
+get_loc (unsigned int line_num, unsigned int col_num)
+{
+ /* Use input_location to get the relevant line_map */
+ const struct line_map_ordinary *line_map
+ = (const line_map_ordinary *)(linemap_lookup (line_table,
+ input_location));
+
+ /* Convert from 0-based column numbers to 1-based column numbers. */
+ source_location loc
+ = linemap_position_for_line_and_column (line_map,
+ line_num, col_num + 1);
+
+ return loc;
+}
+
+/* Was "color" passed in as a plugin argument? */
+static bool force_show_locus_color = false;
+
+/* We want to verify the colorized output of diagnostic_show_locus,
+ but turning on colorization for everything confuses "dg-warning" etc.
+ Hence we special-case it within this plugin by using this modified
+ version of default_diagnostic_finalizer, which, if "color" is
+ passed in as a plugin argument turns on colorization, but just
+ for diagnostic_show_locus. */
+
+static void
+custom_diagnostic_finalizer (diagnostic_context *context,
+ diagnostic_info *diagnostic)
+{
+ bool old_show_color = pp_show_color (context->printer);
+ if (force_show_locus_color)
+ pp_show_color (context->printer) = true;
+ diagnostic_show_locus (context, diagnostic);
+ pp_show_color (context->printer) = old_show_color;
+
+ pp_destroy_prefix (context->printer);
+ pp_newline_and_flush (context->printer);
+}
+
+/* Exercise the diagnostic machinery to emit various warnings,
+ for use by diagnostic-test-show-locus-*.c.
+
+ We inject each warning relative to the start of a function,
+ which avoids lots of hardcoded absolute locations. */
+
+static void
+test_show_locus (function *fun)
+{
+ tree fndecl = fun->decl;
+ tree identifier = DECL_NAME (fndecl);
+ const char *fnname = IDENTIFIER_POINTER (identifier);
+ location_t fnstart = fun->function_start_locus;
+ int fnstart_line = LOCATION_LINE (fnstart);
+
+ diagnostic_finalizer (global_dc) = custom_diagnostic_finalizer;
+
+ /* Test 1. */
+ if (0 == strcmp (fnname, "test1"))
+ {
+ const int line = fnstart_line + 2;
+ rich_location richloc (get_loc (line, 15));
+ richloc.add_range (get_loc (line, 10), get_loc (line, 14));
+ richloc.add_range (get_loc (line, 16), get_loc (line, 16));
+ warning_at_rich_loc (&richloc, 0, "test 1");
+ }
+
+ /* Test 2: as before, but add captions. */
+ if (0 == strcmp (fnname, "test2"))
+ {
+ const int line = fnstart_line + 2;
+ gcc_rich_location richloc (get_loc (line, 15));
+ richloc.add_range_with_caption (get_loc (line, 10), get_loc (line, 14),
+ global_dc,
+ "hello");
+ richloc.add_range_with_caption (get_loc (line, 16), get_loc (line, 16),
+ global_dc,
+ "world");
+ warning_at_rich_loc (&richloc, 0, "test 2");
+ }
+
+ /* Test 3. */
+ if (0 == strcmp (fnname, "test3"))
+ {
+ const int line = fnstart_line + 2;
+ gcc_rich_location richloc (get_loc (line, 24));
+ richloc.add_range_with_caption (get_loc (line, 6),
+ get_loc (line, 22),
+ global_dc,
+ "type %qT", float_type_node);
+ richloc.add_range_with_caption (get_loc (line, 26),
+ get_loc (line, 43),
+ global_dc,
+ "type %qT", void_type_node);
+ warning_at_rich_loc (&richloc, 0, "test 3");
+ }
+
+ /* Test 4. */
+ if (0 == strcmp (fnname, "test4"))
+ {
+ const int line = fnstart_line + 2;
+ gcc_rich_location richloc (get_loc (line + 1, 7));
+ richloc.add_range_with_caption (get_loc (line, 7),
+ get_loc (line, 23),
+ global_dc,
+ "type %qT", float_type_node);
+ richloc.add_range_with_caption (get_loc (line + 1, 9),
+ get_loc (line + 1, 26),
+ global_dc,
+ "type %qT", void_type_node);
+ warning_at_rich_loc (&richloc, 0, "test 4");
+ }
+
+ /* Test 5. Multiline example with captions. */
+ if (0 == strcmp (fnname, "test5"))
+ {
+ const int line = fnstart_line + 2;
+ gcc_rich_location richloc (get_loc (line + 5, 7));
+ richloc.add_range_with_caption (get_loc (line, 7),
+ get_loc (line + 4, 65),
+ global_dc,
+ "type %qT", float_type_node);
+ richloc.add_range_with_caption (get_loc (line + 5, 9),
+ get_loc (line + 10, 61),
+ global_dc,
+ "type %qT", void_type_node);
+ warning_at_rich_loc (&richloc, 0, "test 5");
+ }
+
+ /* Test 6: example of a single-range location where the range extends
+ beyond the caret. */
+ if (0 == strcmp (fnname, "test6"))
+ {
+ const int line = fnstart_line + 2;
+ source_range src_range;
+ src_range.m_start = get_loc (line, 12);
+ src_range.m_finish = get_loc (line, 16);
+ gcc_rich_location richloc (src_range);
+ warning_at_rich_loc (&richloc, 0, "test 6");
+ }
+}
+
+unsigned int
+pass_test_show_locus::execute (function *fun)
+{
+ test_show_locus (fun);
+ return 0;
+}
+
+static gimple_opt_pass *
+make_pass_test_show_locus (gcc::context *ctxt)
+{
+ return new pass_test_show_locus (ctxt);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+ struct plugin_gcc_version *version)
+{
+ struct register_pass_info pass_info;
+ const char *plugin_name = plugin_info->base_name;
+ int argc = plugin_info->argc;
+ struct plugin_argument *argv = plugin_info->argv;
+
+ if (!plugin_default_version_check (version, &gcc_version))
+ return 1;
+
+ for (int i = 0; i < argc; i++)
+ {
+ if (0 == strcmp (argv[i].key, "force-utf8"))
+ {
+ /* Forcibly reinit-box-drawing to use UTF-8, so that
+ we can exercise this even though LANG=C. */
+ g_line_art.init (1);
+
+ /* We also need to reset the carets within the diagnostic
+ context to use the new default caret. */
+ for (int j = 0; j < rich_location::MAX_RANGES; j++)
+ global_dc->caret_chars[j] = g_line_art.default_caret;
+ }
+ else if (0 == strcmp (argv[i].key, "color"))
+ force_show_locus_color = true;
+ }
+
+ pass_info.pass = make_pass_test_show_locus (g);
+ pass_info.reference_pass_name = "ssa";
+ pass_info.ref_pass_instance_number = 1;
+ pass_info.pos_op = PASS_POS_INSERT_AFTER;
+ register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+ &pass_info);
+
+ return 0;
+}
@@ -63,6 +63,11 @@ set plugin_test_list [list \
{ start_unit_plugin.c start_unit-test-1.c } \
{ finish_unit_plugin.c finish_unit-test-1.c } \
{ wide-int_plugin.c wide-int-test-1.c } \
+ { diagnostic_plugin_test_show_locus.c \
+ diagnostic-test-show-locus-ascii-bw.c \
+ diagnostic-test-show-locus-ascii-color.c \
+ diagnostic-test-show-locus-utf-8-bw.c \
+ diagnostic-test-show-locus-utf-8-color.c } \
]
foreach plugin_test $plugin_test_list {
@@ -29,6 +29,7 @@ load_lib libgloss.exp
load_lib target-libpath.exp
load_lib torture-options.exp
load_lib fortran-modules.exp
+load_lib multiline.exp
# We set LC_ALL and LANG to C so that we get the same error messages as expected.
setenv LC_ALL C
@@ -289,7 +289,7 @@ default_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
}
if (set_locus)
- text->set_location (0, DECL_SOURCE_LOCATION (t));
+ text->set_location (0, DECL_SOURCE_LOCATION (t), true);
if (DECL_P (t))
{
@@ -3602,7 +3602,7 @@ void
percent_K_format (text_info *text)
{
tree t = va_arg (*text->args_ptr, tree), block;
- text->set_location (0, EXPR_LOCATION (t));
+ text->set_location (0, EXPR_LOCATION (t), true);
gcc_assert (pp_ti_abstract_origin (text) != NULL);
block = TREE_BLOCK (t);
*pp_ti_abstract_origin (text) = NULL;
@@ -57,7 +57,8 @@ cpp_diagnostic (cpp_reader * pfile, int level, int reason,
if (!pfile->cb.error)
abort ();
- ret = pfile->cb.error (pfile, level, reason, src_loc, 0, _(msgid), ap);
+ rich_location richloc (src_loc);
+ ret = pfile->cb.error (pfile, level, reason, &richloc, _(msgid), ap);
return ret;
}
@@ -139,7 +140,9 @@ cpp_diagnostic_with_line (cpp_reader * pfile, int level, int reason,
if (!pfile->cb.error)
abort ();
- ret = pfile->cb.error (pfile, level, reason, src_loc, column, _(msgid), ap);
+ rich_location richloc (src_loc);
+ richloc.override_column (column);
+ ret = pfile->cb.error (pfile, level, reason, &richloc, _(msgid), ap);
return ret;
}
@@ -573,9 +573,9 @@ struct cpp_callbacks
/* Called to emit a diagnostic. This callback receives the
translated message. */
- bool (*error) (cpp_reader *, int, int, source_location, unsigned int,
+ bool (*error) (cpp_reader *, int, int, rich_location *,
const char *, va_list *)
- ATTRIBUTE_FPTR_PRINTF(6,0);
+ ATTRIBUTE_FPTR_PRINTF(5,0);
/* Callbacks for when a macro is expanded, or tested (whether
defined or not at the time) in #ifdef, #ifndef or "defined". */
@@ -118,6 +118,35 @@ typedef unsigned int linenum_type;
libcpp/location-example.txt. */
typedef unsigned int source_location;
+/* A range of source locations.
+
+ Ranges are half-open:
+ m_start is the first location within the range, whereas
+ m_finish is the first location *after* the range.
+
+ We may need a more compact way to store these, but for now,
+ let's do it the simple way, as a pair. */
+struct GTY(()) source_range
+{
+ source_location m_start;
+ source_location m_finish;
+
+ void debug (const char *msg) const;
+
+ /* We avoid using constructors, since various structs that
+ don't yet have constructors will embed instances of
+ source_range. */
+
+ /* Make a source_range from a source_location. */
+ static source_range from_location (source_location loc)
+ {
+ source_range result;
+ result.m_start = loc;
+ result.m_finish = loc;
+ return result;
+ }
+};
+
/* Memory allocation function typedef. Works like xrealloc. */
typedef void *(*line_map_realloc) (void *, size_t);
@@ -1015,6 +1044,204 @@ typedef struct
bool sysp;
} expanded_location;
+/* Both gcc and emacs number source *lines* starting at 1, but
+ they have differing conventions for *columns*.
+
+ GCC uses a 1-based convention for source columns,
+ whereas Emacs's M-x column-number-mode uses a 0-based convention.
+
+ For example, an error in the initial, left-hand
+ column of source line 3 is reported by GCC as:
+
+ some-file.c:3:1: error: ...etc...
+
+ On navigating to the location of that error in Emacs
+ (e.g. via "next-error"),
+ the locus is reported in the Mode Line
+ (assuming M-x column-number-mode) as:
+
+ some-file.c 10% (3, 0)
+
+ i.e. "3:1:" in GCC corresponds to "(3, 0)" in Emacs. */
+
+/* Ranges are closed
+ m_start is the first location within the range, and
+ m_finish is the last location within the range. */
+struct location_range
+{
+ expanded_location m_start;
+ expanded_location m_finish;
+
+ /* Should a caret be drawn for this range? Typically this is
+ true for the 0th range, and false for subsequent ranges,
+ but the Fortran frontend overrides this for rendering things like:
+
+ x = x + y
+ 1 2
+ Error: Shapes for operands at (1) and (2) are not conformable
+
+ where "1" and "2" are notionally carets. */
+ bool m_show_caret_p;
+
+ /* Caption, if any. If non-NULL, this is dynamically allocated
+ and must be freed. */
+ char *m_caption;
+
+ bool contains_point (int row, int column) const;
+};
+
+/* Should the callee take ownership of char *, or make a copy? */
+enum buffer_ownership
+{
+ BUFFER_OWNERSHIP_GIVEN, /* Take ownership. */
+ BUFFER_OWNERSHIP_BORROWED /* Make a copy. */
+};
+
+/* A "rich" source code location, for use when printing diagnostics.
+ A rich_location has a "primary location", along with zero or more
+ additional ranges.
+
+ rich_location instances are intended to be allocated on the stack
+ when generating diagnostics, and to be short-lived.
+
+ The zeroth range can be thought of as an extension of the primary
+ location within the rich_location; additional ranges may be added
+ to help the user identify other pertinent clauses in a diagnostic.
+
+ Each range may be flagged for having a caret displayed
+ at its start; typically this is the case for the zeroth
+ range.
+
+ Each range may optionally have a caption.
+
+ This class is subclassed by gcc (class gcc_rich_location) to add
+ additional methods; see gcc/gcc-rich-location.h. */
+
+class rich_location
+{
+ public:
+ /* Constructors. */
+
+ /* Constructing from a location. */
+ rich_location (source_location loc);
+
+ /* Constructing from a source_range. */
+ rich_location (source_range src_range);
+
+ /* Destructor. */
+ virtual ~rich_location ();
+
+ /* Accessors. */
+ source_location get_loc () const { return m_loc; }
+
+ source_location *get_loc_addr () { return &m_loc; }
+
+ void
+ add_range (source_location start, source_location finish,
+ bool show_caret_p = false);
+
+ void
+ add_range (source_location start, source_location finish,
+ char *caption,
+ enum buffer_ownership ownership,
+ bool show_caret_p);
+
+ void
+ add_range (source_range src_range,
+ bool show_caret_p = false);
+
+ void
+ add_range (source_range src_range,
+ char *caption,
+ enum buffer_ownership ownership);
+
+ /* The caption, if non-NULL must already be a copy. */
+ void
+ add_range (location_range *src_range);
+
+ void
+ set_range (unsigned int idx, source_range src_range,
+ bool show_caret_p);
+
+ int get_first_line ();
+ int get_last_line ();
+
+ unsigned int get_num_locations () const { return m_num_ranges; }
+
+ location_range *get_range (unsigned int idx)
+ {
+ linemap_assert (idx < m_num_ranges);
+ return &m_ranges[idx];
+ }
+
+ expanded_location lazily_expand_location ();
+
+ class range_iter
+ {
+ public:
+ range_iter (rich_location *richloc);
+ bool at_end () const;
+ void next ();
+ location_range *operator * () const;
+ unsigned int index () const;
+
+ private:
+ rich_location *m_richloc;
+ unsigned int m_idx;
+ };
+
+ range_iter iter_ranges () { return range_iter (this); }
+
+ void
+ override_column (int column);
+
+public:
+ static const int MAX_RANGES = 3;
+
+protected:
+ friend class range_iter;
+
+ source_location m_loc;
+
+ unsigned int m_num_ranges;
+ location_range m_ranges[MAX_RANGES];
+
+ bool m_have_expanded_location;
+ expanded_location m_expanded_location;
+};
+
+inline
+rich_location::range_iter::range_iter (rich_location *richloc) :
+ m_richloc (richloc),
+ m_idx (0)
+{
+}
+
+inline bool
+rich_location::range_iter::at_end () const
+{
+ return m_idx >= m_richloc->m_num_ranges;
+}
+
+inline void
+rich_location::range_iter::next ()
+{
+ m_idx++;
+}
+
+inline location_range *
+rich_location::range_iter::operator * () const
+{
+ return m_richloc->get_range (m_idx);
+}
+
+inline unsigned int
+rich_location::range_iter::index () const
+{
+ return m_idx;
+}
+
+
/* This is enum is used by the function linemap_resolve_location
below. The meaning of the values is explained in the comment of
that function. */
@@ -1158,4 +1385,13 @@ void linemap_dump (FILE *, struct line_maps *, unsigned, bool);
specifies how many macro maps to dump. */
void line_table_dump (FILE *, struct line_maps *, unsigned int, unsigned int);
+/* The rich_location class requires a way to expand source_location instances.
+ We would directly use expand_location_to_spelling_point, which is
+ implemented in gcc/input.c, but we also need to use it for rich_location
+ within genmatch.c.
+ Hence we require client code of libcpp to implement the following
+ symbol. */
+extern expanded_location
+linemap_client_expand_location_to_spelling_point (source_location );
+
#endif /* !LIBCPP_LINE_MAP_H */
@@ -1746,3 +1746,211 @@ line_table_dump (FILE *stream, struct line_maps *set, unsigned int num_ordinary,
fprintf (stream, "\n");
}
}
+
+/* class rich_location. */
+
+/* Construct a rich_location with location LOC as its initial range. */
+
+rich_location::rich_location (source_location loc) :
+ m_loc (loc),
+ m_num_ranges (0),
+ m_have_expanded_location (false)
+{
+ /* Set up the 0th range: */
+ add_range (loc, loc, true);
+}
+
+/* Construct a rich_location with source_range SRC_RANGE as its
+ initial range. */
+
+rich_location::rich_location (source_range src_range)
+: m_loc (src_range.m_start),
+ m_num_ranges (0),
+ m_have_expanded_location (false)
+{
+ /* Set up the 0th range: */
+ add_range (src_range, true);
+}
+
+/* The destructor for class rich_location. */
+
+rich_location::~rich_location ()
+{
+ for (unsigned int i = 0; i < m_num_ranges; i++)
+ free (m_ranges[i].m_caption);
+}
+
+/* Get the first line of the rich_location, either that of
+ the primary location, or of one of the ranges. */
+
+int
+rich_location::get_first_line ()
+{
+ lazily_expand_location ();
+ int result = m_expanded_location.line;
+ for (range_iter iter = iter_ranges (); !iter.at_end (); iter.next())
+ {
+ location_range *range = *iter;
+ if (result > range->m_start.line)
+ result = range->m_start.line;
+ }
+ return result;
+}
+
+/* Get the last line of the rich_location, either that of
+ the primary location, or of one of the ranges. */
+
+int
+rich_location::get_last_line ()
+{
+ lazily_expand_location ();
+ int result = m_expanded_location.line;
+ for (range_iter iter = iter_ranges (); !iter.at_end (); iter.next())
+ {
+ location_range *range = *iter;
+ if (result < range->m_finish.line)
+ result = range->m_finish.line;
+ }
+ return result;
+}
+
+/* Get an expanded_location for this rich_location's primary
+ location. */
+
+expanded_location
+rich_location::lazily_expand_location ()
+{
+ if (!m_have_expanded_location)
+ {
+ m_expanded_location
+ = linemap_client_expand_location_to_spelling_point (m_loc);
+ m_have_expanded_location = true;
+ }
+
+ return m_expanded_location;
+}
+
+/* Set the column of the primary location. */
+
+void
+rich_location::override_column (int column)
+{
+ lazily_expand_location ();
+ m_expanded_location.column = column;
+}
+
+/* Add the given range, with no caption. */
+
+void
+rich_location::add_range (source_location start, source_location finish,
+ bool show_caret_p)
+{
+ linemap_assert (m_num_ranges < MAX_RANGES);
+
+ add_range (start, finish, NULL, BUFFER_OWNERSHIP_GIVEN, show_caret_p);
+}
+
+/* Add the given range, with a caption, taking a copy if OWNERSHIP
+ is BUFFER_OWNERSHIP_BORROWED, or assuming ownership if OWNERSHIP
+ is BUFFER_OWNERSHIP_GIVEN. */
+
+void
+rich_location::add_range (source_location start, source_location finish,
+ char *caption,
+ enum buffer_ownership ownership,
+ bool show_caret_p)
+{
+ linemap_assert (m_num_ranges < MAX_RANGES);
+
+ location_range *range = &m_ranges[m_num_ranges++];
+ range->m_start = linemap_client_expand_location_to_spelling_point (start);
+ range->m_finish = linemap_client_expand_location_to_spelling_point (finish);
+ range->m_show_caret_p = show_caret_p;
+ if (ownership == BUFFER_OWNERSHIP_BORROWED)
+ range->m_caption = caption ? xstrdup (caption) : NULL;
+ else
+ range->m_caption = caption;
+}
+
+/* Add the given range, with no caption. */
+
+void
+rich_location::add_range (source_range src_range, bool show_caret_p)
+{
+ linemap_assert (m_num_ranges < MAX_RANGES);
+
+ add_range (src_range.m_start, src_range.m_finish,
+ NULL, BUFFER_OWNERSHIP_GIVEN,
+ show_caret_p);
+}
+
+/* Add the given range, with a caption, taking a copy if OWNERSHIP
+ is BUFFER_OWNERSHIP_BORROWED, or assuming ownership if OWNERSHIP
+ is BUFFER_OWNERSHIP_GIVEN. */
+
+void
+rich_location::add_range (source_range src_range,
+ char *caption,
+ enum buffer_ownership ownership)
+{
+ linemap_assert (m_num_ranges < MAX_RANGES);
+
+ add_range (src_range.m_start, src_range.m_finish, caption, ownership,
+ false);
+}
+
+void
+rich_location::add_range (location_range *src_range)
+{
+ linemap_assert (m_num_ranges < MAX_RANGES);
+
+ /* The caption, if non-NULL, must already be a copy. */
+ m_ranges[m_num_ranges++] = *src_range;
+}
+
+/* Add or overwrite the range given by IDX. It must either
+ overwrite an existing range, or add one *exactly* on the end of
+ the array.
+
+ This is primarily for use by gcc when implementing diagnostic
+ format decoders e.g. the "+" in the C/C++ frontends, for handling
+ format codes like "%q+D" (which writes the source location of a
+ tree back into range 0 of the rich_location).
+
+ If SHOW_CARET_P is true, then the range should be rendered with
+ a caret at its starting location. This
+ is for use by the Fortran frontend, for implementing the
+ "%C" and "%L" format codes. */
+
+void
+rich_location::set_range (unsigned int idx, source_range src_range,
+ bool show_caret_p)
+{
+ linemap_assert (idx < MAX_RANGES);
+
+ /* We can either overwrite an existing range, or add one exactly
+ on the end of the array. */
+ linemap_assert (idx <= m_num_ranges);
+
+ location_range *locrange = &m_ranges[idx];
+ locrange->m_start
+ = linemap_client_expand_location_to_spelling_point (src_range.m_start);
+ locrange->m_finish
+ = linemap_client_expand_location_to_spelling_point (src_range.m_finish);
+
+ locrange->m_show_caret_p = show_caret_p;
+
+ /* Are we adding a range onto the end? */
+ if (idx == m_num_ranges)
+ {
+ locrange->m_caption = NULL;
+ m_num_ranges = idx + 1;
+ }
+
+ if (idx == 0)
+ {
+ m_loc = src_range.m_start;
+ /* Mark any cached value here as dirty. */
+ m_have_expanded_location = false;
+ }
+}