@@ -1304,6 +1304,7 @@ OBJS = \
langhooks.o \
lcm.o \
lists.o \
+ location-hacks.o \
loop-doloop.o \
loop-init.o \
loop-invariant.o \
@@ -1354,6 +1355,7 @@ OBJS = \
reload1.o \
reorg.o \
resource.o \
+ rich-location.o \
rtl-chkp.o \
rtl-error.o \
rtl.o \
@@ -1495,7 +1497,7 @@ OBJS = \
# Objects in libcommon.a, potentially used by all host binaries and with
# no target dependencies.
OBJS-libcommon = diagnostic.o diagnostic-color.o pretty-print.o intl.o \
- vec.o input.o version.o
+ vec.o input.o version.o box-drawing.o
# Objects in libcommon-target.a, used by drivers and by the core
# compiler and containing target-dependent code.
new file mode 100644
@@ -0,0 +1,99 @@
+/* Box-drawing, either using Unicode box-drawing chars, or as pure ASCII
+ Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "box-drawing.h"
+
+/* Global singleton instance. */
+box_drawing g_line_art;
+
+/* Strings for drawing the caret and ranges within diagnostics.
+ There are either UTF-8-encoded Unicode box-drawing characters,
+ or pure ASCII. */
+
+/* Initializer for box_drawing.
+ This isn't a constructor since the singleton instance is
+ statically-allocated. */
+void
+box_drawing::init (bool have_utf8)
+{
+ if (have_utf8)
+ {
+ /* For the caret, use:
+ U+25B2 BLACK UP-POINTING TRIANGLE
+ which in UTF-8 is: 0xE2 0x96 0xB2. */
+ caret = "\xE2\x96\xB2";
+
+ /* Underlining text ranges. */
+
+ /* The start of an underline: the south-western corner of a box:
+ U+2514 BOX DRAWINGS LIGHT UP AND RIGHT
+ in UTF-8: 0xE2 0x94 0x94. */
+ underline_start = "\xE2\x94\x94";
+
+ /* Within an underline:
+ U+2500 BOX DRAWINGS LIGHT HORIZONTAL
+ which in UTF-8 is: 0xE2 0x94 0x80. */
+ underline_hbar = "\xE2\x94\x80";
+
+ /* The end of an underline: the south-eastern corner of a box:
+ U+2518 BOX DRAWINGS LIGHT UP AND LEFT
+ UTF-8: 0xE2 0x94 0x98. */
+ underline_end = "\xE2\x94\x98";
+
+ /* Vertical margins (when showing captions for ranges). */
+
+ /* The top of a rmargin: the north-eastern corner of a box:
+ U+2510 BOX DRAWINGS LIGHT DOWN AND LEFT
+ UTF-8: 0xE2 0x94 0x90. */
+ rmargin_start = "\xE2\x94\x90";
+
+ /* For vertical lines, use:
+ U+2502 BOX DRAWINGS LIGHT VERTICAL
+ which in UTF-8 is: 0xE2 0x94 0x82. */
+ rmargin_vbar = "\xE2\x94\x82";
+
+ /* For the row within the rmargin containing the caption:
+ U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ UTF-8: 0xE2 0x94 0x9C. */
+ rmargin_caption_row = "\xE2\x94\x9C";
+
+ /* Unless its on the very last line, in which case:
+ U+2534 BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ UTF-8: 0xE2 0x94 0xB4. */
+ rmargin_caption_row_at_end = "\xE2\x94\xB4";
+
+ /* The end of a rmargin: the south-eastern corner of a box:
+ U+2518 BOX DRAWINGS LIGHT UP AND LEFT
+ UTF-8: 0xE2 0x94 0x98. */
+ rmargin_end = "\xE2\x94\x98";
+ }
+ else
+ {
+ caret = "^";
+ underline_start = underline_hbar = underline_end = "~";
+
+ rmargin_start = "|";
+ rmargin_vbar = "|";
+ rmargin_caption_row = rmargin_caption_row_at_end = "+";
+ rmargin_end = "|";
+ }
+}
new file mode 100644
@@ -0,0 +1,43 @@
+/* Box-drawing, either using Unicode box-drawing chars, or as pure ASCII
+ Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+/* Strings for drawing the caret and ranges within diagnostics.
+ There are either UTF-8-encoded Unicode box-drawing characters,
+ or pure ASCII. */
+
+class box_drawing
+{
+ public:
+ void init (bool have_utf8);
+
+ const char *caret;
+ const char *underline_start;
+ const char *underline_hbar;
+ const char *underline_end;
+
+ /* Right-hand-side margins. */
+ const char *rmargin_start;
+ const char *rmargin_vbar;
+ const char *rmargin_caption_row;
+ const char *rmargin_caption_row_at_end;
+ const char *rmargin_end;
+};
+
+/* Singleton instance. */
+extern box_drawing g_line_art;
@@ -4036,6 +4036,7 @@ c_register_builtin_type (tree type, const char* name)
void
binary_op_error (location_t location, enum tree_code code,
+ tree orig_op0, tree orig_op1,
tree type0, tree type1)
{
const char *opname;
@@ -4087,7 +4088,10 @@ binary_op_error (location_t location, enum tree_code code,
default:
gcc_unreachable ();
}
- error_at (location,
+ rich_location richloc (location);
+ richloc.add_expr (orig_op0);
+ richloc.add_expr (orig_op1);
+ error_at_rich_loc (&richloc,
"invalid operands to binary %s (have %qT and %qT)", opname,
type0, type1);
}
@@ -10016,8 +10020,9 @@ c_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
}
if (done_lexing)
location = input_location;
+ rich_location richloc (location);
diagnostic_set_info_translated (&diagnostic, msg, ap,
- location, dlevel);
+ &richloc, dlevel);
if (column_override)
diagnostic_override_column (&diagnostic, column_override);
diagnostic_override_option_index (&diagnostic,
@@ -11528,7 +11533,12 @@ warn_for_div_by_zero (location_t loc, tree divisor)
generating a NaN. */
if (c_inhibit_evaluation_warnings == 0
&& (integer_zerop (divisor) || fixed_zerop (divisor)))
- warning_at (loc, OPT_Wdiv_by_zero, "division by zero");
+ {
+ rich_location richloc (loc);
+ richloc.add_expr (divisor);
+ warning_at_rich_loc (&richloc,
+ OPT_Wdiv_by_zero, "division by zero");
+ }
}
/* Subroutine of build_binary_op. Give warnings for comparisons
@@ -799,7 +799,8 @@ extern tree c_sizeof_or_alignof_type (location_t, tree, bool, bool, int);
extern tree c_alignof_expr (location_t, tree);
/* Print an error message for invalid operands to arith operation CODE.
NOP_EXPR is used as a special case (see truthvalue_conversion). */
-extern void binary_op_error (location_t, enum tree_code, tree, tree);
+extern void binary_op_error (location_t, enum tree_code, tree, tree,
+ tree, tree);
extern tree fix_string_type (tree);
extern void constant_expression_warning (tree);
extern void constant_expression_error (tree);
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see
#include "c-format.h"
#include "alloc-pool.h"
#include "c-target.h"
+#include "rich-location.h"
/* Handle attributes associated with format checking. */
@@ -955,7 +956,7 @@ static void check_format_info (function_format_info *, tree);
static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT);
static void check_format_info_main (format_check_results *,
function_format_info *,
- const char *, int, tree,
+ tree, const char *, int, tree,
unsigned HOST_WIDE_INT, alloc_pool);
static void init_dollar_format_checking (int, tree);
@@ -967,8 +968,9 @@ static void finish_dollar_format_checking (format_check_results *, int);
static const format_flag_spec *get_flag_spec (const format_flag_spec *,
int, const char *);
-static void check_format_types (location_t, format_wanted_type *);
-static void format_type_warning (location_t, format_wanted_type *, tree, tree);
+static void check_format_types (rich_location *, format_wanted_type *);
+static void format_type_warning (rich_location *, format_wanted_type *, tree,
+ tree);
/* Decode a format type from a string, returning the type, or
format_type_error if not valid, in which case the caller should print an
@@ -1619,13 +1621,15 @@ check_format_arg (void *ctx, tree format_tree,
res->number_other++;
fwt_pool = create_alloc_pool ("format_wanted_type pool",
sizeof (format_wanted_type), 10);
- check_format_info_main (res, info, format_chars, format_length,
+ check_format_info_main (res, info, format_tree, format_chars, format_length,
params, arg_num, fwt_pool);
free_alloc_pool (fwt_pool);
}
-/* Do the main part of checking a call to a format function. FORMAT_CHARS
+/* Do the main part of checking a call to a format function.
+ FORMAT_STRING_CST is the STRING_CST format string.
+ FORMAT_CHARS
is the NUL-terminated format string (which at this point may contain
internal NUL characters); FORMAT_LENGTH is its length (excluding the
terminating NUL character). ARG_NUM is one less than the number of
@@ -1634,7 +1638,9 @@ check_format_arg (void *ctx, tree format_tree,
static void
check_format_info_main (format_check_results *res,
- function_format_info *info, const char *format_chars,
+ function_format_info *info,
+ tree format_string_cst,
+ const char *format_chars,
int format_length, tree params,
unsigned HOST_WIDE_INT arg_num, alloc_pool fwt_pool)
{
@@ -1652,6 +1658,8 @@ check_format_info_main (format_check_results *res,
init_dollar_format_checking (info->first_arg_num, first_fillin_param);
+ const char *start_of_format_string = format_chars;
+
while (*format_chars != 0)
{
int i;
@@ -1682,8 +1690,14 @@ check_format_info_main (format_check_results *res,
continue;
if (*format_chars == 0)
{
- warning_at (format_string_loc, OPT_Wformat_,
- "spurious trailing %<%%%> in format");
+ ptrdiff_t offset = (format_chars - 1) - start_of_format_string;
+ rich_location richloc (format_string_cst,
+ offset,
+ offset,
+ offset,
+ format_string_loc);
+ warning_at_rich_loc (&richloc, OPT_Wformat_,
+ "spurious trailing %<%%%> in format");
continue;
}
if (*format_chars == '%')
@@ -1691,6 +1705,7 @@ check_format_info_main (format_check_results *res,
++format_chars;
continue;
}
+ const char *start_of_this_format = format_chars;
flag_chars[0] = 0;
if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0)
@@ -2323,7 +2338,16 @@ check_format_info_main (format_check_results *res,
}
if (first_wanted_type != 0)
- check_format_types (format_string_loc, first_wanted_type);
+ {
+ ptrdiff_t offset_to_format_start = (start_of_this_format - 1) - orig_format_chars;
+ ptrdiff_t offset_to_format_end = (format_chars - 1) - orig_format_chars;
+ rich_location richloc (format_string_cst,
+ offset_to_format_start,
+ offset_to_format_start,
+ offset_to_format_end,
+ format_string_loc);
+ check_format_types (&richloc, first_wanted_type);
+ }
}
if (format_chars - orig_format_chars != format_length)
@@ -2344,7 +2368,7 @@ check_format_info_main (format_check_results *res,
including width and precision arguments). LOC is the location of
the format string. */
static void
-check_format_types (location_t loc, format_wanted_type *types)
+check_format_types (rich_location *richloc, format_wanted_type *types)
{
for (; types != 0; types = types->next)
{
@@ -2371,7 +2395,7 @@ check_format_types (location_t loc, format_wanted_type *types)
cur_param = types->param;
if (!cur_param)
{
- format_type_warning (loc, types, wanted_type, NULL);
+ format_type_warning (richloc, types, wanted_type, NULL);
continue;
}
@@ -2445,7 +2469,7 @@ check_format_types (location_t loc, format_wanted_type *types)
}
else
{
- format_type_warning (loc, types, wanted_type, orig_cur_type);
+ format_type_warning (richloc, types, wanted_type, orig_cur_type);
break;
}
}
@@ -2513,7 +2537,7 @@ check_format_types (location_t loc, format_wanted_type *types)
&& TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type))
continue;
/* Now we have a type mismatch. */
- format_type_warning (loc, types, wanted_type, orig_cur_type);
+ format_type_warning (richloc, types, wanted_type, orig_cur_type);
}
}
@@ -2526,7 +2550,7 @@ check_format_types (location_t loc, format_wanted_type *types)
are taken from TYPE. ARG_TYPE is the type of the actual argument,
or NULL if it is missing. */
static void
-format_type_warning (location_t loc, format_wanted_type *type,
+format_type_warning (rich_location *richloc, format_wanted_type *type,
tree wanted_type, tree arg_type)
{
int kind = type->kind;
@@ -2571,36 +2595,36 @@ format_type_warning (location_t loc, format_wanted_type *type,
if (wanted_type_name)
{
if (arg_type)
- warning_at (loc, OPT_Wformat_,
- "%s %<%s%.*s%> expects argument of type %<%s%s%>, "
- "but argument %d has type %qT",
- gettext (kind_descriptions[kind]),
- (kind == CF_KIND_FORMAT ? "%" : ""),
- format_length, format_start,
- wanted_type_name, p, arg_num, arg_type);
+ warning_at_rich_loc (richloc, OPT_Wformat_,
+ "%s %<%s%.*s%> expects argument of type %<%s%s%>, "
+ "but argument %d has type %qT",
+ gettext (kind_descriptions[kind]),
+ (kind == CF_KIND_FORMAT ? "%" : ""),
+ format_length, format_start,
+ wanted_type_name, p, arg_num, arg_type);
else
- warning_at (loc, OPT_Wformat_,
- "%s %<%s%.*s%> expects a matching %<%s%s%> argument",
- gettext (kind_descriptions[kind]),
- (kind == CF_KIND_FORMAT ? "%" : ""),
- format_length, format_start, wanted_type_name, p);
+ warning_at_rich_loc (richloc, OPT_Wformat_,
+ "%s %<%s%.*s%> expects a matching %<%s%s%> argument",
+ gettext (kind_descriptions[kind]),
+ (kind == CF_KIND_FORMAT ? "%" : ""),
+ format_length, format_start, wanted_type_name, p);
}
else
{
if (arg_type)
- warning_at (loc, OPT_Wformat_,
- "%s %<%s%.*s%> expects argument of type %<%T%s%>, "
- "but argument %d has type %qT",
- gettext (kind_descriptions[kind]),
- (kind == CF_KIND_FORMAT ? "%" : ""),
- format_length, format_start,
- wanted_type, p, arg_num, arg_type);
+ warning_at_rich_loc (richloc, OPT_Wformat_,
+ "%s %<%s%.*s%> expects argument of type %<%T%s%>, "
+ "but argument %d has type %qT",
+ gettext (kind_descriptions[kind]),
+ (kind == CF_KIND_FORMAT ? "%" : ""),
+ format_length, format_start,
+ wanted_type, p, arg_num, arg_type);
else
- warning_at (loc, OPT_Wformat_,
- "%s %<%s%.*s%> expects a matching %<%T%s%> argument",
- gettext (kind_descriptions[kind]),
- (kind == CF_KIND_FORMAT ? "%" : ""),
- format_length, format_start, wanted_type, p);
+ warning_at_rich_loc (richloc, OPT_Wformat_,
+ "%s %<%s%.*s%> expects a matching %<%T%s%> argument",
+ gettext (kind_descriptions[kind]),
+ (kind == CF_KIND_FORMAT ? "%" : ""),
+ format_length, format_start, wanted_type, p);
}
}
@@ -5293,9 +5293,10 @@ warn_defaults_to (location_t location, int opt, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
flag_isoc99 ? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = opt;
report_diagnostic (&diagnostic);
@@ -49,13 +49,14 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool warned = false;
+ rich_location richloc (location);
va_start (ap, gmsgid);
/* If desired, issue the C99/C11 compat warning, which is more specific
than -pedantic. */
if (warn_c99_c11_compat > 0)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
(pedantic && !flag_isoc11)
? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = OPT_Wc99_c11_compat;
@@ -67,7 +68,7 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...)
/* For -pedantic outside C11, issue a pedwarn. */
else if (pedantic && !flag_isoc11)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
diagnostic.option_index = opt;
warned = report_diagnostic (&diagnostic);
}
@@ -87,6 +88,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, gmsgid);
/* Warnings such as -Wvla are the most specific ones. */
@@ -97,7 +99,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
goto out;
else if (opt_var > 0)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
(pedantic && !flag_isoc99)
? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = opt;
@@ -109,7 +111,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
specific than -pedantic. */
if (warn_c90_c99_compat > 0)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
(pedantic && !flag_isoc99)
? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = OPT_Wc90_c99_compat;
@@ -121,7 +123,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
/* For -pedantic outside C99, issue a pedwarn. */
else if (pedantic && !flag_isoc99)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
diagnostic.option_index = opt;
report_diagnostic (&diagnostic);
}
@@ -2869,7 +2869,11 @@ build_function_call (location_t loc, tree function, tree params)
static void inform_declaration (tree decl)
{
if (decl && (TREE_CODE (decl) != FUNCTION_DECL || !DECL_BUILT_IN (decl)))
- inform (DECL_SOURCE_LOCATION (decl), "declared here");
+ {
+ rich_location richloc (DECL_SOURCE_LOCATION (decl));
+ richloc.add_expr (decl);
+ inform_at_rich_loc (&richloc, "declared here");
+ }
}
/* Build a function call to function FUNCTION with parameters PARAMS.
@@ -2936,14 +2940,22 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
function);
else if (DECL_P (function))
{
- error_at (loc,
- "called object %qD is not a function or function pointer",
- function);
+ rich_location richloc (loc);
+ richloc.add_expr (function);
+ error_at_rich_loc
+ (&richloc,
+ "called object %qD is not a function or function pointer",
+ function);
inform_declaration (function);
}
else
- error_at (loc,
- "called object is not a function or function pointer");
+ {
+ rich_location richloc (loc);
+ richloc.add_expr (function);
+ error_at_rich_loc
+ (&richloc,
+ "called object is not a function or function pointer");
+ }
return error_mark_node;
}
@@ -10987,7 +10999,7 @@ build_binary_op (location_t location, enum tree_code code,
&& (!tree_int_cst_equal (TYPE_SIZE (type0), TYPE_SIZE (type1))
|| !vector_types_compatible_elements_p (type0, type1)))
{
- binary_op_error (location, code, type0, type1);
+ binary_op_error (location, code, orig_op0, orig_op1, type0, type1);
return error_mark_node;
}
@@ -11226,7 +11238,8 @@ build_binary_op (location_t location, enum tree_code code,
if (!result_type)
{
- binary_op_error (location, code, TREE_TYPE (op0), TREE_TYPE (op1));
+ binary_op_error (location, code, orig_op0, orig_op1,
+ TREE_TYPE (op0), TREE_TYPE (op1));
return error_mark_node;
}
@@ -59,6 +59,7 @@ along with GCC; see the file COPYING3. If not see
#include "cgraph.h"
#include "wide-int.h"
#include "internal-fn.h"
+#include "rich-location.h"
/* The various kinds of conversion. */
@@ -4023,13 +4024,17 @@ print_error_for_call_failure (tree fn, vec<tree, va_gc> *args,
{
tree name = DECL_NAME (OVL_CURRENT (fn));
location_t loc = location_of (name);
+ rich_location richloc (loc);
+ richloc.add_expr (fn);
if (!any_strictly_viable (candidates))
- error_at (loc, "no matching function for call to %<%D(%A)%>",
- name, build_tree_list_vec (args));
+ error_at_rich_loc (&richloc,
+ "no matching function for call to %<%D(%A)%>",
+ name, build_tree_list_vec (args));
else
- error_at (loc, "call of overloaded %<%D(%A)%> is ambiguous",
- name, build_tree_list_vec (args));
+ error_at_rich_loc (&richloc,
+ "call of overloaded %<%D(%A)%> is ambiguous",
+ name, build_tree_list_vec (args));
if (candidates)
print_z_candidates (loc, candidates);
}
@@ -4441,8 +4446,16 @@ op_error (location_t loc, enum tree_code code, enum tree_code code2,
default:
if (arg2)
if (flag_diagnostics_show_caret)
- error_at (loc, op_error_string (G_("%<operator%s%>"), 2, match),
- opname, TREE_TYPE (arg1), TREE_TYPE (arg2));
+ {
+ rich_location richloc (loc);
+ richloc.add_expr_with_caption (arg1, global_dc->printer,
+ G_("type %qT"), TREE_TYPE (arg1));
+ richloc.add_expr_with_caption (arg2, global_dc->printer,
+ G_("type %qT"), TREE_TYPE (arg2));
+ error_at_rich_loc (&richloc,
+ op_error_string (G_("%<operator%s%>"), 2, match),
+ opname, TREE_TYPE (arg1), TREE_TYPE (arg2));
+ }
else
error_at (loc, op_error_string (G_("%<operator%s%> in %<%E %s %E%>"),
2, match),
@@ -3104,7 +3104,7 @@ static void
cp_diagnostic_starter (diagnostic_context *context,
diagnostic_info *diagnostic)
{
- diagnostic_report_current_module (context, diagnostic->location);
+ diagnostic_report_current_module (context, diagnostic->richloc->get_loc ());
cp_print_error_function (context, diagnostic);
maybe_print_instantiation_context (context);
maybe_print_constexpr_context (context);
@@ -3125,7 +3125,7 @@ cp_print_error_function (diagnostic_context *context,
if (diagnostic_last_function_changed (context, diagnostic))
{
const char *old_prefix = context->printer->prefix;
- const char *file = LOCATION_FILE (diagnostic->location);
+ const char *file = LOCATION_FILE (diagnostic->richloc->get_loc ());
tree abstract_origin = diagnostic_abstract_origin (diagnostic);
char *new_prefix = (file && abstract_origin == NULL)
? file_name_as_prefix (context, file) : NULL;
@@ -3629,9 +3629,10 @@ pedwarn_cxx98 (location_t location, int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
(cxx_dialect == cxx98) ? DK_PEDWARN : DK_WARNING);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
@@ -25157,12 +25157,20 @@ cp_parser_check_class_key (enum tag_types class_key, tree type)
return;
if ((TREE_CODE (type) == UNION_TYPE) != (class_key == union_type))
{
- if (permerror (input_location, "%qs tag used in naming %q#T",
- class_key == union_type ? "union"
- : class_key == record_type ? "struct" : "class",
- type))
- inform (DECL_SOURCE_LOCATION (TYPE_NAME (type)),
- "%q#T was previously declared here", type);
+ rich_location rl_tag_reuse (input_location);
+ rl_tag_reuse.add_expr (type); // FIXME: new tree it doesn't exist yet, probably must use token
+ if (permerror_at_rich_loc (&rl_tag_reuse,
+ "%qs tag used in naming %q#T",
+ class_key == union_type ? "union"
+ : class_key == record_type ? "struct" : "class",
+ type))
+ {
+ rich_location rl_prev_decl (DECL_SOURCE_LOCATION (TYPE_NAME (type)));
+ rl_prev_decl.add_expr (TYPE_NAME (type));
+ inform_at_rich_loc (&rl_prev_decl,
+ "%q#T was previously declared here",
+ type);
+ }
}
}
@@ -4774,7 +4774,8 @@ cp_build_binary_op (location_t location,
|| !vector_types_compatible_elements_p (type0, type1))
{
if (complain & tf_error)
- binary_op_error (location, code, type0, type1);
+ binary_op_error (location, code, orig_op0, orig_op1,
+ type0, type1);
return error_mark_node;
}
arithmetic_types_p = 1;
@@ -4797,8 +4798,16 @@ cp_build_binary_op (location_t location,
if (!result_type)
{
if (complain & tf_error)
- error ("invalid operands of types %qT and %qT to binary %qO",
- TREE_TYPE (orig_op0), TREE_TYPE (orig_op1), code);
+ {
+ rich_location richloc (location);
+ richloc.add_expr_with_caption (orig_op0, global_dc->printer,
+ G_("type %qT"), TREE_TYPE (orig_op0));
+ richloc.add_expr_with_caption (orig_op1, global_dc->printer,
+ G_("type %qT"), TREE_TYPE (orig_op1));
+ error_at_rich_loc (&richloc,
+ "invalid operands of types %qT and %qT to binary %qO",
+ TREE_TYPE (orig_op0), TREE_TYPE (orig_op1), code);
+ }
return error_mark_node;
}
@@ -8553,8 +8562,13 @@ check_return_expr (tree retval, bool *no_warning)
its side-effects. */
finish_expr_stmt (retval);
else
- permerror (input_location, "return-statement with a value, in function "
- "returning 'void'");
+ {
+ rich_location richloc (input_location);
+ richloc.add_expr (retval);
+ permerror_at_rich_loc (&richloc,
+ "return-statement with a value, in function "
+ "returning 'void'");
+ }
current_function_returns_null = 1;
/* There's really no value to return, after all. */
@@ -165,6 +165,9 @@ static struct color_cap color_dict[] =
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 },
+ { "range0", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_BLUE), 6, false },
+ { "range1", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA), 6, false },
+ { "rangeN", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 6, false },
{ "locus", SGR_SEQ (COLOR_BOLD), 5, false },
{ "quote", SGR_SEQ (COLOR_BOLD), 5, false },
{ NULL, NULL, 0, false }
@@ -41,6 +41,8 @@ extern const char *progname;
extern const char *trim_filename (const char *);
+class rich_location;
+
/* If we haven't already defined a front-end-specific diagnostics
style, use the generic one. */
#ifndef GCC_DIAG_STYLE
@@ -64,18 +66,26 @@ extern bool warning_n (location_t, int, int, const char *, const char *, ...)
ATTRIBUTE_GCC_DIAG(4,6) ATTRIBUTE_GCC_DIAG(5,6);
extern bool warning_at (location_t, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
+extern bool warning_at_rich_loc (rich_location *, int, const char *, ...)
+ ATTRIBUTE_GCC_DIAG(3,4);
extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
extern void error_n (location_t, int, const char *, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
extern void error_at (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern void error_at_rich_loc (rich_location *, const char *, ...)
+ ATTRIBUTE_GCC_DIAG(2,3);
extern void fatal_error (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3)
ATTRIBUTE_NORETURN;
/* Pass one of the OPT_W* from options.h as the second parameter. */
extern bool pedwarn (location_t, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4);
extern bool permerror (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern bool permerror_at_rich_loc (rich_location *, const char *,
+ ...) ATTRIBUTE_GCC_DIAG(2,3);
extern void sorry (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
extern void inform (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern void inform_at_rich_loc (rich_location *, const char *,
+ ...) ATTRIBUTE_GCC_DIAG(2,3);
extern void inform_n (location_t, int, const char *, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
extern void verbatim (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
#include "config.h"
#include "system.h"
#include "coretypes.h"
+#include "tm.h"
#include "version.h"
#include "demangle.h"
#include "input.h"
@@ -32,6 +33,23 @@ along with GCC; see the file COPYING3. If not see
#include "backtrace.h"
#include "diagnostic.h"
#include "diagnostic-color.h"
+#include "vec.h"
+
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+
+#include "tree-core.h"
+#include "tree.h"
+
+#include "rich-location.h"
+#include "box-drawing.h"
#ifdef HAVE_TERMIOS_H
# include <termios.h>
@@ -146,7 +164,8 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
context->classify_diagnostic[i] = DK_UNSPECIFIED;
context->show_caret = false;
diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer));
- context->caret_char = '^';
+ gcc_assert (g_line_art.caret);
+ context->caret_char = g_line_art.caret;
context->show_option_requested = false;
context->abort_on_error = false;
context->show_column = false;
@@ -235,13 +254,13 @@ diagnostic_finish (diagnostic_context *context)
translated. */
void
diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
- va_list *args, location_t location,
+ va_list *args, rich_location *richloc,
diagnostic_t kind)
{
diagnostic->message.err_no = errno;
diagnostic->message.args_ptr = args;
diagnostic->message.format_spec = msg;
- diagnostic->location = location;
+ diagnostic->richloc = richloc;
diagnostic->override_column = 0;
diagnostic->kind = kind;
diagnostic->option_index = 0;
@@ -251,12 +270,19 @@ diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
translated. */
void
diagnostic_set_info (diagnostic_info *diagnostic, const char *gmsgid,
- va_list *args, location_t location,
+ va_list *args, rich_location *richloc,
diagnostic_t kind)
{
- diagnostic_set_info_translated (diagnostic, _(gmsgid), args, location, kind);
+ 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
+};
+
/* Return a malloc'd string describing a location. The caller is
responsible for freeing the memory. */
char *
@@ -269,12 +295,6 @@ diagnostic_build_prefix (diagnostic_context *context,
#undef DEFINE_DIAGNOSTIC_KIND
"must-not-happen"
};
- static const char *const diagnostic_kind_color[] = {
-#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C),
-#include "diagnostic.def"
-#undef DEFINE_DIAGNOSTIC_KIND
- NULL
- };
gcc_assert (diagnostic->kind < DK_LAST_DIAGNOSTIC_KIND);
const char *text = _(diagnostic_kind_text[diagnostic->kind]);
@@ -291,7 +311,8 @@ diagnostic_build_prefix (diagnostic_context *context,
locus_cs = colorize_start (pp_show_color (pp), "locus");
locus_ce = colorize_stop (pp_show_color (pp));
- expanded_location s = diagnostic_expand_location (diagnostic);
+ diagnostic->richloc->lazily_expand_location ();
+ expanded_location s = diagnostic->richloc->m_expanded_location;
return
(s.file == NULL
? build_message_string ("%s%s:%s %s%s%s", locus_cs, progname, locus_ce,
@@ -330,6 +351,576 @@ adjust_line (const char *line, int line_width,
return line;
}
+/* Is (column, row) within the given range?
+
+ Ranges are half-open.
+
+ 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
+ *outside* of it).
+ - 'a' indicates a subsequent point *after* the range. */
+
+bool
+location_range::contains_point (int row, int column) const
+{
+ gcc_assert (m_start.line <= m_finish.line);
+ /* ...but the equivalent isn't true for the columns;
+ consider example B in the comment above. */
+
+ if (row < m_start.line)
+ /* Points before the first line of the range are
+ outside it (corresponding to line 01 in example A
+ and lines 01 and 02 in example B above). */
+ return false;
+
+ if (row == m_start.line)
+ /* On same line as start of range (corresponding
+ to line 02 in example A and line 03 in example B). */
+ {
+ if (column < m_start.column)
+ /* Points on the starting line of the range, but
+ before the column in which it begins. */
+ return false;
+
+ if (row < m_finish.line)
+ /* This is a multiline range; the point
+ is within it (corresponds to line 03 in example B
+ from column 14 onwards) */
+ return true;
+ else
+ {
+ /* This is a single-line range. */
+ gcc_assert (row == m_finish.line);
+ return column < m_finish.column;
+ }
+ }
+
+ /* The point is in a line beyond that containing the
+ start of the range: lines 03 onwards in example A,
+ and lines 04 onwards in example B. */
+ gcc_assert (row > m_start.line);
+
+ if (row > m_finish.line)
+ /* The point is beyond the final line of the range
+ (lines 03 onwards in example A, and lines 06 onwards
+ in example B). */
+ return false;
+
+ if (row < m_finish.line)
+ {
+ /* The point is in a line that's fully within a multiline
+ range (e.g. line 04 in example B). */
+ gcc_assert (m_start.line < m_finish.line);
+ return true;
+ }
+
+ gcc_assert (row == m_finish.line);
+
+ return column < m_finish.column;
+}
+
+/* state:
+ -2 : normal text
+ -1 : at caret
+ [0..n]: within range n. */
+#define STATE_NORMAL_TEXT (-2)
+#define STATE_AT_CARET (-1)
+
+static int
+get_state_at_point (int row, int column, const rich_location *richloc,
+ expanded_location *caret,
+ int first_non_ws, int last_non_ws)
+{
+ if (row == caret->line && column == caret->column)
+ return STATE_AT_CARET;
+
+ /* Within a multiline range, don't display the caret in any leading
+ or trailing whitespace on a line. */
+ if (column < first_non_ws || column > last_non_ws)
+ return STATE_NORMAL_TEXT;
+
+ int i;
+ location_range *range;
+ FOR_EACH_VEC_ELT (richloc->m_ranges, i, range)
+ {
+#if 0
+ fprintf (stderr,
+ "range ( (%i, %i), (%i, %i))->contains_point (%i, %i): %s\n",
+ range->m_start.line,
+ range->m_start.column,
+ range->m_finish.line,
+ range->m_finish.column,
+ row,
+ column,
+ range->contains_point (row, column) ? "true" : "false");
+#endif
+
+ if (range->contains_point (row, column))
+ return i;
+ }
+
+ return STATE_NORMAL_TEXT;
+}
+
+/* Get the rightmost column that could contain a caret or range marker,
+ given that we stop rendering at trailing whitespace. */
+
+static int
+get_final_column_for_row (int row, int caret_column,
+ const rich_location *richloc,
+ int last_non_ws)
+{
+ int i;
+ location_range *range;
+
+ int result = caret_column;
+
+ FOR_EACH_VEC_ELT (richloc->m_ranges, i, range)
+ if (row >= range->m_start.line)
+ {
+ if (range->m_finish.line == row)
+ {
+ /* On the final line within a range; ensure that
+ we render up to the end of the range. */
+ if (result < range->m_finish.column)
+ result = range->m_finish.column;
+ }
+ else if (row < range->m_finish.line)
+ {
+ /* Within a multiline range; ensure that we render up to the
+ last non-whitespace column. */
+ if (result <= last_non_ws)
+ result = last_non_ws + 1;
+ }
+ }
+
+ return result;
+}
+
+/* A class to track state when printing the diagnostic locus.
+ Specifically, we track colorization. */
+
+class locus_printer
+{
+ public:
+ locus_printer (diagnostic_context *context,
+ const diagnostic_info *diagnostic);
+ ~locus_printer ();
+
+ void set_state (int state);
+
+ private:
+ void begin_state (int state);
+ void finish_state (int state);
+
+ private:
+ 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_range0_cs;
+ const char *m_range1_cs;
+ const char *m_rangeN_cs;
+ const char *m_range_ce;
+};
+
+locus_printer::locus_printer (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_range0_cs = colorize_start (pp_show_color (context->printer), "range0");
+ m_range1_cs = colorize_start (pp_show_color (context->printer), "range1");
+ m_rangeN_cs = colorize_start (pp_show_color (context->printer), "rangeN");
+ m_range_ce = colorize_stop (pp_show_color (context->printer));
+}
+
+locus_printer::~locus_printer ()
+{
+ finish_state (m_current_state);
+}
+
+/* Update state. */
+void
+locus_printer::set_state (int new_state)
+{
+ if (m_current_state != new_state)
+ {
+ finish_state (m_current_state);
+ m_current_state = new_state;
+ begin_state (new_state);
+ }
+}
+
+/* Turn on any colorization for STATE. */
+
+void
+locus_printer::begin_state (int state)
+{
+ switch (state)
+ {
+ case STATE_NORMAL_TEXT:
+ break;
+ case STATE_AT_CARET:
+ /* Make the caret 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_kind_color[m_diagnostic->kind]));
+ break;
+ default:
+ {
+ /* Within a range. */
+ gcc_assert (state >= 0);
+
+ const char *range_cs;
+ switch (state)
+ {
+ case 0:
+ range_cs = m_range0_cs;
+ break;
+ case 1:
+ range_cs = m_range1_cs;
+ break;
+ default:
+ range_cs = m_rangeN_cs;
+ break;
+ }
+ pp_string (m_context->printer, range_cs);
+ }
+ break;
+ }
+}
+
+/* Turn off any colorization for STATE. */
+
+void
+locus_printer::finish_state (int state)
+{
+ switch (state)
+ {
+ case STATE_NORMAL_TEXT:
+ break;
+ case STATE_AT_CARET:
+ 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;
+ }
+}
+
+/* Information on how to display a specific range within a
+ rich_location. */
+class range_layout
+{
+ public:
+ range_layout (int range_idx);
+
+ int get_range_index () const { return m_range_idx; }
+
+ void add_uniquely_captioned_row (int row);
+
+ int get_first_unique_row () const;
+ int get_last_unique_row () const;
+
+ bool contains_line (int line) const;
+
+ void determine_location_for_caption (const char *filename);
+
+ int get_caption_row () const { return m_caption_row; }
+ int get_caption_column () const { return m_caption_column; }
+
+ private:
+ int m_range_idx;
+ auto_vec<int> m_unique_rows;
+ int m_caption_row;
+ int m_caption_column;
+};
+
+range_layout::range_layout (int range_idx)
+: m_range_idx (range_idx),
+ m_unique_rows ()
+{
+}
+
+void
+range_layout::add_uniquely_captioned_row (int row)
+{
+ m_unique_rows.safe_push (row);
+}
+
+int
+range_layout::get_first_unique_row () const
+{
+ gcc_assert (m_unique_rows.length () > 0);
+ return m_unique_rows[0];
+}
+
+int
+range_layout::get_last_unique_row () const
+{
+ gcc_assert (m_unique_rows.length () > 0);
+
+ return m_unique_rows[m_unique_rows.length () - 1];
+}
+
+bool
+range_layout::contains_line (int line) const
+{
+ if (0 == m_unique_rows.length ())
+ return false;
+
+ return (line >= get_first_unique_row ()
+ && line <= get_last_unique_row ());
+}
+
+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;
+}
+
+void
+range_layout::determine_location_for_caption (const char *filename)
+{
+ if (0 == m_unique_rows.length ())
+ return;
+
+ /* Determine the widest line, using it as the column in which to render
+ the caption (m_caption_column). */
+ int result = 0;
+ for (int row = get_first_unique_row ();
+ row <= get_last_unique_row ();
+ row++)
+ {
+ int line_width;
+ const char *line = location_get_source_line (filename, row, &line_width);
+ line_width = get_line_width_without_trailing_whitespace (line,
+ line_width);
+ if (result < line_width)
+ result = line_width + 1;
+ }
+ m_caption_column = result;
+
+ /* Determine which row to write the caption text in: the middle row
+ within the range, rounding down mathematically (and thus up
+ visually). */
+ m_caption_row = (get_first_unique_row () + get_last_unique_row ()) / 2;
+}
+
+/* For now, just support the case of a single margin at once,
+ showing any captioned ranges that exclusively occupy their lines. */
+
+class layout
+{
+ public:
+ layout (rich_location *richloc);
+
+ range_layout *
+ get_any_range (int line);
+
+ void
+ print_any_margin (diagnostic_context *context,
+ locus_printer *locpr,
+ int line, int column, bool src);
+
+ private:
+ rich_location *m_richloc;
+ auto_vec <range_layout> m_range_layouts;
+};
+
+layout::layout (rich_location *richloc)
+ : m_richloc (richloc),
+ m_range_layouts ()
+{
+ int i;
+ location_range *range;
+ FOR_EACH_VEC_ELT (richloc->m_ranges, i, range)
+ {
+ range_layout rl (i);
+ m_range_layouts.safe_push (rl);
+ }
+
+ /* For each row, determine if it has a unique captioned range. */
+ int last_line = m_richloc->get_last_line ();
+ for (int row = m_richloc->get_first_line ();
+ row <= last_line;
+ row++)
+ {
+ int i;
+ location_range *range;
+ int num_captions_in_row = 0;
+ location_range *first_caption = NULL;
+ int first_caption_idx;
+ FOR_EACH_VEC_ELT (richloc->m_ranges, i, range)
+ if (range->m_caption)
+ {
+ if (row >= range->m_start.line
+ && row <= range->m_finish.line)
+ {
+ num_captions_in_row++;
+ if (!first_caption)
+ {
+ first_caption = range;
+ first_caption_idx = i;
+ }
+ }
+ }
+ if (first_caption && num_captions_in_row == 1)
+ m_range_layouts[first_caption_idx].add_uniquely_captioned_row (row);
+ }
+
+ /* At this stage, each range_layout now has a list of lines for which its
+ location_range uniquely captions that source line. */
+
+ /* Next, calculate the best position in which to draw each caption. */
+ range_layout *rl;
+ FOR_EACH_VEC_ELT (m_range_layouts, i, rl)
+ rl->determine_location_for_caption (richloc->m_ranges[i].m_start.file);
+}
+
+range_layout *
+layout::get_any_range (int line)
+{
+ int i;
+ range_layout *rl;
+ FOR_EACH_VEC_ELT (m_range_layouts, i, rl)
+ if (rl->contains_line (line))
+ return rl;
+ return NULL;
+}
+
+void
+layout::print_any_margin (diagnostic_context *context,
+ locus_printer *locpr,
+ int line, int column, bool src)
+{
+ range_layout *rl = get_any_range (line);
+ if (rl)
+ {
+ /* Fill with whitespace to get to the appropriate y-coordinate. */
+ while (column++ < rl->get_caption_column ())
+ pp_space (context->printer);
+
+ /* Colorize based on the range. */
+ int new_state = rl->get_range_index ();
+ locpr->set_state (new_state);
+
+ /* Draw a vertical line, with corners pointing to the left,
+ (falling back to '|' if unicode box-drawing is not available). */
+ if (line == rl->get_caption_row () && !src)
+ pp_string (context->printer,
+ line == rl->get_last_unique_row ()
+ ? g_line_art.rmargin_caption_row_at_end
+ : g_line_art.rmargin_caption_row);
+ else if (line == rl->get_first_unique_row () && src)
+ /* Start of multiline range. */
+ pp_string (context->printer, g_line_art.rmargin_start);
+ else if (line == rl->get_last_unique_row () && !src)
+ /* End of multiline range. */
+ pp_string (context->printer, g_line_art.rmargin_end);
+ else
+ pp_string (context->printer, g_line_art.rmargin_vbar);
+
+ /* Render the caption on the appropriate line.
+ This method is called twice per source line:
+ once for the source line, and once for the "underlining" line.
+ We render the caption for the second call. */
+ if (line == rl->get_caption_row () && !src)
+ {
+ location_range *range
+ = m_richloc->get_range (rl->get_range_index ());
+ gcc_assert (range);
+ /* Display any caption. */
+ if (range->m_caption)
+ pp_string (context->printer, range->m_caption);
+ }
+ }
+}
+
+/* For debugging layout issues in diagnostic_show_locus and friends,
+ render a ruler giving column numbers (after the 1-column indent). */
+
+static void
+show_ruler (diagnostic_context *context, int max_width)
+{
+ /* Hundreds. */
+ if (max_width > 99)
+ {
+ pp_space (context->printer);
+ for (int column = 1; column < max_width; column++)
+ if (0 == column % 10)
+ pp_character (context->printer, '0' + (column / 100) % 10);
+ else
+ pp_space (context->printer);
+ pp_newline (context->printer);
+ }
+
+ /* Tens. */
+ pp_space (context->printer);
+ for (int column = 1; column < max_width; column++)
+ if (0 == column % 10)
+ pp_character (context->printer, '0' + (column / 10) % 10);
+ else
+ pp_space (context->printer);
+ pp_newline (context->printer);
+
+ /* Units. */
+ pp_space (context->printer);
+ for (int column = 1; column < max_width; column++)
+ pp_character (context->printer, '0' + (column % 10));
+ pp_newline (context->printer);
+}
+
/* Print the physical source line corresponding to the location of
this diagnostic, and a caret indicating the precise column. */
void
@@ -338,20 +929,24 @@ diagnostic_show_locus (diagnostic_context * context,
{
const char *line;
int line_width;
- char *buffer;
expanded_location s;
int max_width;
const char *saved_prefix;
- const char *caret_cs, *caret_ce;
+ location_t location = diagnostic->richloc->get_loc ();
if (!context->show_caret
- || diagnostic->location <= BUILTINS_LOCATION
- || diagnostic->location == context->last_location)
+ || location <= BUILTINS_LOCATION
+ || location == context->last_location)
return;
- context->last_location = diagnostic->location;
+ context->last_location = location;
+#if 0
s = diagnostic_expand_location (diagnostic);
- line = location_get_source_line (s, &line_width);
+#else
+ diagnostic->richloc->lazily_expand_location ();
+ s = diagnostic->richloc->m_expanded_location;
+#endif
+ line = location_get_source_line (s.file, s.line, &line_width);
if (line == NULL || s.column > line_width)
return;
@@ -361,6 +956,9 @@ diagnostic_show_locus (diagnostic_context * context,
pp_newline (context->printer);
saved_prefix = pp_get_prefix (context->printer);
pp_set_prefix (context->printer, NULL);
+ /* FIXME: need to reimplement the adjust_line margin-adjustment logic
+ for the case of multiline, with ranges as well as carets. */
+#if 0
pp_space (context->printer);
while (max_width > 0 && line_width > 0)
{
@@ -373,19 +971,156 @@ diagnostic_show_locus (diagnostic_context * context,
line++;
}
pp_newline (context->printer);
- caret_cs = colorize_start (pp_show_color (context->printer), "caret");
- caret_ce = colorize_stop (pp_show_color (context->printer));
+#endif
+
+ if (0)
+ show_ruler (context, max_width);
/* pp_printf does not implement %*c. */
- size_t len = s.column + 3 + strlen (caret_cs) + strlen (caret_ce);
- buffer = XALLOCAVEC (char, len);
- snprintf (buffer, len, "%s %*c%s", caret_cs, s.column, context->caret_char,
- caret_ce);
- pp_string (context->printer, buffer);
+ /* For now, assume we have disjoint ranges. */
+ {
+ locus_printer locpr (context, diagnostic);
+ layout layout (diagnostic->richloc);
+ int last_line = diagnostic->richloc->get_last_line ();
+ for (int row = diagnostic->richloc->get_first_line ();
+ row <= last_line;
+ row++)
+ {
+ int first_non_ws = INT_MAX;
+ int last_non_ws = 0;
+ line = location_get_source_line (s.file, row, &line_width);
+ if (line)
+ {
+ /* Print the source code line. */
+
+ /* Stop printing at any trailing whitespace. */
+ line_width
+ = get_line_width_without_trailing_whitespace (line,
+ line_width);
+
+ pp_space (context->printer);
+ locpr.set_state (STATE_NORMAL_TEXT);
+ int column;
+ for (column = 1; column <= line_width; column++)
+ {
+ int new_state = get_state_at_point (row, column,
+ diagnostic->richloc, &s,
+ 0, INT_MAX);
+ locpr.set_state (new_state);
+ char c = *line == '\t' ? ' ' : *line;
+ if (c == '\0')
+ c = ' ';
+ if (c != ' ')
+ {
+ last_non_ws = column;
+ if (first_non_ws == INT_MAX)
+ first_non_ws = column;
+ }
+ pp_character (context->printer, c);
+ line++;
+ }
+ layout.print_any_margin (context, &locpr, row, column, true);
+ pp_newline (context->printer);
+ }
+
+ /* Print a line consisting of the caret/underlines for the
+ given source line. */
+ int final_column
+ = get_final_column_for_row (row, s.column,
+ diagnostic->richloc,
+ last_non_ws);
+ pp_space (context->printer);
+ for (int column = 1; column < final_column; column++)
+ {
+ int new_state = get_state_at_point (row, column,
+ diagnostic->richloc, &s,
+ first_non_ws, last_non_ws);
+ locpr.set_state (new_state);
+ if (new_state == STATE_AT_CARET)
+ pp_string (context->printer, context->caret_char);
+ else if (new_state == STATE_NORMAL_TEXT)
+ pp_character (context->printer, ' ');
+ else {
+ /* Underlining a range. */
+ const location_range *range
+ = &diagnostic->richloc->m_ranges[new_state];
+ if (range->m_start.line != range->m_finish.line)
+ {
+ /* Underline multiline ranges using southwest/southeast
+ corners for the very first/last positions, and hbar
+ everywhere else. */
+ if (row == range->m_start.line
+ && column == range->m_start.column)
+ /* Start of multiline range: use a southwest corner for
+ the underline. */
+ pp_string (context->printer, g_line_art.underline_start);
+ else if (row == range->m_finish.line
+ && column == range->m_finish.column - 1)
+ /* End of multiline range: use a southeast corner for
+ the underline. */
+ pp_string (context->printer, g_line_art.underline_end);
+ else
+ pp_string (context->printer, g_line_art.underline_hbar);
+ }
+ else
+ /* Render single-line ranges with the hbar character
+ throughout. */
+ pp_string (context->printer, g_line_art.underline_hbar);
+ }
+ }
+ layout.print_any_margin (context, &locpr, row, final_column, false);
+
+ if (row < last_line)
+ pp_newline (context->printer);
+ }
+ }
+
pp_set_prefix (context->printer, saved_prefix);
pp_needs_newline (context->printer) = true;
}
+int
+rich_location::get_first_line ()
+{
+ lazily_expand_location ();
+ int result = m_expanded_location.line;
+
+ int i;
+ location_range *range;
+
+ FOR_EACH_VEC_ELT (m_ranges, i, range)
+ if (result > range->m_start.line)
+ result = range->m_start.line;
+
+ return result;
+}
+
+int
+rich_location::get_last_line ()
+{
+ lazily_expand_location ();
+ int result = m_expanded_location.line;
+
+ int i;
+ location_range *range;
+
+ FOR_EACH_VEC_ELT (m_ranges, i, range)
+ if (result < range->m_finish.line)
+ result = range->m_finish.line;
+
+ return result;
+}
+
+void
+rich_location::lazily_expand_location ()
+{
+ if (m_have_expanded_location)
+ return;
+
+ m_expanded_location = expand_location_to_spelling_point (m_loc);
+ m_have_expanded_location = true;
+}
+
/* Functions at which to stop the backtrace print. It's not
particularly helpful to print the callers of these functions. */
@@ -604,7 +1339,7 @@ void
default_diagnostic_starter (diagnostic_context *context,
diagnostic_info *diagnostic)
{
- diagnostic_report_current_module (context, diagnostic->location);
+ diagnostic_report_current_module (context, diagnostic->richloc->get_loc ());
pp_set_prefix (context->printer, diagnostic_build_prefix (context,
diagnostic));
}
@@ -716,7 +1451,7 @@ bool
diagnostic_report_diagnostic (diagnostic_context *context,
diagnostic_info *diagnostic)
{
- location_t location = diagnostic->location;
+ location_t location = diagnostic->richloc->get_loc();
diagnostic_t orig_diag_kind = diagnostic->kind;
const char *saved_format_spec;
@@ -859,7 +1594,7 @@ diagnostic_report_diagnostic (diagnostic_context *context,
free (option_text);
}
}
- diagnostic->message.locus = &diagnostic->location;
+ diagnostic->message.locus = diagnostic->richloc->get_loc_addr ();
diagnostic->message.x_data = &diagnostic->x_data;
diagnostic->x_data = NULL;
pp_format (context->printer, &diagnostic->message);
@@ -936,9 +1671,40 @@ diagnostic_append_note (diagnostic_context *context,
diagnostic_info diagnostic;
va_list ap;
const char *saved_prefix;
+ rich_location richloc (location);
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &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, location, DK_NOTE);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE);
if (context->inhibit_notes_p)
{
va_end (ap);
@@ -963,16 +1729,17 @@ emit_diagnostic (diagnostic_t kind, location_t location, int opt,
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, gmsgid);
if (kind == DK_PERMERROR)
{
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
permissive_error_kind (global_dc));
diagnostic.option_index = permissive_error_option (global_dc);
}
else {
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, kind);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, kind);
if (kind == DK_WARNING || kind == DK_PEDWARN)
diagnostic.option_index = opt;
}
@@ -989,9 +1756,23 @@ inform (location_t location, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &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);
}
@@ -1004,11 +1785,12 @@ inform_n (location_t location, int n, const char *singular_gmsgid,
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, plural_gmsgid);
diagnostic_set_info_translated (&diagnostic,
ngettext (singular_gmsgid, plural_gmsgid, n),
- &ap, location, DK_NOTE);
+ &ap, &richloc, DK_NOTE);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -1022,9 +1804,10 @@ warning (int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_WARNING);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
@@ -1042,9 +1825,27 @@ warning_at (location_t location, int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_WARNING);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING);
+ diagnostic.option_index = opt;
+ ret = report_diagnostic (&diagnostic);
+ va_end (ap);
+ return ret;
+}
+
+/* Same as warning at, but using RICHLOC. */
+
+bool
+warning_at_rich_loc (rich_location *richloc, int opt, const char *gmsgid, ...)
+{
+ diagnostic_info diagnostic;
+ va_list ap;
+ bool ret;
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_WARNING);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
va_end (ap);
@@ -1062,11 +1863,13 @@ warning_n (location_t location, int opt, int n, const char *singular_gmsgid,
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, plural_gmsgid);
diagnostic_set_info_translated (&diagnostic,
ngettext (singular_gmsgid, plural_gmsgid, n),
- &ap, location, DK_WARNING);
+ &ap, &richloc, DK_WARNING
+);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
va_end (ap);
@@ -1092,9 +1895,10 @@ pedwarn (location_t location, int opt, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
diagnostic.option_index = opt;
ret = report_diagnostic (&diagnostic);
va_end (ap);
@@ -1114,9 +1918,28 @@ permerror (location_t location, const char *gmsgid, ...)
diagnostic_info diagnostic;
va_list ap;
bool ret;
+ rich_location richloc (location);
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
+ permissive_error_kind (global_dc));
+ diagnostic.option_index = permissive_error_option (global_dc);
+ ret = report_diagnostic (&diagnostic);
+ va_end (ap);
+ return ret;
+}
+
+/* Same as "permerror", but at RICHLOC. */
+
+bool
+permerror_at_rich_loc (rich_location *richloc, const char *gmsgid, ...)
+{
+ diagnostic_info diagnostic;
+ va_list ap;
+ bool ret;
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc,
permissive_error_kind (global_dc));
diagnostic.option_index = permissive_error_option (global_dc);
ret = report_diagnostic (&diagnostic);
@@ -1131,9 +1954,10 @@ error (const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ERROR);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -1146,11 +1970,12 @@ error_n (location_t location, int n, const char *singular_gmsgid,
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (location);
va_start (ap, plural_gmsgid);
diagnostic_set_info_translated (&diagnostic,
ngettext (singular_gmsgid, plural_gmsgid, n),
- &ap, location, DK_ERROR);
+ &ap, &richloc, DK_ERROR);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -1161,9 +1986,25 @@ error_at (location_t loc, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (loc);
+
+ va_start (ap, gmsgid);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &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, loc, DK_ERROR);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, rich_loc,
+ DK_ERROR);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -1176,9 +2017,10 @@ sorry (const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_SORRY);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_SORRY);
report_diagnostic (&diagnostic);
va_end (ap);
}
@@ -1199,9 +2041,10 @@ fatal_error (location_t loc, const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (loc);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_FATAL);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_FATAL);
report_diagnostic (&diagnostic);
va_end (ap);
@@ -1217,9 +2060,10 @@ internal_error (const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE);
report_diagnostic (&diagnostic);
va_end (ap);
@@ -1234,9 +2078,10 @@ internal_error_no_backtrace (const char *gmsgid, ...)
{
diagnostic_info diagnostic;
va_list ap;
+ rich_location richloc (input_location);
va_start (ap, gmsgid);
- diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE_NOBT);
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE_NOBT);
report_diagnostic (&diagnostic);
va_end (ap);
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
#include "pretty-print.h"
#include "diagnostic-core.h"
+#include "rich-location.h"
/* A diagnostic is described by the MESSAGE to send, the FILE and LINE of
its context and its KIND (ice, error, warning, note, ...) See complete
@@ -30,7 +31,7 @@ along with GCC; see the file COPYING3. If not see
struct diagnostic_info
{
text_info message;
- location_t location;
+ rich_location *richloc;
unsigned int override_column;
/* Auxiliary data for client. */
void *x_data;
@@ -106,7 +107,7 @@ struct diagnostic_context
int caret_max_width;
/* Character used for caret diagnostics. */
- char caret_char;
+ const char *caret_char;
/* True if we should print the command line option which controls
each diagnostic, if known. */
@@ -282,13 +283,17 @@ extern bool diagnostic_report_diagnostic (diagnostic_context *,
diagnostic_info *);
#ifdef ATTRIBUTE_GCC_DIAG
extern void diagnostic_set_info (diagnostic_info *, const char *, va_list *,
- location_t, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0);
+ rich_location *, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0);
extern void diagnostic_set_info_translated (diagnostic_info *, const char *,
- va_list *, location_t,
+ va_list *, rich_location *,
diagnostic_t)
ATTRIBUTE_GCC_DIAG(2,0);
extern void diagnostic_append_note (diagnostic_context *, location_t,
const char *, ...) ATTRIBUTE_GCC_DIAG(3,4);
+extern void diagnostic_append_note_at_rich_loc (diagnostic_context *,
+ rich_location *,
+ const char *, ...)
+ ATTRIBUTE_GCC_DIAG(3,4);
#endif
extern char *diagnostic_build_prefix (diagnostic_context *, const diagnostic_info *);
void default_diagnostic_starter (diagnostic_context *, diagnostic_info *);
@@ -306,7 +311,7 @@ static inline expanded_location
diagnostic_expand_location (const diagnostic_info * diagnostic)
{
expanded_location s
- = expand_location_to_spelling_point (diagnostic->location);
+ = expand_location_to_spelling_point (diagnostic->richloc->get_loc ());
if (diagnostic->override_column)
s.column = diagnostic->override_column;
return s;
@@ -1045,6 +1045,7 @@ cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
diagnostic_t dlevel;
bool save_warn_system_headers = global_dc->dc_warn_system_headers;
bool ret;
+ rich_location richloc (location);
switch (level)
{
@@ -1073,7 +1074,7 @@ cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
gcc_unreachable ();
}
diagnostic_set_info_translated (&diagnostic, msg, ap,
- location, dlevel);
+ &richloc, dlevel);
if (column_override)
diagnostic_override_column (&diagnostic, column_override);
if (reason == CPP_W_WARNING_DIRECTIVE)
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic.h"
#include "diagnostic-color.h"
#include "tree-diagnostic.h" /* tree_diagnostics_defaults */
+#include "box-drawing.h"
#include <new> /* For placement-new */
@@ -1125,7 +1126,7 @@ gfc_format_decoder (pretty_printer *pp,
= linemap_position_for_loc_and_offset (line_table,
loc->lb->location,
offset);
- global_dc->caret_char = '1';
+ global_dc->caret_char = "1";
pp_string (pp, result);
return true;
}
@@ -1639,7 +1640,7 @@ gfc_diagnostics_init (void)
diagnostic_starter (global_dc) = gfc_diagnostic_starter;
diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
diagnostic_format_decoder (global_dc) = gfc_format_decoder;
- global_dc->caret_char = '^';
+ global_dc->caret_char = g_line_art.caret;
pp_warning_buffer = new (XNEW (output_buffer)) output_buffer ();
pp_warning_buffer->flush_p = false;
pp_error_buffer = new (XNEW (output_buffer)) output_buffer ();
@@ -1654,5 +1655,5 @@ gfc_diagnostics_finish (void)
defaults. */
diagnostic_starter (global_dc) = gfc_diagnostic_starter;
diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
- global_dc->caret_char = '^';
+ global_dc->caret_char = g_line_art.caret;
}
@@ -685,27 +685,27 @@ read_line_num (fcache *c, size_t line_num,
return read_next_line (c, line, line_len);
}
-/* Return the physical source line that corresponds to xloc in a
+/* Return the physical source line that corresponds to FILE_PATH/LINE in a
buffer that is statically allocated. The newline is replaced by
the null character. Note that the line can contain several null
characters, so LINE_LEN, if non-null, points to the actual length
of the line. */
const char *
-location_get_source_line (expanded_location xloc,
+location_get_source_line (const char *file_path, int line,
int *line_len)
{
static char *buffer;
static ssize_t len;
- if (xloc.line == 0)
+ if (line == 0)
return NULL;
- fcache *c = lookup_or_add_file_to_cache_tab (xloc.file);
+ fcache *c = lookup_or_add_file_to_cache_tab (file_path);
if (c == NULL)
return NULL;
- bool read = read_line_num (c, xloc.line, &buffer, &len);
+ bool read = read_line_num (c, line, &buffer, &len);
if (read && line_len)
*line_len = len;
@@ -38,7 +38,7 @@ extern char builtins_location_check[(BUILTINS_LOCATION
extern bool is_location_from_builtin_token (source_location);
extern expanded_location expand_location (source_location);
-extern const char *location_get_source_line (expanded_location xloc,
+extern const char *location_get_source_line (const char *file_path, int line,
int *line_size);
extern expanded_location expand_location_to_spelling_point (source_location);
extern source_location expansion_point_location_if_in_system_header (source_location);
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
#include "intl.h"
+#include "box-drawing.h"
#ifdef HAVE_LANGINFO_CODESET
#include <langinfo.h>
@@ -86,6 +87,15 @@ gcc_init_libintl (void)
}
#endif
}
+
+ bool have_utf8 = false;
+#if defined HAVE_LANGINFO_CODESET
+ if (locale_utf8)
+ have_utf8 = true;
+#endif
+
+ //fprintf (stderr, "have_utf8: %s\n", have_utf8 ? "true" : "false");
+ g_line_art.init (have_utf8);
}
#if defined HAVE_WCHAR_H && defined HAVE_WORKING_MBSTOWCS && defined HAVE_WCSWIDTH
new file mode 100644
@@ -0,0 +1,319 @@
+/* A file containing kludges and hardcoded data
+ Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+#include "tree-core.h"
+#include "tree.h"
+#include "diagnostic-core.h"
+#include "rich-location.h"
+#include "print-tree.h"
+#include "pretty-print.h"
+#include "intl.h"
+#include "location-hacks.h"
+
+/* We need to come up with an efficient way of storing information on
+ source code *ranges* (rather than mere points) for various constructs
+ such as expressions and macros.
+
+ Ideally the location data would preserve range information, and we'd
+ consult that.
+
+ In the meantime, here's a massive kludge: a magical location "oracle",
+ that simply hardcodes the data for each testcase.
+
+ Obviously this needs to be eliminated, but having it allows us to work on
+ other aspects of rich location reporting (e.g. to extend the diagnostics
+ API, and to write testcases) without needing to address the range
+ representation issue up-front.
+
+ Note that the column numbers here are 0-based convention, for ease of
+ working with Emacs, whereas the GCC location data structures and
+ reporting mechanisms are 1-based. */
+
+struct hacked_range
+{
+ int start_line;
+ int start_column;
+ int finish_line;
+ int finish_column;
+};
+
+struct expr_locations_for_testcase
+{
+ const char *testcase_name;
+ int num_locs;
+ struct hacked_range loc_range[3];
+};
+
+struct expr_locations_for_testcase expr_location_oracle[] = {
+ {"testsuite/gcc.dg/20150303-01.c",
+ 3,
+ {{6, 2, 6, 15},
+ {7, 2, 7, 6},
+ {4, 16, 4, 20}}
+ },
+
+ {"testsuite/gcc.dg/20150303-04.c",
+ 2,
+ {{9, 2, 9, 10},
+ {9, 11, 9, 12}}
+ },
+
+ {"testsuite/gcc.dg/20150304-01.c",
+ 3,
+ {{4, 89, 4, 92},
+ {4, 95, 4, 98},
+ {11, 6, 11, 17}}
+ },
+
+ {"testsuite/gcc.dg/divbyzero.c",
+ 3,
+ {{13, 17, 13, 21},
+ {14, 17, 14, 18},
+ {15, 17, 15, 21}}
+ },
+
+ {"testsuite/g++.dg/diagnostic/20150303-02.C",
+ 2,
+ {{12, 9, 12, 17},
+ {12, 20, 12, 22}}
+ },
+
+ {"testsuite/g++.dg/diagnostic/20150303-03.C",
+ 1,
+ {{5, 9, 5, 16}}
+ },
+
+ {"testsuite/g++.dg/diagnostic/20150304-02.C",
+ 1,
+ {{10, 3, 10, 7}}
+ },
+
+ {"testsuite/g++.dg/diagnostic/bitfld1.C",
+ 2,
+ {{12, 10, 12, 13},
+ {12, 16, 12, 19}}
+ },
+
+ {"testsuite/g++.dg/diagnostic/multiline-rich-location-01.C",
+ 2,
+ {{12, 10, 12, 49},
+ {13, 12, 13, 52}}
+ },
+
+ {"testsuite/g++.dg/diagnostic/multiline-rich-location-02.C",
+ 2,
+ {{25, 10, 29, 70},
+ {30, 12, 35, 65}}
+ },
+
+ {"testsuite/g++.dg/diagnostic/wrong-tag-1.C",
+ 2,
+ {{5, 14, 5, 21},
+ {4, 15, 4, 22}}
+ },
+
+ {"testsuite/g++.dg/other/error16.C",
+ 2,
+ {{14, 2, 14, 10},
+ {14, 14, 14, 15}}
+ },
+
+ /* Sentinel. */
+ {NULL, 0, {}}
+};
+
+static bool
+consult_the_range_oracle (location_range *r)
+{
+ const char *testcase = strstr (main_input_filename,
+ "testsuite/");
+ if (!testcase)
+ return false;
+
+ /* Locate testcase by name within "expr_location_oracle" array. */
+ static int num_calls = 0;
+ for (expr_locations_for_testcase *iter = expr_location_oracle;
+ iter->testcase_name;
+ iter++)
+ if (0 == strcmp (testcase, iter->testcase_name))
+ {
+ if (num_calls >= iter->num_locs)
+ {
+ fprintf (stderr,
+ "ran out of locations within location oracle"
+ " (testcase: %s)\n",
+ testcase);
+ return false;
+ }
+
+ gcc_assert (num_calls < iter->num_locs);
+ struct hacked_range *hr = &iter->loc_range[num_calls++];
+ r->m_start.file = main_input_filename;
+ r->m_finish.file = main_input_filename;
+ r->m_start.line = hr->start_line;
+ r->m_finish.line = hr->finish_line;
+ /* Convert from 0-based column numberings to 1-based numbering. */
+ r->m_start.column = hr->start_column + 1;
+ r->m_finish.column = hr->finish_column + 1;
+ return true;
+ }
+
+ /* Testcase not found. */
+ fprintf (stderr,
+ "not found within location oracle (testcase: %s)\n",
+ testcase);
+ return false;
+}
+
+/* Get the location_range for the given tree expression.
+ For now, this simply looks thinks up in the hardcoded data. */
+
+bool
+get_range_for_expr (tree /* expr */, location_range *r)
+{
+ /* For now, a total hack. */
+ return consult_the_range_oracle (r);
+}
+
+/* Get the location_range for the given macro.
+ For now, this simply looks thinks up in the hardcoded data. */
+
+bool
+get_range_for_macro (const line_map * /* macro */, location_range *r)
+{
+ /* Likewise, this is also currently a total hack. */
+ return consult_the_range_oracle (r);
+}
+
+struct string_location
+{
+ int index;
+ int line;
+ int column; /* 0-based */
+};
+
+struct string_constant
+{
+ const char *chars;
+ int num_locs;
+ string_location locs[3];
+};
+
+struct string_locations_for_testcase
+{
+ const char *testcase_name;
+ int num_consts;
+ string_constant consts[1];
+};
+
+struct string_locations_for_testcase string_location_oracle[] = {
+ {"testsuite/gcc.dg/format/pr52952-multiline-format-string.c",
+ 1,
+ {{"before the fmt specifier%dafter the fmt specifier",
+ 2,
+ {{24, 6, 11},
+ {25, 7, 11}}}}
+ },
+
+ {NULL, 0, {}}
+};
+
+/* Determine the source-code location of character IDX within
+ string constant STRING_CST, writing the resuilt to LOC_OUT
+ and EXPLOC_OUT. Ideally we'd be able to get this from the
+ STRING_CST. Sadly, it's a hack, and HACKED_LOC_HINT is
+ needed to help for the case where we don't have hardcoded
+ data. */
+
+void
+get_location_within_string_cst (location_t *loc_out,
+ expanded_location *exploc_out,
+ tree string_cst, int idx,
+ location_t hacked_loc_hint)
+{
+ gcc_assert (loc_out);
+ gcc_assert (exploc_out);
+ gcc_assert (string_cst);
+ gcc_assert (TREE_CODE (string_cst) == STRING_CST);
+ gcc_assert (idx >= 0);
+
+ /* FIXME: STRING_CSTs don't have a LOCATION_P, so for now we have
+ to fake it. */
+ gcc_assert (!CAN_HAVE_LOCATION_P (string_cst));
+
+ const char *testcase = strstr (main_input_filename,
+ "testsuite/");
+ if (testcase)
+ {
+ for (string_locations_for_testcase *iter = string_location_oracle;
+ iter->testcase_name;
+ iter++)
+ if (0 == strcmp (testcase, iter->testcase_name))
+ {
+ const char *str_chars = TREE_STRING_POINTER (string_cst);
+ int str_length = TREE_STRING_LENGTH (string_cst);
+
+ for (int i = 0; i < iter->num_consts; i++)
+ {
+ string_constant *str_const = &iter->consts[i];
+ if (0 == strncmp (str_chars, str_const->chars, str_length - 1))
+ for (int j = 0; j < str_const->num_locs; j++)
+ {
+ string_location *str_loc = &str_const->locs[j];
+ if (idx == str_loc->index)
+ {
+ *loc_out = hacked_loc_hint;
+ *exploc_out
+ = expand_location_to_spelling_point (*loc_out);
+ exploc_out->line = str_loc->line;
+ /* Convert from 0-based column numberings to
+ 1-based numbering. */
+ exploc_out->column = str_loc->column + 1;
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ *loc_out = hacked_loc_hint;
+
+ *exploc_out = expand_location_to_spelling_point (*loc_out);
+
+ /* FIXME: for now, assume a simple contiguous string literal
+ with no escaped characters
+ The existing loc hint is for the opening quote of the string
+ literal, and so we need an extra + 1 so that idx 0 is the location
+ of the initial character. */
+ exploc_out->column += idx + 1;
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/* Header file for location kludges and hardcoded data
+ Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+extern bool
+get_range_for_expr (tree expr, location_range *r);
+
+extern bool
+get_range_for_macro (const line_map * macro, location_range *r);
new file mode 100644
@@ -0,0 +1,147 @@
+/* Implementation of rich_location class
+ Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+#include "tree-core.h"
+#include "tree.h"
+#include "diagnostic-core.h"
+#include "rich-location.h"
+#include "print-tree.h"
+#include "pretty-print.h"
+#include "intl.h"
+#include "location-hacks.h"
+
+/* A ctor for rich_location, constructing a rich_location for
+ a subset of string literal STRING_CST.
+ CARET_IDX, START_IDX and END_IDX are indices of characters within
+ the string, for the caret, and for the start/end of a range. */
+
+rich_location::rich_location (tree string_cst,
+ int caret_idx,
+ int start_idx,
+ int end_idx,
+ location_t hacked_loc_hint)
+ : m_loc (UNKNOWN_LOCATION),
+ m_ranges (),
+ m_have_expanded_location (false)
+{
+ gcc_assert (string_cst);
+ gcc_assert (TREE_CODE (string_cst) == STRING_CST);
+
+ m_have_expanded_location = true;
+ get_location_within_string_cst (&m_loc,
+ &m_expanded_location,
+ string_cst, caret_idx,
+ hacked_loc_hint);
+
+ location_t tmploc;
+ location_range r;
+ r.m_caption = NULL;
+ get_location_within_string_cst (&tmploc,
+ &r.m_start,
+ string_cst, start_idx,
+ hacked_loc_hint);
+ /* For the end of the range, the end_idx is within the range,
+ so get its location, and offset by 1 to locate the first
+ column *outside* the range. */
+ get_location_within_string_cst (&tmploc,
+ &r.m_finish,
+ string_cst, end_idx,
+ hacked_loc_hint);
+ r.m_finish.column += 1;
+ m_ranges.safe_push (r);
+}
+
+/* Add a range to the rich_location, covering expression EXPR. */
+
+void
+rich_location::add_expr (tree expr)
+{
+ gcc_assert (expr);
+
+ location_range r;
+ r.m_caption = NULL;
+ //debug_tree (expr);
+ if (get_range_for_expr (expr, &r))
+ m_ranges.safe_push (r);
+}
+
+/* As per rich_location::add_expr, but adding a caption to the
+ resulting range. */
+
+void
+rich_location::add_expr_with_caption (tree expr, pretty_printer *pp,
+ const char *gmsgid, ...)
+{
+ gcc_assert (expr);
+ gcc_assert (gmsgid);
+
+ location_range r;
+ r.m_caption = NULL;
+ //debug_tree (expr);
+ if (get_range_for_expr (expr, &r))
+ {
+ /* We want to save the string information for later replay.
+ The printer->format_decoder machinery assumes that we're working
+ directly from va_args. It's not possible to save a va_args
+ for later reuse, since it makes use of a specific stack frame
+ which needs to still be around.
+ Hence we need to expand the formatting now, and save the result. */
+
+ /* FIXME: Only bother if show-caret is enabled. */
+ text_info text;
+ va_list ap;
+ va_start (ap, gmsgid);
+ text.err_no = errno;
+ text.args_ptr = ≈
+ text.format_spec = G_(gmsgid); //_(gmsgid);
+ text.locus = NULL;
+ pp_format (pp, &text);
+ pp_output_formatted_text (pp);
+ va_end (ap);
+ r.m_caption = xstrdup (pp_formatted_text (pp));
+ pp_clear_output_area (pp);
+
+ m_ranges.safe_push (r);
+ }
+}
+
+/* Add a range to the rich_location, covering MACRO. */
+
+void
+rich_location::add_range_for_macro (const line_map *macro)
+{
+ location_range r;
+ r.m_caption = NULL;
+ if (get_range_for_macro (macro, &r))
+ m_ranges.safe_push (r);
+}
new file mode 100644
@@ -0,0 +1,126 @@
+/* Declarations relating to class rich_location
+ Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_RICH_LOCATION_H
+#define GCC_RICH_LOCATION_H
+
+#include "vec.h"
+
+/* 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 half-open:
+ m_start is the first location within the range, whereas
+ m_finish is the first location *after* the range. */
+struct location_range
+{
+ expanded_location m_start;
+ expanded_location m_finish;
+
+ /* Caption, if any. */
+ char *m_caption;
+
+ bool contains_point (int row, int column) const;
+};
+
+/* A "rich" source code location. A position, along with
+ zero of more ranges. Each range may optionally have a caption. */
+
+class rich_location
+{
+ public:
+ /* Constructors. */
+
+ /* Constructing from a location. */
+ rich_location (location_t loc) :
+ m_loc (loc),
+ m_ranges (),
+ m_have_expanded_location (false)
+ {}
+
+ /* Constructing from a caret and a range of characters, all within
+ a string literal.
+ The range is open: both START_IDX and END_IDX are within the range. */
+ rich_location (tree string_cst,
+ int caret_idx,
+ int start_idx,
+ int end_idx,
+ location_t hacked_loc_hint);
+
+ /* Accessors. */
+ location_t get_loc () const { return m_loc; }
+
+ location_t *get_loc_addr () { return &m_loc; }
+
+ void
+ add_expr (tree expr);
+
+ void
+ add_expr_with_caption (tree expr, pretty_printer *pp,
+ const char *gmsgid, ...)
+ ATTRIBUTE_GCC_DIAG(4,5);
+
+ void
+ add_range_for_macro (const line_map *macro);
+
+ int get_first_line ();
+ int get_last_line ();
+
+ location_range *get_range (unsigned int idx)
+ {
+ gcc_assert (idx < m_ranges.length ());
+ return &m_ranges[idx];
+ }
+
+ void lazily_expand_location ();
+
+//private:
+ location_t m_loc;
+ auto_vec <location_range> m_ranges;
+
+//private:
+ bool m_have_expanded_location;
+ expanded_location m_expanded_location;
+
+};
+
+extern void
+get_location_within_string_cst (location_t *loc_out,
+ expanded_location *exploc_out,
+ tree string_cst, int idx,
+ location_t hacked_loc_hint);
+
+#endif /* GCC_RICH_LOCATION_H */
@@ -70,9 +70,10 @@ diagnostic_for_asm (const rtx_insn *insn, const char *msg, va_list *args_ptr,
diagnostic_t kind)
{
diagnostic_info diagnostic;
+ rich_location richloc (location_for_asm (insn));
diagnostic_set_info (&diagnostic, msg, args_ptr,
- location_for_asm (insn), kind);
+ &richloc, kind);
report_diagnostic (&diagnostic);
}
new file mode 100644
@@ -0,0 +1,18 @@
+// Adapted from https://gcc.gnu.org/wiki/ClangDiagnosticsComparison
+// { dg-options "-fdiagnostics-show-caret" }
+
+struct a {
+ virtual int bar();
+};
+
+struct foo : public virtual a {
+};
+
+int test(foo *P) {
+ return P->bar() + *P; // { dg-error "no match" }
+}
+
+/* { dg-begin-multiline-output "" }
+ return P->bar() + *P;
+ ~~~~~~~~ ^ ~~
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,12 @@
+// Adapted from https://gcc.gnu.org/wiki/ClangDiagnosticsComparison
+// { dg-options "-fdiagnostics-show-caret" }
+
+void test(int *P) {
+ return *P + *P; // { dg-error "return-statement with a value" }
+}
+
+/*
+{ dg-begin-multiline-output "" }
+ return *P + *P;
+ ~~~~~~^
+{ dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,23 @@
+// Adapted from https://gcc.gnu.org/wiki/ClangDiagnosticsComparison
+// { dg-options "-fdiagnostics-show-caret" }
+
+template<class T> void f(typename T::type) { } // { dg-message "candidate: template<class T> void f\\(typename T::type\\)|no type named 'type' in 'struct A'" }
+
+struct A { };
+void g()
+{
+ A a;
+ f<A>(a); // { dg-error "no matching function for call to 'f\\(A&\\)'" }
+}
+
+/*
+{ dg-begin-multiline-output "" }
+ f<A>(a);
+ ~~~~ ^
+{ dg-end-multiline-output "" }
+
+{ dg-begin-multiline-output "" }
+ template<class T> void f(typename T::type) { }
+ ^
+{ dg-end-multiline-output "" }
+*/
@@ -1,5 +1,6 @@
// PR c++/46001
// { dg-do compile }
+// { dg-options "-fdiagnostics-show-caret" }
struct S
{
@@ -10,4 +11,9 @@ struct S
struct S s;
void *a = s.p | s.f; // { dg-error "unsigned char:1" }
+/* { dg-begin-multiline-output "" }
+ void *a = s.p | s.f;
+ ~~~ ^ ~~~
+ { dg-end-multiline-output "" } */
+
// { dg-bogus "__java_boolean" "" { target *-*-* } 11 }
new file mode 100644
@@ -0,0 +1,21 @@
+// Verify that we can print range information that spans
+// multiple source lines.
+
+// { dg-options "-fdiagnostics-show-caret" }
+
+struct s {};
+extern int first_function_with_a_very_long_name ();
+extern s second_function_with_a_very_long_name ();
+
+int test()
+{
+ return (first_function_with_a_very_long_name ()
+ + second_function_with_a_very_long_name ()); // { dg-error "no match" }
+}
+
+/* { dg-begin-multiline-output "" }
+ return (first_function_with_a_very_long_name ()|
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+type 'int'
+ + second_function_with_a_very_long_name ()); |
+ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type 's'
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,64 @@
+// Verify that we can print range information that spans
+// multiple source lines, where the ranges themselves
+// are multiline
+
+// { dg-options "-fdiagnostics-show-caret" }
+
+struct s {};
+extern int first_function_with_a_very_long_name
+ (int lorem, int ipsum, int dolor, int sit, int amet, int consectetur,
+ int adipiscing, int elit, int sed, int eiusmod, int tempor,
+ int incididunt, int ut, int labore, int et, int dolore, int magna,
+ int aliqua);
+extern s second_function_with_a_very_long_name
+ (int lorem, int ipsum, int dolor, int sit, int amet, int consectetur,
+ int adipiscing, int elit, int sed, int eiusmod, int tempor,
+ int incididunt, int ut, int labore, int et, int dolore, int magna,
+ int aliqua);
+
+int test (int lorem, int ipsum, int dolor, int sit, int amet, int consectetur,
+ int adipiscing, int elit, int sed, int eiusmod, int tempor,
+ int incididunt, int ut, int labore, int et, int dolore, int magna,
+ int aliqua)
+{
+ /* The next line deliberately contains trailing whitespace. */
+ return (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-error "no match" }
+ amet, consectetur,
+ adipiscing, elit, sed,
+ eiusmod, tempor, incididunt,
+ ut, labore, et, dolore,
+ magna, aliqua));
+}
+
+/* Note that the carets under the first line of the RHS of the expression
+ appear longer than they should: the quotes source line contains a dg
+ directive, which we can't quote in full. The carets underline it.
+{ dg-begin-multiline-output "" }
+ return (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,|
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+ consectetur, adipiscing, elit, |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
+ sed, eiusmod, tempor, |
+ ~~~~~~~~~~~~~~~~~~~~~ +type 'int'
+ incididunt, ut, labore, et, |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
+ dolore, magna, aliqua) |
+ ~~~~~~~~~~~~~~~~~~~~~~ |
+ + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, |
+ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+ amet, consectetur, |
+ ~~~~~~~~~~~~~~~~~~ |
+ adipiscing, elit, sed, |
+ ~~~~~~~~~~~~~~~~~~~~~~ +type 's'
+ eiusmod, tempor, incididunt, |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
+ ut, labore, et, dolore, |
+ ~~~~~~~~~~~~~~~~~~~~~~~ |
+ magna, aliqua)); |
+ ~~~~~~~~~~~~~~ |
+{ dg-end-multiline-output "" } */
@@ -1,4 +1,15 @@
// Origin PR c++/51427
+// { dg-options "-fdiagnostics-show-caret" }
typedef struct _GMutex GMutex; // { dg-message "previously declared here"}
typedef union _GMutex GMutex; // { dg-error "tag used in naming" }
+
+/* { dg-begin-multiline-output "" }
+ typedef union _GMutex GMutex;
+ ^~~~~~~
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ typedef struct _GMutex GMutex;
+ ^~~~~~~
+ { dg-end-multiline-output "" } */
@@ -1,4 +1,5 @@
// PR c++/17763
+// { dg-options "-fdiagnostics-show-caret" }
template <typename U> struct Outer {
struct Inner {};
@@ -12,3 +13,8 @@ int main() {
Outer<int> ab;
ab.foo() == 1; // { dg-error "operand types are 'Outer<int>::Inner' and 'int'" }
}
+
+/* { dg-begin-multiline-output "" }
+ ab.foo() == 1;
+ ~~~~~~~~ ^ ~
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,24 @@
+/* Adapted from https://gcc.gnu.org/wiki/ClangDiagnosticsComparison */
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+void foo(char **argP, char **argQ)
+{
+ (argP - argQ)(); /* { dg-error "called object is not a function or function pointer" } */
+ argP(); /* { dg-error "called object 'argP' is not a function or function pointer" } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+ (argP - argQ)();
+ ^~~~~~~~~~~~~
+{ dg-end-multiline-output "" }
+
+{ dg-begin-multiline-output "" }
+ argP();
+ ^~~~
+{ dg-end-multiline-output "" }
+
+{ dg-begin-multiline-output "" }
+ void foo(char **argP, char **argQ)
+ ^~~~
+{ dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,16 @@
+/* Adapted from https://gcc.gnu.org/wiki/ClangDiagnosticsComparison */
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+typedef float __m128 __attribute__ ((vector_size (32)));
+void f()
+{
+ __m128 myvec[2];
+ int const *P;
+ myvec[1]/P; /* { dg-error "invalid operands to binary /" } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+ myvec[1]/P;
+ ~~~~~~~~^~
+{ dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,24 @@
+/* Adapted from https://gcc.gnu.org/wiki/ClangDiagnosticsComparison */
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+#define MYMAX(A,B) __extension__ ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; }) /* { dg-error "invalid operands to binary < \\(have 'struct mystruct' and 'float'\\)" } */
+
+struct mystruct { int i; };
+void f() {
+ int X;
+ float F;
+ struct mystruct P;
+ X = MYMAX(P, F); /* { dg-message "in expansion of macro 'MYMAX'" } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+ #define MYMAX(A,B) __extension__ ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; })
+ ~~~ ^ ~~~
+{ dg-end-multiline-output "" }
+
+{ dg-begin-multiline-output "" }
+ X = MYMAX(P, F);
+ ^~~~~~~~~~~
+{ dg-end-multiline-output "" }
+*/
@@ -1,6 +1,7 @@
/* Copyright (C) 2001 Free Software Foundation, Inc. */
/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-show-caret" } */
/* Source: Neil Booth, Oct 22 2001. PR 150 - warn about division by
zero. */
@@ -19,3 +20,18 @@ int main (int argc, char *argv[])
return 0;
}
+
+/* { dg-begin-multiline-output "" }
+ int w = argc % ZERO;
+ ^ ~~~~
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ int x = argc / 0;
+ ^ ~
+ { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ int y = argc / ZERO;
+ ^ ~~~~
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,15 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+
+void foo (const char *msg)
+{
+ printf("hello %i", msg); /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+ printf("hello %i", msg);
+ ^~
+{ dg-end-multiline-output "" }
+*/
new file mode 100644
@@ -0,0 +1,18 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+extern int printf (__const char *__restrict __format, ...);
+void f(void) {
+ printf ("before the fmt specifier"
+ "%" /* { dg-warning "format '%d' expects a matching 'int' argument" } */
+ "d"
+ "after the fmt specifier");
+}
+
+/*
+{ dg-begin-multiline-output "" }
+ "%"
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ "d"
+ ~~
+{ dg-end-multiline-output "" }
+*/
new file mode 100644
@@ -0,0 +1,15 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+
+void foo(void)
+{
+ printf("hello world %"); /* { dg-warning "spurious trailing" } */
+}
+
+/*
+{ dg-begin-multiline-output "" }
+ printf("hello world %");
+ ^
+{ dg-end-multiline-output "" }
+*/
@@ -29,6 +29,7 @@ load_lib libgloss.exp
load_lib target-libpath.exp
load_lib torture-options.exp
load_lib fortran-modules.exp
+load_lib multiline.exp
# We set LC_ALL and LANG to C so that we get the same error messages as expected.
setenv LC_ALL C
new file mode 100644
@@ -0,0 +1,240 @@
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Testing of multiline output
+
+# We have pre-existing testcases like this:
+# |typedef struct _GMutex GMutex; // { dg-message "previously declared here"}
+# (using "|" here to indicate the start of a line),
+# generating output like this:
+# |gcc/testsuite/g++.dg/diagnostic/wrong-tag-1.C:4:16: note: 'struct _GMutex' was previously declared here
+# where the location of the dg-message determines the expected line at
+# which the error should be reported.
+#
+# To handle rich error-reporting, we want to be able to verify that we
+# get output like this:
+# |gcc/testsuite/g++.dg/diagnostic/wrong-tag-1.C:4:16: note: 'struct _GMutex' was previously declared here
+# | typedef struct _GMutex GMutex; // { dg-message "previously declared here"}
+# | ^~~~~~~
+# where the compiler's first line of output is as before, but in
+# which it then echoes the source lines, adding annotations.
+#
+# We want to be able to write testcases that verify that the
+# emitted source-and-annotations are sane.
+#
+# A complication here is that the source lines contain comments
+# containing DejaGnu directives (such as the "dg-message" above).
+#
+# We punt this somewhat by only matching the beginnings of lines.
+# so that we can write e.g.
+# |/* { dg-begin-multiline-output "" }
+# | typedef struct _GMutex GMutex;
+# | ^~~~~~~
+# | { dg-end-multiline-output "" } */
+# to have the testsuite verify the expected output.
+
+############################################################################
+# Global variables. Although global, these are intended to only be used from
+# within multiline.exp.
+############################################################################
+
+# The line number of the last dg-begin-multiline-output directive.
+set _multiline_last_beginning_line -1
+
+# A list of lists of strings.
+set _multiline_expected_outputs []
+
+############################################################################
+# Exported functions.
+############################################################################
+
+# Mark the beginning of an expected multiline output
+# All lines between this and the next dg-end-multiline-output are
+# expected to be seen.
+
+proc dg-begin-multiline-output { args } {
+ global _multiline_last_beginning_line
+ verbose "dg-begin-multiline-output: args: $args" 3
+ set line [expr [lindex $args 0] + 1]
+ set _multiline_last_beginning_line $line
+}
+
+# Mark the end of an expected multiline output
+# All lines up to here since the last dg-begin-multiline-output are
+# expected to be seen.
+
+proc dg-end-multiline-output { args } {
+ global _multiline_last_beginning_line
+ verbose "dg-end-multiline-output: args: $args" 3
+ set line [expr [lindex $args 0] - 1]
+ verbose "multiline output lines: $_multiline_last_beginning_line-$line" 3
+
+ upvar 1 prog prog
+ verbose "prog: $prog" 3
+ # "prog" now contains the filename
+ # Load it and split it into lines
+
+ set lines [_get_lines $prog $_multiline_last_beginning_line $line]
+ set _multiline_last_beginning_line -1
+
+ verbose "lines: $lines" 3
+ global _multiline_expected_outputs
+ lappend _multiline_expected_outputs $lines
+ verbose "within dg-end-multiline-output: _multiline_expected_outputs: $_multiline_expected_outputs" 3
+}
+
+# Hook to be called by prune.exp's prune_gcc_output to
+# look for the expected multiline outputs, pruning them,
+# reporting PASS for those that are found, and FAIL for
+# those that weren't found.
+#
+# It returns a pruned version of its output.
+#
+# It also clears the list of expected multiline outputs.
+
+proc handle-multiline-outputs { text } {
+ global _multiline_expected_outputs
+ set index 0
+ foreach multiline $_multiline_expected_outputs {
+ verbose " multiline: $multiline" 4
+ set rexp [_build_multiline_regex $multiline $index]
+ verbose "rexp: ${rexp}" 4
+ # Escape newlines in $rexp so that we can print them in
+ # pass/fail results.
+ set escaped_regex [string map {"\n" "\\n"} $rexp]
+ verbose "escaped_regex: ${escaped_regex}" 4
+
+ # Use "regsub" to attempt to prune the pattern from $text
+ if {[regsub -line $rexp $text "" text]} {
+ # Success; the multiline pattern was pruned.
+ pass "expected multiline pattern $index was found: \"$escaped_regex\""
+ } else {
+ fail "expected multiline pattern $index not found: \"$escaped_regex\""
+ }
+
+ set index [expr $index + 1]
+ }
+
+ # Clear the list of expected multiline outputs
+ set _multiline_expected_outputs []
+
+ return $text
+}
+
+############################################################################
+# Internal functions
+############################################################################
+
+# Load FILENAME and extract the lines from FIRST_LINE
+# to LAST_LINE (inclusive) as a list of strings.
+
+proc _get_lines { filename first_line last_line } {
+ verbose "_get_lines" 3
+ verbose " filename: $filename" 3
+ verbose " first_line: $first_line" 3
+ verbose " last_line: $last_line" 3
+
+ set fp [open $filename r]
+ set file_data [read $fp]
+ close $fp
+ set data [split $file_data "\n"]
+ set linenum 1
+ set lines []
+ foreach line $data {
+ verbose "line $linenum: $line" 4
+ if { $linenum >= $first_line && $linenum <= $last_line } {
+ lappend lines $line
+ }
+ set linenum [expr $linenum + 1]
+ }
+
+ return $lines
+}
+
+# Convert $multiline from a list of strings to a multiline regex
+# We need to support matching arbitrary followup text on each line,
+# to deal with comments containing containing DejaGnu directives.
+
+proc _build_multiline_regex { multiline index } {
+ verbose "_build_multiline_regex: $multiline $index" 4
+
+ set rexp ""
+ foreach line $multiline {
+ verbose " line: $line" 4
+
+ # We need to escape "^" and other regexp metacharacters.
+ set line [string map {"^" "\\^"
+ "(" "\\("
+ ")" "\\)"
+ "[" "\\["
+ "]" "\\]"
+ "." "\\."
+ "?" "\\?"
+ "+" "\\+"
+ "*" "\\*"
+ "|" "\\|"} $line]
+
+ append rexp $line
+ if {[string match "*^" $line] || [string match "*~" $line]} {
+ # Assume a line containing a caret/range. This must be
+ # an exact match.
+ } elseif {[string match "*\\|" $line]} {
+ # Assume a source line with a right-margin. Support
+ # arbitrary text in place of any whitespace before the
+ # right-margin, to deal with comments containing containing
+ # DejaGnu directives.
+
+ # Remove final "\|":
+ set rexp [string range $rexp 0 [expr [string length $rexp] - 3]]
+
+ # Trim off trailing whitespace:
+ set old_length [string length $rexp]
+ set rexp [string trimright $rexp]
+ set new_length [string length $rexp]
+
+ # Replace the trimmed whitespace with "." chars to match anything:
+ set ws [string repeat "." [expr $old_length - $new_length]]
+ set rexp "${rexp}${ws}"
+
+ # Add back the trailing '\|':
+ set rexp "${rexp}\\|"
+ } else {
+ # Assume that we have a quoted source line.
+ # Support arbitrary followup text on each line,
+ # to deal with comments containing containing DejaGnu
+ # directives.
+ append rexp ".*"
+ }
+ append rexp "\n"
+ }
+
+ # dg.exp's dg-test trims leading whitespace from the output
+ # in this line:
+ # set comp_output [string trimleft $comp_output]
+ # so we can't rely on the exact leading whitespace for the
+ # first line in the *first* multiline regex.
+ #
+ # Trim leading whitespace from the regexp, replacing it with
+ # a "\s*", to match zero or more whitespace characters.
+ if { $index == 0 } {
+ set rexp [string trimleft $rexp]
+ set rexp "\\s*$rexp"
+ }
+
+ verbose "rexp: $rexp" 4
+
+ return $rexp
+}
@@ -16,6 +16,8 @@
# Prune messages from gcc that aren't useful.
+load_lib multiline.exp
+
if ![info exists TEST_ALWAYS_FLAGS] {
set TEST_ALWAYS_FLAGS ""
}
@@ -68,6 +70,9 @@ proc prune_gcc_output { text } {
# Ignore harmless warnings from Xcode 4.0.
regsub -all "(^|\n)\[^\n\]*ld: warning: could not create compact unwind for\[^\n\]*" $text "" text
+ # Call into multiline.exp to handle any multiline output directives.
+ set text [handle-multiline-outputs $text]
+
#send_user "After:$text\n"
return $text
@@ -48,7 +48,7 @@ void
diagnostic_report_current_function (diagnostic_context *context,
diagnostic_info *diagnostic)
{
- diagnostic_report_current_module (context, diagnostic->location);
+ diagnostic_report_current_module (context, diagnostic->richloc->get_loc ());
lang_hooks.print_error_function (context, LOCATION_FILE (input_location),
diagnostic);
}
@@ -153,7 +153,7 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
first macro which expansion triggered this trace was expanded
inside a system header. */
int saved_location_line =
- expand_location_to_spelling_point (diagnostic->location).line;
+ expand_location_to_spelling_point (diagnostic->richloc->get_loc ()).line;
if (!LINEMAP_SYSP (map))
FOR_EACH_VEC_ELT (loc_vec, ix, iter)
@@ -226,9 +226,12 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
MACRO_MAP_EXPANSION_POINT_LOCATION (iter->map),
LRK_MACRO_DEFINITION_LOCATION, NULL);
- diagnostic_append_note (context, resolved_exp_loc,
- "in expansion of macro %qs",
- linemap_map_get_macro_name (iter->map));
+ rich_location richloc (resolved_exp_loc);
+ richloc.add_range_for_macro (iter->map);
+ diagnostic_append_note_at_rich_loc
+ (context, &richloc,
+ "in expansion of macro %qs",
+ linemap_map_get_macro_name (iter->map));
}
loc_vec.release ();
@@ -252,7 +255,7 @@ virt_loc_aware_diagnostic_finalizer (diagnostic_context *context,
diagnostic_info *diagnostic)
{
maybe_unwind_expanded_macro_loc (context, diagnostic,
- diagnostic->location);
+ diagnostic->richloc->get_loc ());
}
/* Default tree printer. Handles declarations only. */
new file mode 100644
@@ -0,0 +1,5 @@
+TODO:
+ * eliminate the "oracle"
+ * how to store efficiently?
+ * selftests
+ * eliminate this file, or move to official docs