+2015-11-06 David Malcolm <dmalcolm@redhat.com>
+
+ * diagnostic-color.c (color_dict): Eliminate "caret"; add "range1"
+ and "range2".
+ (parse_gcc_colors): Update comment to describe default GCC_COLORS.
+ * diagnostic-core.h (warning_at_rich_loc): New declaration.
+ (error_at_rich_loc): New declaration.
+ (permerror_at_rich_loc): New declaration.
+ (inform_at_rich_loc): New declaration.
+ * diagnostic-show-locus.c (adjust_line): Delete.
+ (struct point_state): New struct.
+ (class colorizer): New class.
+ (class layout_point): New class.
+ (class layout_range): New class.
+ (struct line_bounds): New.
+ (class layout): New class.
+ (colorizer::colorizer): New ctor.
+ (colorizer::~colorizer): New dtor.
+ (layout::layout): New ctor.
+ (layout::print_source_line): New method.
+ (layout::print_annotation_line): New method.
+ (layout::get_state_at_point): New method.
+ (layout::get_x_bound_for_row): New method.
+ (diagnostic_show_locus): Reimplement in terms of class layout.
+ (diagnostic_print_caret_line): Delete.
+ * diagnostic.c (diagnostic_initialize): Replace
+ MAX_LOCATIONS_PER_MESSAGE with rich_location::MAX_RANGES.
+ (diagnostic_set_info_translated): Convert param from location_t
+ to rich_location *. Eliminate calls to set_location on the
+ message in favor of storing the rich_location ptr there.
+ (diagnostic_set_info): Convert param from location_t to
+ rich_location *.
+ (diagnostic_build_prefix): Break out array into...
+ (diagnostic_kind_color): New variable.
+ (diagnostic_get_color_for_kind): New function.
+ (diagnostic_report_diagnostic): Colorize the option_text
+ using the color for the severity.
+ (diagnostic_append_note): Update for change in signature of
+ diagnostic_set_info.
+ (diagnostic_append_note_at_rich_loc): New function.
+ (emit_diagnostic): Update for change in signature of
+ diagnostic_set_info.
+ (inform): Likewise.
+ (inform_at_rich_loc): New function.
+ (inform_n): Update for change in signature of diagnostic_set_info.
+ (warning): Likewise.
+ (warning_at): Likewise.
+ (warning_at_rich_loc): New function.
+ (warning_n): Update for change in signature of diagnostic_set_info.
+ (pedwarn): Likewise.
+ (permerror): Likewise.
+ (permerror_at_rich_loc): New function.
+ (error): Update for change in signature of diagnostic_set_info.
+ (error_n): Likewise.
+ (error_at): Likewise.
+ (error_at_rich_loc): New function.
+ (sorry): Update for change in signature of diagnostic_set_info.
+ (fatal_error): Likewise.
+ (internal_error): Likewise.
+ (internal_error_no_backtrace): Likewise.
+ (source_range::debug): New function.
+ * diagnostic.h (struct diagnostic_info): Eliminate field
+ "override_column". Add field "richloc".
+ (struct diagnostic_context): Add field "colorize_source_p".
+ (diagnostic_override_column): Delete.
+ (diagnostic_set_info): Convert param from location_t to
+ rich_location *.
+ (diagnostic_set_info_translated): Likewise.
+ (diagnostic_append_note_at_rich_loc): New function.
+ (diagnostic_num_locations): New function.
+ (diagnostic_expand_location): Get the location from the
+ rich_location.
+ (diagnostic_print_caret_line): Delete.
+ (diagnostic_get_color_for_kind): New declaration.
+ * genmatch.c (linemap_client_expand_location_to_spelling_point): New.
+ (error_cb): Update for change in signature of "error" callback.
+ (fatal_at): Likewise.
+ (warning_at): Likewise.
+ * input.c (linemap_client_expand_location_to_spelling_point): New.
+ * pretty-print.c (text_info::set_range): New method.
+ (text_info::get_location): New method.
+ * pretty-print.h (MAX_LOCATIONS_PER_MESSAGE): Eliminate this macro.
+ (struct text_info): Eliminate "locations" array in favor of
+ "m_richloc", a rich_location *.
+ (textinfo::set_location): Add a "caret_p" param, and reimplement
+ in terms of a call to set_range.
+ (textinfo::get_location): Eliminate inline implementation in favor of
+ an out-of-line reimplementation.
+ (textinfo::set_range): New method.
+ * rtl-error.c (diagnostic_for_asm): Update for change in signature
+ of diagnostic_set_info.
+ * tree-diagnostic.c (default_tree_printer): Update for new
+ "caret_p" param for textinfo::set_location.
+ * tree-pretty-print.c (percent_K_format): Likewise.
+
2015-11-06 Ramana Radhakrishnan <ramana.radhakrishnan@arm.com>
Properly apply.
+2015-11-06 David Malcolm <dmalcolm@redhat.com>
+
+ * c-common.c (c_cpp_error): Convert parameter from location_t to
+ rich_location *. Eliminate the "column_override" parameter and
+ the call to diagnostic_override_column.
+ Update the "done_lexing" clause to set range 0
+ on the rich_location, rather than overwriting a location_t.
+ * c-common.h (c_cpp_error): Convert parameter from location_t to
+ rich_location *. Eliminate the "column_override" parameter.
+
2015-11-05 Cesar Philippidis <cesar@codesourcery.com>
Thomas Schwinge <thomas@codesourcery.com>
James Norris <jnorris@codesourcery.com>
/* 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;
gcc_unreachable ();
}
if (done_lexing)
- location = input_location;
+ richloc->set_range (0,
+ source_range::from_location (input_location),
+ true, 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);
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);
+2015-11-06 David Malcolm <dmalcolm@redhat.com>
+
+ * c-decl.c (warn_defaults_to): Update for change in signature
+ of diagnostic_set_info.
+ * c-errors.c (pedwarn_c99): Likewise.
+ (pedwarn_c90): Likewise.
+ * c-objc-common.c (c_tree_printer): Update for new "caret_p" param
+ for textinfo::set_location.
+
2015-11-05 Cesar Philippidis <cesar@codesourcery.com>
Thomas Schwinge <thomas@codesourcery.com>
James Norris <jnorris@codesourcery.com>
{
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);
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;
/* 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);
}
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, gmsgid);
/* Warnings such as -Wvla are the most specific ones. */
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;
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;
/* 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);
}
{
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)
+2015-11-06 David Malcolm <dmalcolm@redhat.com>
+
+ * error.c (cp_printer): Update for new "caret_p" param for
+ textinfo::set_location.
+ (pedwarn_cxx98): Update for change in signature of
+ diagnostic_set_info.
+
2015-11-06 Jason Merrill <jason@redhat.com>
Support non-type constrained-type-specifiers.
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
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);
{ "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 }
}
/* 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)
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);
# include <sys/ioctl.h>
#endif
-/* If LINE is longer than MAX_WIDTH, and COLUMN is not smaller than
- MAX_WIDTH by some margin, then adjust the start of the line such
- that the COLUMN is smaller than MAX_WIDTH minus the margin. The
- margin is either CARET_LINE_MARGIN characters or the difference
- between the column and the length of the line, whatever is smaller.
- The length of LINE is given by LINE_WIDTH. */
-static const char *
-adjust_line (const char *line, int line_width,
- int max_width, int *column_p)
-{
- int right_margin = CARET_LINE_MARGIN;
- int column = *column_p;
-
- gcc_checking_assert (line_width >= column);
- right_margin = MIN (line_width - column, right_margin);
- right_margin = max_width - right_margin;
- if (line_width >= max_width && column > right_margin)
+/* Classes for rendering source code and diagnostics, within an
+ anonymous namespace.
+ The work is done by "class layout", which embeds and uses
+ "class colorizer" and "class layout_range" to get things done. */
+
+namespace {
+
+/* The state at a given point of the source code, assuming that we're
+ in a range: which range are we in, and whether we should draw a caret at
+ this point. */
+
+struct point_state
+{
+ int range_idx;
+ bool draw_caret_p;
+};
+
+/* A class to inject colorization codes when printing the diagnostic locus.
+
+ It has one kind of colorization for each of:
+ - normal text
+ - range 0 (the "primary location")
+ - range 1
+ - range 2
+
+ The class caches the lookup of the color codes for the above.
+
+ The class also has responsibility for tracking which of the above is
+ active, filtering out unnecessary changes. This allows
+ layout::print_source_line and layout::print_annotation_line
+ to simply request a colorization code for *every* character they print,
+ via this class, and have the filtering be done for them here. */
+
+class colorizer
+{
+ public:
+ colorizer (diagnostic_context *context,
+ const diagnostic_info *diagnostic);
+ ~colorizer ();
+
+ void set_range (int range_idx) { set_state (range_idx); }
+ void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
+
+ private:
+ void set_state (int state);
+ void begin_state (int state);
+ void finish_state (int state);
+
+ private:
+ static const int STATE_NORMAL_TEXT = -1;
+
+ diagnostic_context *m_context;
+ const diagnostic_info *m_diagnostic;
+ int m_current_state;
+ const char *m_caret_cs;
+ const char *m_caret_ce;
+ const char *m_range1_cs;
+ const char *m_range2_cs;
+ const char *m_range_ce;
+};
+
+/* A point within a layout_range; similar to an expanded_location,
+ but after filtering on file. */
+
+class layout_point
+{
+ public:
+ layout_point (const expanded_location &exploc)
+ : m_line (exploc.line),
+ m_column (exploc.column) {}
+
+ int m_line;
+ int m_column;
+};
+
+/* A class for use by "class layout" below: a filtered location_range. */
+
+class layout_range
+{
+ public:
+ layout_range (const location_range *loc_range);
+
+ bool contains_point (int row, int column) const;
+
+ layout_point m_start;
+ layout_point m_finish;
+ bool m_show_caret_p;
+ layout_point m_caret;
+};
+
+/* A struct for use by layout::print_source_line for telling
+ layout::print_annotation_line the extents of the source line that
+ it printed, so that underlines can be clipped appropriately. */
+
+struct line_bounds
+{
+ int m_first_non_ws;
+ int m_last_non_ws;
+};
+
+/* A class to control the overall layout when printing a diagnostic.
+
+ The layout is determined within the constructor.
+ It is then printed by repeatedly calling the "print_source_line"
+ and "print_annotation_line" methods.
+
+ We assume we have disjoint ranges. */
+
+class layout
+{
+ public:
+ layout (diagnostic_context *context,
+ const diagnostic_info *diagnostic);
+
+ int get_first_line () const { return m_first_line; }
+ int get_last_line () const { return m_last_line; }
+
+ bool print_source_line (int row, line_bounds *lbounds_out);
+ void print_annotation_line (int row, const line_bounds lbounds);
+
+ private:
+ bool
+ get_state_at_point (/* Inputs. */
+ int row, int column,
+ int first_non_ws, int last_non_ws,
+ /* Outputs. */
+ point_state *out_state);
+
+ int
+ get_x_bound_for_row (int row, int caret_column,
+ int last_non_ws);
+
+ private:
+ diagnostic_context *m_context;
+ pretty_printer *m_pp;
+ diagnostic_t m_diagnostic_kind;
+ expanded_location m_exploc;
+ colorizer m_colorizer;
+ bool m_colorize_source_p;
+ auto_vec <layout_range> m_layout_ranges;
+ int m_first_line;
+ int m_last_line;
+ int m_x_offset;
+};
+
+/* Implementation of "class colorizer". */
+
+/* The constructor for "colorizer". Lookup and store color codes for the
+ different kinds of things we might need to print. */
+
+colorizer::colorizer (diagnostic_context *context,
+ const diagnostic_info *diagnostic) :
+ m_context (context),
+ m_diagnostic (diagnostic),
+ m_current_state (STATE_NORMAL_TEXT)
+{
+ m_caret_ce = colorize_stop (pp_show_color (context->printer));
+ m_range1_cs = colorize_start (pp_show_color (context->printer), "range1");
+ m_range2_cs = colorize_start (pp_show_color (context->printer), "range2");
+ m_range_ce = colorize_stop (pp_show_color (context->printer));
+}
+
+/* The destructor for "colorize". If colorization is on, print a code to
+ turn it off. */
+
+colorizer::~colorizer ()
+{
+ finish_state (m_current_state);
+}
+
+/* Update state, printing color codes if necessary if there's a state
+ change. */
+
+void
+colorizer::set_state (int new_state)
+{
+ if (m_current_state != new_state)
{
- line += column - right_margin;
- *column_p = right_margin;
+ finish_state (m_current_state);
+ m_current_state = new_state;
+ begin_state (new_state);
}
- return line;
}
-/* Print the physical source line corresponding to the location of
- this diagnostic, and a caret indicating the precise column. This
- function only prints two caret characters if the two locations
- given by DIAGNOSTIC are on the same line according to
- diagnostic_same_line(). */
+/* Turn on any colorization for STATE. */
+
void
-diagnostic_show_locus (diagnostic_context * context,
- const diagnostic_info *diagnostic)
+colorizer::begin_state (int state)
{
- if (!context->show_caret
- || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
- || diagnostic_location (diagnostic, 0) == context->last_location)
- return;
+ switch (state)
+ {
+ case STATE_NORMAL_TEXT:
+ break;
- context->last_location = diagnostic_location (diagnostic, 0);
- expanded_location s0 = diagnostic_expand_location (diagnostic, 0);
- expanded_location s1 = { };
- /* Zero-initialized. This is checked later by diagnostic_print_caret_line. */
+ case 0:
+ /* Make range 0 be the same color as the "kind" text
+ (error vs warning vs note). */
+ pp_string
+ (m_context->printer,
+ colorize_start (pp_show_color (m_context->printer),
+ diagnostic_get_color_for_kind (m_diagnostic->kind)));
+ break;
- if (diagnostic_location (diagnostic, 1) > BUILTINS_LOCATION)
- s1 = diagnostic_expand_location (diagnostic, 1);
+ case 1:
+ pp_string (m_context->printer, m_range1_cs);
+ break;
- diagnostic_print_caret_line (context, s0, s1,
- context->caret_chars[0],
- context->caret_chars[1]);
+ case 2:
+ pp_string (m_context->printer, m_range2_cs);
+ break;
+
+ default:
+ /* We don't expect more than 3 ranges per diagnostic. */
+ gcc_unreachable ();
+ break;
+ }
}
-/* Print (part) of the source line given by xloc1 with caret1 pointing
- at the column. If xloc2.column != 0 and it fits within the same
- line as xloc1 according to diagnostic_same_line (), then caret2 is
- printed at xloc2.colum. Otherwise, the caller has to set up things
- to print a second caret line for xloc2. */
+/* Turn off any colorization for STATE. */
+
void
-diagnostic_print_caret_line (diagnostic_context * context,
- expanded_location xloc1,
- expanded_location xloc2,
- char caret1, char caret2)
-{
- if (!diagnostic_same_line (context, xloc1, xloc2))
- /* This will mean ignore xloc2. */
- xloc2.column = 0;
- else if (xloc1.column == xloc2.column)
- xloc2.column++;
-
- int cmax = MAX (xloc1.column, xloc2.column);
+colorizer::finish_state (int state)
+{
+ switch (state)
+ {
+ case STATE_NORMAL_TEXT:
+ break;
+
+ case 0:
+ pp_string (m_context->printer, m_caret_ce);
+ break;
+
+ default:
+ /* Within a range. */
+ gcc_assert (state > 0);
+ pp_string (m_context->printer, m_range_ce);
+ break;
+ }
+}
+
+/* Implementation of class layout_range. */
+
+/* The constructor for class layout_range.
+ Initialize various layout_point fields from expanded_location
+ equivalents; we've already filtered on file. */
+
+layout_range::layout_range (const location_range *loc_range)
+: m_start (loc_range->m_start),
+ m_finish (loc_range->m_finish),
+ m_show_caret_p (loc_range->m_show_caret_p),
+ m_caret (loc_range->m_caret)
+{
+}
+
+/* Is (column, row) within the given range?
+ We've already filtered on the file.
+
+ Ranges are closed (both limits are within the range).
+
+ Example A: a single-line range:
+ start: (col=22, line=2)
+ finish: (col=38, line=2)
+
+ |00000011111111112222222222333333333344444444444
+ |34567890123456789012345678901234567890123456789
+--+-----------------------------------------------
+01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
+03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+ Example B: a multiline range with
+ start: (col=14, line=3)
+ finish: (col=08, line=5)
+
+ |00000011111111112222222222333333333344444444444
+ |34567890123456789012345678901234567890123456789
+--+-----------------------------------------------
+01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
+04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
+05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+--+-----------------------------------------------
+
+ Legend:
+ - 'b' indicates a point *before* the range
+ - 'S' indicates the start of the range
+ - 'w' indicates a point within the range
+ - 'F' indicates the finish of the range (which is
+ within it).
+ - 'a' indicates a subsequent point *after* the range. */
+
+bool
+layout_range::contains_point (int row, int column) const
+{
+ gcc_assert (m_start.m_line <= m_finish.m_line);
+ /* ...but the equivalent isn't true for the columns;
+ consider example B in the comment above. */
+
+ if (row < m_start.m_line)
+ /* Points before the first line of the range are
+ outside it (corresponding to line 01 in example A
+ and lines 01 and 02 in example B above). */
+ return false;
+
+ if (row == m_start.m_line)
+ /* On same line as start of range (corresponding
+ to line 02 in example A and line 03 in example B). */
+ {
+ if (column < m_start.m_column)
+ /* Points on the starting line of the range, but
+ before the column in which it begins. */
+ return false;
+
+ if (row < m_finish.m_line)
+ /* This is a multiline range; the point
+ is within it (corresponds to line 03 in example B
+ from column 14 onwards) */
+ return true;
+ else
+ {
+ /* This is a single-line range. */
+ gcc_assert (row == m_finish.m_line);
+ return column <= m_finish.m_column;
+ }
+ }
+
+ /* The point is in a line beyond that containing the
+ start of the range: lines 03 onwards in example A,
+ and lines 04 onwards in example B. */
+ gcc_assert (row > m_start.m_line);
+
+ if (row > m_finish.m_line)
+ /* The point is beyond the final line of the range
+ (lines 03 onwards in example A, and lines 06 onwards
+ in example B). */
+ return false;
+
+ if (row < m_finish.m_line)
+ {
+ /* The point is in a line that's fully within a multiline
+ range (e.g. line 04 in example B). */
+ gcc_assert (m_start.m_line < m_finish.m_line);
+ return true;
+ }
+
+ gcc_assert (row == m_finish.m_line);
+
+ return column <= m_finish.m_column;
+}
+
+/* Given a source line LINE of length LINE_WIDTH, determine the width
+ without any trailing whitespace. */
+
+static int
+get_line_width_without_trailing_whitespace (const char *line, int line_width)
+{
+ int result = line_width;
+ while (result > 0)
+ {
+ char ch = line[result - 1];
+ if (ch == ' ' || ch == '\t')
+ result--;
+ else
+ break;
+ }
+ gcc_assert (result >= 0);
+ gcc_assert (result <= line_width);
+ gcc_assert (result == 0 ||
+ (line[result - 1] != ' '
+ && line[result -1] != '\t'));
+ return result;
+}
+
+/* Implementation of class layout. */
+
+/* Constructor for class layout.
+
+ Filter the ranges from the rich_location to those that we can
+ sanely print, populating m_layout_ranges.
+ Determine the range of lines that we will print.
+ Determine m_x_offset, to ensure that the primary caret
+ will fit within the max_width provided by the diagnostic_context. */
+
+layout::layout (diagnostic_context * context,
+ const diagnostic_info *diagnostic)
+: m_context (context),
+ m_pp (context->printer),
+ m_diagnostic_kind (diagnostic->kind),
+ m_exploc (diagnostic->richloc->lazily_expand_location ()),
+ m_colorizer (context, diagnostic),
+ m_colorize_source_p (context->colorize_source_p),
+ m_layout_ranges (rich_location::MAX_RANGES),
+ m_first_line (m_exploc.line),
+ m_last_line (m_exploc.line),
+ m_x_offset (0)
+{
+ rich_location *richloc = diagnostic->richloc;
+ for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
+ {
+ /* This diagnostic printer can only cope with "sufficiently sane" ranges.
+ Ignore any ranges that are awkward to handle. */
+ location_range *loc_range = richloc->get_range (idx);
+
+ /* If any part of the range isn't in the same file as the primary
+ location of this diagnostic, ignore the range. */
+ if (loc_range->m_start.file != m_exploc.file)
+ continue;
+ if (loc_range->m_finish.file != m_exploc.file)
+ continue;
+ if (loc_range->m_show_caret_p)
+ if (loc_range->m_caret.file != m_exploc.file)
+ continue;
+
+ /* Passed all the tests; add the range to m_layout_ranges so that
+ it will be printed. */
+ layout_range ri (loc_range);
+ m_layout_ranges.safe_push (ri);
+
+ /* Update m_first_line/m_last_line if necessary. */
+ if (loc_range->m_start.line < m_first_line)
+ m_first_line = loc_range->m_start.line;
+ if (loc_range->m_finish.line > m_last_line)
+ m_last_line = loc_range->m_finish.line;
+ }
+
+ /* Adjust m_x_offset.
+ Center the primary caret to fit in max_width; all columns
+ will be adjusted accordingly. */
+ int max_width = m_context->caret_max_width;
int line_width;
- const char *line = location_get_source_line (xloc1.file, xloc1.line,
+ const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
&line_width);
- if (line == NULL || cmax > line_width)
- return;
+ if (line && m_exploc.column <= line_width)
+ {
+ int right_margin = CARET_LINE_MARGIN;
+ int column = m_exploc.column;
+ right_margin = MIN (line_width - column, right_margin);
+ right_margin = max_width - right_margin;
+ if (line_width >= max_width && column > right_margin)
+ m_x_offset = column - right_margin;
+ gcc_assert (m_x_offset >= 0);
+ }
+}
- /* Center the interesting part of the source line to fit in
- max_width, and adjust all columns accordingly. */
- int max_width = context->caret_max_width;
- int offset = (int) cmax;
- line = adjust_line (line, line_width, max_width, &offset);
- offset -= cmax;
- cmax += offset;
- xloc1.column += offset;
- if (xloc2.column)
- xloc2.column += offset;
-
- /* Print the source line. */
- pp_newline (context->printer);
- const char *saved_prefix = pp_get_prefix (context->printer);
- pp_set_prefix (context->printer, NULL);
- pp_space (context->printer);
- while (max_width > 0 && line_width > 0)
+/* Attempt to print line ROW of source code, potentially colorized at any
+ ranges.
+ Return true if the line was printed, populating *LBOUNDS_OUT.
+ Return false if the source line could not be read, leaving *LBOUNDS_OUT
+ untouched. */
+
+bool
+layout::print_source_line (int row, line_bounds *lbounds_out)
+{
+ int line_width;
+ const char *line = location_get_source_line (m_exploc.file, row,
+ &line_width);
+ if (!line)
+ return false;
+
+ line += m_x_offset;
+
+ m_colorizer.set_normal_text ();
+
+ /* We will stop printing the source line at any trailing
+ whitespace. */
+ line_width = get_line_width_without_trailing_whitespace (line,
+ line_width);
+
+ pp_space (m_pp);
+ int first_non_ws = INT_MAX;
+ int last_non_ws = 0;
+ int column;
+ for (column = 1 + m_x_offset; column <= line_width; column++)
{
+ /* Assuming colorization is enabled for the caret and underline
+ characters, we may also colorize the associated characters
+ within the source line.
+
+ For frontends that generate range information, we color the
+ associated characters in the source line the same as the
+ carets and underlines in the annotation line, to make it easier
+ for the reader to see the pertinent code.
+
+ For frontends that only generate carets, we don't colorize the
+ characters above them, since this would look strange (e.g.
+ colorizing just the first character in a token). */
+ if (m_colorize_source_p)
+ {
+ bool in_range_p;
+ point_state state;
+ in_range_p = get_state_at_point (row, column,
+ 0, INT_MAX,
+ &state);
+ if (in_range_p)
+ m_colorizer.set_range (state.range_idx);
+ else
+ m_colorizer.set_normal_text ();
+ }
char c = *line == '\t' ? ' ' : *line;
if (c == '\0')
c = ' ';
- pp_character (context->printer, c);
- max_width--;
- line_width--;
+ if (c != ' ')
+ {
+ last_non_ws = column;
+ if (first_non_ws == INT_MAX)
+ first_non_ws = column;
+ }
+ pp_character (m_pp, c);
line++;
}
- pp_newline (context->printer);
+ pp_newline (m_pp);
+
+ lbounds_out->m_first_non_ws = first_non_ws;
+ lbounds_out->m_last_non_ws = last_non_ws;
+ return true;
+}
+
+/* Print a line consisting of the caret/underlines for the given
+ source line. */
- /* Print the caret under the line. */
- const char *caret_cs, *caret_ce;
- caret_cs = colorize_start (pp_show_color (context->printer), "caret");
- caret_ce = colorize_stop (pp_show_color (context->printer));
- int cmin = xloc2.column
- ? MIN (xloc1.column, xloc2.column) : xloc1.column;
- int caret_min = cmin == xloc1.column ? caret1 : caret2;
- int caret_max = cmin == xloc1.column ? caret2 : caret1;
-
- /* cmin is >= 1, but we indent with an extra space at the start like
- we did above. */
+void
+layout::print_annotation_line (int row, const line_bounds lbounds)
+{
+ int x_bound = get_x_bound_for_row (row, m_exploc.column,
+ lbounds.m_last_non_ws);
+
+ pp_space (m_pp);
+ for (int column = 1 + m_x_offset; column < x_bound; column++)
+ {
+ bool in_range_p;
+ point_state state;
+ in_range_p = get_state_at_point (row, column,
+ lbounds.m_first_non_ws,
+ lbounds.m_last_non_ws,
+ &state);
+ if (in_range_p)
+ {
+ /* Within a range. Draw either the caret or an underline. */
+ m_colorizer.set_range (state.range_idx);
+ if (state.draw_caret_p)
+ /* Draw the caret. */
+ pp_character (m_pp, m_context->caret_chars[state.range_idx]);
+ else
+ pp_character (m_pp, '~');
+ }
+ else
+ {
+ /* Not in a range. */
+ m_colorizer.set_normal_text ();
+ pp_character (m_pp, ' ');
+ }
+ }
+ pp_newline (m_pp);
+}
+
+/* Return true if (ROW/COLUMN) is within a range of the layout.
+ If it returns true, OUT_STATE is written to, with the
+ range index, and whether we should draw the caret at
+ (ROW/COLUMN) (as opposed to an underline). */
+
+bool
+layout::get_state_at_point (/* Inputs. */
+ int row, int column,
+ int first_non_ws, int last_non_ws,
+ /* Outputs. */
+ point_state *out_state)
+{
+ layout_range *range;
int i;
- for (i = 0; i < cmin; i++)
- pp_space (context->printer);
- pp_printf (context->printer, "%s%c%s", caret_cs, caret_min, caret_ce);
+ FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
+ {
+ if (range->contains_point (row, column))
+ {
+ out_state->range_idx = i;
+
+ /* Are we at the range's caret? is it visible? */
+ out_state->draw_caret_p = false;
+ if (row == range->m_caret.m_line
+ && column == range->m_caret.m_column)
+ out_state->draw_caret_p = range->m_show_caret_p;
- if (xloc2.column)
+ /* Within a multiline range, don't display any underline
+ in any leading or trailing whitespace on a line.
+ We do display carets, however. */
+ if (!out_state->draw_caret_p)
+ if (column < first_non_ws || column > last_non_ws)
+ return false;
+
+ /* We are within a range. */
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Helper function for use by layout::print_line when printing the
+ annotation line under the source line.
+ Get the column beyond the rightmost one that could contain a caret or
+ range marker, given that we stop rendering at trailing whitespace.
+ ROW is the source line within the given file.
+ CARET_COLUMN is the column of range 0's caret.
+ LAST_NON_WS_COLUMN is the last column containing a non-whitespace
+ character of source (as determined when printing the source line). */
+
+int
+layout::get_x_bound_for_row (int row, int caret_column,
+ int last_non_ws_column)
+{
+ int result = caret_column + 1;
+
+ layout_range *range;
+ int i;
+ FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
{
- for (i++; i < cmax; i++)
- pp_space (context->printer);
- pp_printf (context->printer, "%s%c%s", caret_cs, caret_max, caret_ce);
+ if (row >= range->m_start.m_line)
+ {
+ if (range->m_finish.m_line == row)
+ {
+ /* On the final line within a range; ensure that
+ we render up to the end of the range. */
+ if (result <= range->m_finish.m_column)
+ result = range->m_finish.m_column + 1;
+ }
+ else if (row < range->m_finish.m_line)
+ {
+ /* Within a multiline range; ensure that we render up to the
+ last non-whitespace column. */
+ if (result <= last_non_ws_column)
+ result = last_non_ws_column + 1;
+ }
+ }
}
+
+ return result;
+}
+
+} /* End of anonymous namespace. */
+
+/* Print the physical source code corresponding to the location of
+ this diagnostic, with additional annotations. */
+
+void
+diagnostic_show_locus (diagnostic_context * context,
+ const diagnostic_info *diagnostic)
+{
+ if (!context->show_caret
+ || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
+ || diagnostic_location (diagnostic, 0) == context->last_location)
+ return;
+
+ context->last_location = diagnostic_location (diagnostic, 0);
+
+ pp_newline (context->printer);
+
+ const char *saved_prefix = pp_get_prefix (context->printer);
+ pp_set_prefix (context->printer, NULL);
+
+ {
+ layout layout (context, diagnostic);
+ int last_line = layout.get_last_line ();
+ for (int row = layout.get_first_line ();
+ row <= last_line;
+ row++)
+ {
+ /* Print the source line, followed by an annotation line
+ consisting of any caret/underlines. If the source line can't
+ be read, print nothing. */
+ line_bounds lbounds;
+ if (layout.print_source_line (row, &lbounds))
+ layout.print_annotation_line (row, lbounds);
+ }
+
+ /* The closing scope here leads to the dtor for layout and thus
+ colorizer being called here, which affects the precise
+ place where colorization is turned off in the unittest
+ for colorized output. */
+ }
+
pp_set_prefix (context->printer, saved_prefix);
- pp_needs_newline (context->printer) = true;
}
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++)
+ for (i = 0; i < rich_location::MAX_RANGES; i++)
context->caret_chars[i] = '^';
context->show_option_requested = false;
context->abort_on_error = false;
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;
}
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
#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]);
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);
}
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);
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;
}
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
+
+ va_start (ap, gmsgid);
+ 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, location, DK_NOTE);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE);
report_diagnostic (&diagnostic);
va_end (ap);
}
{
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);
}
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);
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
+
+ va_start (ap, gmsgid);
+ 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, location, DK_WARNING);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_WARNING);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
va_end (ap);
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);
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);
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,
+ 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, richloc,
permissive_error_kind (global_dc));
diagnostic.option_index = permissive_error_option (global_dc);
ret = report_diagnostic (&diagnostic);
{
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);
}
{
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);
}
{
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);
}
{
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);
}
{
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);
{
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);
{
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);
{
abort ();
}
+
+/* Display the given source_range instance, with MSG as a descriptive
+ comment. This issues a "note" diagnostic at the range.
+
+ This is declared within libcpp, but implemented here, since it
+ makes use of the diagnostic-printing machinery. */
+
+DEBUG_FUNCTION void
+source_range::debug (const char *msg) const
+{
+ rich_location richloc (m_start);
+ richloc.add_range (m_start, m_finish, false);
+ inform_at_rich_loc (&richloc, "%s", msg);
+}
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. */
/* Maximum width of the source line printed. */
int caret_max_width;
- /* Characters used for caret diagnostics. */
- char caret_chars[MAX_LOCATIONS_PER_MESSAGE];
+ /* Character used for caret diagnostics. */
+ char caret_chars[rich_location::MAX_RANGES];
/* True if we should print the command line option which controls
each diagnostic, if known. */
int lock;
bool inhibit_notes_p;
+
+ /* When printing source code, should the characters at carets and ranges
+ be colorized? (assuming colorization is on at all).
+ This should be true for frontends that generate range information
+ (so that the ranges of code are colorized),
+ and false for frontends that merely specify points within the
+ source code (to avoid e.g. colorizing just the first character in
+ a token, which would look strange). */
+ bool colorize_source_p;
};
static inline void
#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) \
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 *);
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. */
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_caret;
}
/* This is somehow the right-side margin of a caret line, that is, we
&& context->caret_max_width - CARET_LINE_MARGIN > abs (s1.column - s2.column);
}
-void
-diagnostic_print_caret_line (diagnostic_context * context,
- expanded_location xloc1,
- expanded_location xloc2,
- char caret1, 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 *);
+2015-11-06 David Malcolm <dmalcolm@redhat.com>
+
+ * cpp.c (cb_cpp_error): Convert parameter from location_t to
+ rich_location *. Eliminate the "column_override" parameter.
+ * error.c (gfc_warning): Update for change in signature of
+ diagnostic_set_info.
+ (gfc_format_decoder): Update handling of %C/%L for changes
+ to struct text_info.
+ (gfc_diagnostic_starter): Use richloc when determining whether to
+ print one locus or two. When handling a location that will
+ involve a call to diagnostic_show_locus, only attempt to print the
+ locus for the primary location, and don't call into
+ diagnostic_print_caret_line.
+ (gfc_warning_now_at): Update for change in signature of
+ diagnostic_set_info.
+ (gfc_warning_now): Likewise.
+ (gfc_error_now): Likewise.
+ (gfc_fatal_error): Likewise.
+ (gfc_error): Likewise.
+ (gfc_internal_error): Likewise.
+
2015-11-05 Cesar Philippidis <cesar@codesourcery.com>
* openmp.c (gfc_match_omp_clauses): Update support for the tile
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 *);
/* 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;
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);
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;
--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);
/* 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;
}
}
/* This function prints the locus (file:line:column), the diagnostic kind
- (Error, Warning) and (optionally) the caret line (a source line
- with '1' and/or '2' below it).
+ (Error, Warning) and (optionally) the relevant lines of code with
+ annotation lines with '1' and/or '2' below them.
- With -fdiagnostic-show-caret (the default) and for valid locations,
- it prints for one location:
+ With -fdiagnostic-show-caret (the default) it prints:
- [locus]:
+ [locus of primary range]:
some code
1
Error: Some error at (1)
- for two locations that fit in the same locus line:
+ With -fno-diagnostic-show-caret or if the primary range is not
+ valid, it prints:
- [locus]:
-
- some code and some more code
- 1 2
- Error: Some error at (1) and (2)
-
- and for two locations that do not fit in the same locus line:
-
- [locus]:
-
- some code
- 1
- [locus2]:
-
- some other code
- 2
- Error: Some error at (1) and (2)
-
- With -fno-diagnostic-show-caret or if one of the locations is not
- valid, it prints for one location (or for two locations that fit in
- the same locus line):
-
- [locus]: Error: Some error at (1) and (2)
-
- and for two locations that do not fit in the same locus line:
-
- [name]:[locus]: Error: (1)
- [name]:[locus2]: Error: Some error at (1) and (2)
+ [locus of primary range]: Error: Some error at (1) and (2)
*/
static void
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)
/* If the caret line was shown, the prefix does not contain the
locus. */
pp_set_prefix (context->printer, kind_prefix);
-
- if (one_locus || same_locus)
- return;
-
- locus_prefix = gfc_diagnostic_build_locus_prefix (context, s2);
- if (diagnostic_location (diagnostic, 1) <= BUILTINS_LOCATION)
- {
- /* No caret line for the second location. Override the previous
- prefix with [locus2]:[prefix]. */
- pp_set_prefix (context->printer,
- concat (locus_prefix, " ", kind_prefix, NULL));
- free (kind_prefix);
- free (locus_prefix);
- }
- else
- {
- /* We print the caret for the second location. */
- pp_verbatim (context->printer, locus_prefix);
- free (locus_prefix);
- /* Fortran uses an empty line between locus and caret line. */
- pp_newline (context->printer);
- s1.column = 0; /* Print only a caret line for s2. */
- diagnostic_print_caret_line (context, s2, s1,
- context->caret_chars[1], '\0');
- pp_newline (context->printer);
- /* If the caret line was shown, the prefix does not contain the
- locus. */
- pp_set_prefix (context->printer, kind_prefix);
- }
}
}
{
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);
{
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);
{
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);
}
{
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);
}
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;
--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)
{
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);
static struct line_maps *line_table;
+/* The rich_location class within libcpp requires a way to expand
+ source_location instances, and relies on the client code
+ providing a symbol named
+ linemap_client_expand_location_to_spelling_point
+ to do this.
+
+ This is the implementation for genmatch. */
+
+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,
#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);
}
#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);
}
#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);
}
#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);
}
return expand_location_1 (loc, /*expansion_point_p=*/false);
}
+/* The rich_location class within libcpp requires a way to expand
+ source_location instances, and relies on the client code
+ providing a symbol named
+ linemap_client_expand_location_to_spelling_point
+ to do this.
+
+ This is the implementation for libcommon.a (all host binaries),
+ which simply calls into expand_location_to_spelling_point. */
+
+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
#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, true);
+}
+
+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 ()
/* 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
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:
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);
}
+2015-11-06 David Malcolm <dmalcolm@redhat.com>
+
+ * gcc.dg/plugin/diagnostic-test-show-locus-bw.c: New file.
+ * gcc.dg/plugin/diagnostic-test-show-locus-color.c: New file.
+ * gcc.dg/plugin/diagnostic_plugin_test_show_locus.c: New file.
+ * gcc.dg/plugin/plugin.exp (plugin_test_list): Add the above.
+ * lib/gcc-dg.exp: Load multiline.exp.
+
2015-11-06 Ramana Radhakrishnan <ramana.radhakrishnan@arm.com>
* gcc.target/arm/combine-movs.c: Adjust for unified asm.
--- /dev/null
+/* { 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 test_simple (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = myvar.x;
+ ~~~~~^~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_simple_2 (void)
+{
+#if 0
+ x = first_function () + second_function (); /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+ x = first_function () + second_function ();
+ ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+
+void test_multiline (void)
+{
+#if 0
+ x = (first_function ()
+ + second_function ()); /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+ x = (first_function ()
+ ~~~~~~~~~~~~~~~~~
+ + second_function ());
+ ^ ~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_many_lines (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" } */
+ 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,
+ ~~~~~~~~~~~~~~~~~~~~~
+ incididunt, ut, labore, et,
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ dolore, magna, aliqua)
+ ~~~~~~~~~~~~~~~~~~~~~~
+ + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit,
+ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ amet, consectetur,
+ ~~~~~~~~~~~~~~~~~~
+ adipiscing, elit, sed,
+ ~~~~~~~~~~~~~~~~~~~~~~
+ eiusmod, tempor, incididunt,
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ut, labore, et, dolore,
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ magna, aliqua));
+ ~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_richloc_from_proper_range (void)
+{
+#if 0
+ float f = 98.6f; /* { dg-warning "test" } */
+/* { dg-begin-multiline-output "" }
+ float f = 98.6f;
+ ^~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_caret_within_proper_range (void)
+{
+#if 0
+ float f = foo * bar; /* { dg-warning "17: test" } */
+/* { dg-begin-multiline-output "" }
+ float f = foo * bar;
+ ~~~~^~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_very_wide_line (void)
+{
+#if 0
+ float f = foo * bar; /* { dg-warning "95: test" } */
+/* { dg-begin-multiline-output "" }
+ float f = foo * bar;
+ ~~~~^~~~~
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_multiple_carets (void)
+{
+#if 0
+ x = x + y /* { dg-warning "8: test" } */
+/* { dg-begin-multiline-output "" }
+ x = x + y
+ A B
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_caret_on_leading_whitespace (void)
+{
+#if 0
+ ASSOCIATE (y => x)
+ y = 5 /* { dg-warning "6: test" } */
+/* { dg-begin-multiline-output "" }
+ ASSOCIATE (y => x)
+ 2
+ y = 5
+ 1
+ { dg-end-multiline-output "" } */
+#endif
+}
--- /dev/null
+/* { 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 test_simple (void)
+{
+#if 0
+ myvar = myvar.x; /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+ myvar = \e[32m\e[Kmyvar\e[m\e[K\e[01;35m\e[K.\e[m\e[K\e[34m\e[Kx\e[m\e[K;
+ \e[32m\e[K~~~~~\e[m\e[K\e[01;35m\e[K^\e[m\e[K\e[34m\e[K~
+\e[m\e[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_simple_2 (void)
+{
+#if 0
+ x = first_function () + second_function (); /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+ x = \e[32m\e[Kfirst_function ()\e[m\e[K \e[01;35m\e[K+\e[m\e[K \e[34m\e[Ksecond_function ()\e[m\e[K;
+ \e[32m\e[K~~~~~~~~~~~~~~~~~\e[m\e[K \e[01;35m\e[K^\e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~
+\e[m\e[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+
+void test_multiline (void)
+{
+#if 0
+ x = (first_function ()
+ + second_function ()); /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+ x = (\e[32m\e[Kfirst_function ()
+ \e[m\e[K \e[32m\e[K~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[01;35m\e[K+\e[m\e[K \e[34m\e[Ksecond_function ()\e[m\e[K);
+ \e[01;35m\e[K^\e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~
+\e[m\e[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_many_lines (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" } */
+ amet, consectetur,
+ adipiscing, elit, sed,
+ eiusmod, tempor, incididunt,
+ ut, labore, et, dolore,
+ magna, aliqua));
+
+/* { dg-begin-multiline-output "" }
+ x = (\e[32m\e[Kfirst_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,
+ \e[m\e[K \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[32m\e[K consectetur, adipiscing, elit,
+ \e[m\e[K \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[32m\e[K sed, eiusmod, tempor,
+ \e[m\e[K \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[32m\e[K incididunt, ut, labore, et,
+ \e[m\e[K \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[32m\e[K dolore, magna, aliqua)
+ \e[m\e[K \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[01;35m\e[K+\e[m\e[K \e[34m\e[Ksecond_function_with_a_very_long_name (lorem, ipsum, dolor, sit,
+ \e[m\e[K \e[01;35m\e[K^\e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K amet, consectetur,
+ \e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K adipiscing, elit, sed,
+ \e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K eiusmod, tempor, incididunt,
+ \e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K ut, labore, et, dolore,
+ \e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K magna, aliqua)\e[m\e[K);
+ \e[34m\e[K~~~~~~~~~~~~~~
+\e[m\e[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_richloc_from_proper_range (void)
+{
+#if 0
+ float f = 98.6f; /* { dg-warning "test" } */
+/* { dg-begin-multiline-output "" }
+ float f = \e[01;35m\e[K98.6f\e[m\e[K;
+ \e[01;35m\e[K^~~~~
+\e[m\e[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_caret_within_proper_range (void)
+{
+#if 0
+ float f = foo * bar; /* { dg-warning "17: test" } */
+/* { dg-begin-multiline-output "" }
+ float f = \e[01;35m\e[Kfoo * bar\e[m\e[K;
+ \e[01;35m\e[K~~~~^~~~~
+\e[m\e[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_very_wide_line (void)
+{
+#if 0
+ float f = foo * bar; /* { dg-warning "95: test" } */
+/* { dg-begin-multiline-output "" }
+ float f = \e[01;35m\e[Kfoo * bar\e[m\e[K;
+ \e[01;35m\e[K~~~~^~~~~
+\e[m\e[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_multiple_carets (void)
+{
+#if 0
+ x = x + y /* { dg-warning "8: test" } */
+/* { dg-begin-multiline-output "" }
+ x = \e[01;35m\e[Kx\e[m\e[K + \e[32m\e[Ky\e[m\e[K
+ \e[01;35m\e[KA\e[m\e[K \e[32m\e[KB
+\e[m\e[K
+ { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_caret_on_leading_whitespace (void)
+{
+#if 0
+ ASSOCIATE (y => x)
+ y = 5 /* { dg-warning "6: test" } */
+/* { dg-begin-multiline-output "" }
+ ASSOCIATE (y =>\e[32m\e[K \e[m\e[Kx)
+ \e[32m\e[K2
+\e[m\e[K \e[01;35m\e[K \e[m\e[Ky = 5
+ \e[01;35m\e[K1
+\e[m\e[K
+ { dg-end-multiline-output "" } */
+#endif
+}
--- /dev/null
+/* { 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 two test files used with this code:
+
+ diagnostic-test-show-locus-ascii-bw.c
+ ..........................-ascii-color.c
+
+ to exercise uncolored vs colored output by supplying plugin arguments
+ to hack in the desired behavior:
+
+ -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 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 "print-tree.h"
+
+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;
+
+ /* Hardcode the "terminal width", to verify the behavior of
+ very wide lines. */
+ global_dc->caret_max_width = 70;
+
+ if (0 == strcmp (fnname, "test_simple"))
+ {
+ const int line = fnstart_line + 2;
+ rich_location richloc (get_loc (line, 15));
+ richloc.add_range (get_loc (line, 10), get_loc (line, 14), false);
+ richloc.add_range (get_loc (line, 16), get_loc (line, 16), false);
+ warning_at_rich_loc (&richloc, 0, "test");
+ }
+
+ if (0 == strcmp (fnname, "test_simple_2"))
+ {
+ const int line = fnstart_line + 2;
+ rich_location richloc (get_loc (line, 24));
+ richloc.add_range (get_loc (line, 6),
+ get_loc (line, 22), false);
+ richloc.add_range (get_loc (line, 26),
+ get_loc (line, 43), false);
+ warning_at_rich_loc (&richloc, 0, "test");
+ }
+
+ if (0 == strcmp (fnname, "test_multiline"))
+ {
+ const int line = fnstart_line + 2;
+ rich_location richloc (get_loc (line + 1, 7));
+ richloc.add_range (get_loc (line, 7),
+ get_loc (line, 23), false);
+ richloc.add_range (get_loc (line + 1, 9),
+ get_loc (line + 1, 26), false);
+ warning_at_rich_loc (&richloc, 0, "test");
+ }
+
+ if (0 == strcmp (fnname, "test_many_lines"))
+ {
+ const int line = fnstart_line + 2;
+ rich_location richloc (get_loc (line + 5, 7));
+ richloc.add_range (get_loc (line, 7),
+ get_loc (line + 4, 65), false);
+ richloc.add_range (get_loc (line + 5, 9),
+ get_loc (line + 10, 61), false);
+ warning_at_rich_loc (&richloc, 0, "test");
+ }
+
+ /* Example of a rich_location constructed directly from a
+ source_range where the range is larger than one character. */
+ if (0 == strcmp (fnname, "test_richloc_from_proper_range"))
+ {
+ 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);
+ rich_location richloc (src_range);
+ warning_at_rich_loc (&richloc, 0, "test");
+ }
+
+ /* Example of a single-range location where the range starts
+ before the caret. */
+ if (0 == strcmp (fnname, "test_caret_within_proper_range"))
+ {
+ const int line = fnstart_line + 2;
+ location_t caret = get_loc (line, 16);
+ source_range src_range;
+ src_range.m_start = get_loc (line, 12);
+ src_range.m_finish = get_loc (line, 20);
+ rich_location richloc (caret);
+ richloc.set_range (0, src_range, true, false);
+ warning_at_rich_loc (&richloc, 0, "test");
+ }
+
+ /* Example of a very wide line, where the information of interest
+ is beyond the width of the terminal (hardcoded above). */
+ if (0 == strcmp (fnname, "test_very_wide_line"))
+ {
+ const int line = fnstart_line + 2;
+ location_t caret = get_loc (line, 94);
+ source_range src_range;
+ src_range.m_start = get_loc (line, 90);
+ src_range.m_finish = get_loc (line, 98);
+ rich_location richloc (caret);
+ richloc.set_range (0, src_range, true, false);
+ warning_at_rich_loc (&richloc, 0, "test");
+ }
+
+ /* Example of multiple carets. */
+ if (0 == strcmp (fnname, "test_multiple_carets"))
+ {
+ const int line = fnstart_line + 2;
+ location_t caret_a = get_loc (line, 7);
+ location_t caret_b = get_loc (line, 11);
+ rich_location richloc (caret_a);
+ richloc.add_range (caret_b, caret_b, true);
+ global_dc->caret_chars[0] = 'A';
+ global_dc->caret_chars[1] = 'B';
+ warning_at_rich_loc (&richloc, 0, "test");
+ global_dc->caret_chars[0] = '^';
+ global_dc->caret_chars[1] = '^';
+ }
+
+ /* Example of two carets where both carets appear to have an off-by-one
+ error appearing one column early.
+ Seen with gfortran.dg/associate_5.f03.
+ In an earlier version of the printer, the printing of caret 0 aka
+ "1" was suppressed due to it appearing within the leading whitespace
+ before the text in its line. Ensure that we at least faithfully
+ print both carets, at the given (erroneous) locations. */
+ if (0 == strcmp (fnname, "test_caret_on_leading_whitespace"))
+ {
+ const int line = fnstart_line + 3;
+ location_t caret_a = get_loc (line, 5);
+ location_t caret_b = get_loc (line - 1, 19);
+ rich_location richloc (caret_a);
+ richloc.add_range (caret_b, caret_b, true);
+ global_dc->caret_chars[0] = '1';
+ global_dc->caret_chars[1] = '2';
+ warning_at_rich_loc (&richloc, 0, "test");
+ global_dc->caret_chars[0] = '^';
+ global_dc->caret_chars[1] = '^';
+ }
+}
+
+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 now, tell the dc to expect ranges and thus to colorize the source
+ lines, not just the carets/underlines. This will be redundant
+ once the C frontend generates ranges. */
+ global_dc->colorize_source_p = true;
+
+ for (int i = 0; i < argc; i++)
+ {
+ 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;
+}
{ 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-bw.c \
+ diagnostic-test-show-locus-color.c } \
]
foreach plugin_test $plugin_test_list {
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
}
if (set_locus)
- text->set_location (0, DECL_SOURCE_LOCATION (t));
+ text->set_location (0, DECL_SOURCE_LOCATION (t), true);
if (DECL_P (t))
{
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;
2015-11-06 David Malcolm <dmalcolm@redhat.com>
+ * errors.c (cpp_diagnostic): Update for change in signature
+ of "error" callback.
+ (cpp_diagnostic_with_line): Likewise, calling override_column
+ on the rich_location.
+ * include/cpplib.h (struct cpp_callbacks): Within "error"
+ callback, convert param from source_location to rich_location *,
+ and drop column_override param.
+ * include/line-map.h (struct source_range): New struct.
+ (struct location_range): New struct.
+ (class rich_location): New class.
+ (linemap_client_expand_location_to_spelling_point): New declaration.
+ * line-map.c (rich_location::rich_location): New ctors.
+ (rich_location::lazily_expand_location): New method.
+ (rich_location::override_column): New method.
+ (rich_location::add_range): New methods.
+ (rich_location::set_range): New method.
+
+2015-11-06 David Malcolm <dmalcolm@redhat.com>
+
* include/line-map.h (struct linemap_stats): Add fields
"adhoc_table_size" and "adhoc_table_entries_used".
* line-map.c (linemap_get_statistics): Populate above fields.
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;
}
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;
}
/* 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". */
libcpp/location-example.txt. */
typedef unsigned int source_location;
+/* A range of source locations.
+
+ Ranges are closed:
+ m_start is the first location within the range,
+ m_finish is the last location within 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;
+
+ /* Display this source_range instance, with MSG as a descriptive
+ comment. This issues a "note" diagnostic at the range, using
+ gcc's diagnostic machinery.
+
+ This is declared here, but is implemented within gcc/diagnostic.c,
+ since it makes use of gcc's diagnostic-printing machinery. This
+ is a slight layering violation, but this is sufficiently useful
+ for debugging that it's worth it.
+
+ This declaration would have a DEBUG_FUNCTION annotation, but that
+ is implemented in gcc/system.h and thus is not available here in
+ libcpp. */
+ 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);
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;
+ expanded_location m_caret;
+};
+
+/* A "rich" source code location, for use when printing diagnostics.
+ A rich_location has one or more ranges, each optionally with
+ a caret. Typically the zeroth range has a caret; other ranges
+ sometimes have carets.
+
+ The "primary" location of a rich_location is the caret of range 0,
+ used for determining the line/column when printing diagnostic
+ text, such as:
+
+ some-file.c:3:1: error: ...etc...
+
+ Additional ranges may be added to help the user identify other
+ pertinent clauses in a diagnostic.
+
+ rich_location instances are intended to be allocated on the stack
+ when generating diagnostics, and to be short-lived.
+
+ Examples of rich locations
+ --------------------------
+
+ Example A
+ *********
+ int i = "foo";
+ ^
+ This "rich" location is simply a single range (range 0), with
+ caret = start = finish at the given point.
+
+ Example B
+ *********
+ a = (foo && bar)
+ ~~~~~^~~~~~~
+ This rich location has a single range (range 0), with the caret
+ at the first "&", and the start/finish at the parentheses.
+ Compare with example C below.
+
+ Example C
+ *********
+ a = (foo && bar)
+ ~~~ ^~ ~~~
+ This rich location has three ranges:
+ - Range 0 has its caret and start location at the first "&" and
+ end at the second "&.
+ - Range 1 has its start and finish at the "f" and "o" of "foo";
+ the caret is not flagged for display, but is perhaps at the "f"
+ of "foo".
+ - Similarly, range 2 has its start and finish at the "b" and "r" of
+ "bar"; the caret is not flagged for display, but is perhaps at the
+ "b" of "bar".
+ Compare with example B above.
+
+ Example D (Fortran frontend)
+ ****************************
+ x = x + y
+ 1 2
+ This rich location has range 0 at "1", and range 1 at "2".
+ Both are flagged for caret display. Both ranges have start/finish
+ equal to their caret point. The frontend overrides the diagnostic
+ context's default caret character for these ranges.
+
+ Example E
+ *********
+ printf ("arg0: %i arg1: %s arg2: %i",
+ ^~
+ 100, 101, 102);
+ ~~~
+ This rich location has two ranges:
+ - range 0 is at the "%s" with start = caret = "%" and finish at
+ the "s".
+ - range 1 has start/finish covering the "101" and is not flagged for
+ caret printing; it is perhaps at the start of "101". */
+
+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);
+
+ /* 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);
+
+ void
+ add_range (source_range src_range, bool show_caret_p);
+
+ void
+ add_range (location_range *src_range);
+
+ void
+ set_range (unsigned int idx, source_range src_range,
+ bool show_caret_p, bool overwrite_loc_p);
+
+ 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 ();
+
+ void
+ override_column (int column);
+
+public:
+ static const int MAX_RANGES = 3;
+
+protected:
+ 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;
+};
+
/* 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. */
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 */
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);
+ m_ranges[0].m_caret = lazily_expand_location ();
+}
+
+/* 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);
+}
+
+/* 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. */
+
+void
+rich_location::add_range (source_location start, source_location finish,
+ 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_caret = range->m_start;
+ range->m_show_caret_p = show_caret_p;
+}
+
+/* Add the given range. */
+
+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, show_caret_p);
+}
+
+void
+rich_location::add_range (location_range *src_range)
+{
+ linemap_assert (m_num_ranges < MAX_RANGES);
+
+ 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, bool overwrite_loc_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;
+ if (overwrite_loc_p)
+ locrange->m_caret = locrange->m_start;
+
+ /* Are we adding a range onto the end? */
+ if (idx == m_num_ranges)
+ m_num_ranges = idx + 1;
+
+ if (idx == 0 && overwrite_loc_p)
+ {
+ m_loc = src_range.m_start;
+ /* Mark any cached value here as dirty. */
+ m_have_expanded_location = false;
+ }
+}