@@ -754,7 +754,7 @@ static const format_char_info gcc_cxxdiag_char_table[] =
/* Custom conversion specifiers. */
/* These will require a "tree" at runtime. */
- { "ADEFKSTVX",0,STD_C89,{ T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+#", "", NULL },
+ { "ADEFHIKSTVX",0,STD_C89,{ T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+#", "", NULL },
{ "v", 0,STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q#", "", NULL },
@@ -1342,6 +1342,10 @@ fdefault-inline
C++ ObjC++ Ignore
Does nothing. Preserved for backward compatibility.
+fdiagnostics-show-template-tree
+C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0)
+Print hierarchical comparisons when template types are mismatched.
+
fdirectives-only
C ObjC C++ ObjC++
Preprocess directives only.
@@ -1361,6 +1365,10 @@ Write all declarations as Ada code for the given file only.
felide-constructors
C++ ObjC++ Var(flag_elide_constructors) Init(1)
+felide-type
+C++ ObjC++ Var(flag_elide_type) Init(1)
+-fno-elide-type Do not elide common elements in template comparisons.
+
fenforce-eh-specs
C++ ObjC++ Var(flag_enforce_eh_specs) Init(1)
Generate code to check exception specifications.
@@ -28,7 +28,7 @@ along with GCC; see the file COPYING3. If not see
#include "c-objc-common.h"
static bool c_tree_printer (pretty_printer *, text_info *, const char *,
- int, bool, bool, bool);
+ int, bool, bool, bool, const char **);
bool
c_missing_noreturn_ok_p (tree decl)
@@ -75,7 +75,8 @@ c_objc_common_init (void)
diagnostic machinery. */
static bool
c_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
- int precision, bool wide, bool set_locus, bool hash)
+ int precision, bool wide, bool set_locus, bool hash,
+ const char **)
{
tree t = NULL_TREE;
tree name;
@@ -10099,7 +10099,7 @@ perform_implicit_conversion_flags (tree type, tree expr,
else if (invalid_nonstatic_memfn_p (loc, expr, complain))
/* We gave an error. */;
else
- error_at (loc, "could not convert %qE from %qT to %qT", expr,
+ error_at (loc, "could not convert %qE from %H to %I", expr,
TREE_TYPE (expr), type);
}
expr = error_mark_node;
@@ -99,7 +99,47 @@ static void cp_diagnostic_starter (diagnostic_context *, diagnostic_info *);
static void cp_print_error_function (diagnostic_context *, diagnostic_info *);
static bool cp_printer (pretty_printer *, text_info *, const char *,
- int, bool, bool, bool);
+ int, bool, bool, bool, const char **);
+
+/* Struct for handling %H or %I, which require delaying printing the
+ type until a postprocessing stage. */
+
+struct deferred_printed_type
+{
+ deferred_printed_type ()
+ : m_tree (NULL_TREE), m_buffer_ptr (NULL), m_verbose (false)
+ {}
+
+ deferred_printed_type (tree type, const char **buffer_ptr, bool verbose)
+ : m_tree (type), m_buffer_ptr (buffer_ptr), m_verbose (verbose)
+ {
+ gcc_assert (type);
+ gcc_assert (buffer_ptr);
+ }
+
+ /* The tree is not GTY-marked: they are only non-NULL within a
+ call to pp_format. */
+ tree m_tree;
+ const char **m_buffer_ptr;
+ bool m_verbose;
+};
+
+/* Subclass of format_postprocessor for the C++ frontend.
+ This handles the %H and %I formatting codes, printing them
+ in a postprocessing phase (since they affect each other). */
+
+class cxx_format_postprocessor : public format_postprocessor
+{
+ public:
+ cxx_format_postprocessor ()
+ : m_type_a (), m_type_b ()
+ {}
+
+ void handle (pretty_printer *pp) FINAL OVERRIDE;
+
+ deferred_printed_type m_type_a;
+ deferred_printed_type m_type_b;
+};
/* CONTEXT->printer is a basic pretty printer that was constructed
presumably by diagnostic_initialize(), called early in the
@@ -123,6 +163,7 @@ cxx_initialize_diagnostics (diagnostic_context *context)
diagnostic_starter (context) = cp_diagnostic_starter;
/* diagnostic_finalizer is already c_diagnostic_finalizer. */
diagnostic_format_decoder (context) = cp_printer;
+ pp->m_format_postprocessor = new cxx_format_postprocessor ();
}
/* Dump a scope, if deemed necessary. */
@@ -3565,6 +3606,370 @@ maybe_print_constexpr_context (diagnostic_context *context)
}
}
+
+/* Return true iff TYPE_A and TYPE_B are template types that are
+ meaningful to compare. */
+
+static bool
+comparable_template_types_p (tree type_a, tree type_b)
+{
+ if (TREE_CODE (type_a) != RECORD_TYPE)
+ return false;
+ if (TREE_CODE (type_b) != RECORD_TYPE)
+ return false;
+
+ tree tinfo_a = TYPE_TEMPLATE_INFO (type_a);
+ tree tinfo_b = TYPE_TEMPLATE_INFO (type_b);
+ if (!tinfo_a || !tinfo_b)
+ return false;
+
+ return TI_TEMPLATE (tinfo_a) == TI_TEMPLATE (tinfo_b);
+}
+
+/* Start a new line indented by SPC spaces on PP. */
+
+static void
+newline_and_indent (pretty_printer *pp, int spc)
+{
+ pp_newline (pp);
+ for (int i = 0; i < spc; i++)
+ pp_space (pp);
+}
+
+/* Generate a GC-allocated string for ARG, an expression or type. */
+
+static const char *
+arg_to_string (tree arg, bool verbose)
+{
+ if (EXPR_P (arg))
+ return expr_to_string (arg);
+ return type_to_string (arg, verbose);
+}
+
+/* Subroutine to type_to_string_with_compare and
+ print_template_tree_comparison.
+
+ Print a representation of ARG (an expression or type) to PP,
+ colorizing it as "type-diff" if PP->show_color. */
+
+static void
+print_nonequal_arg (pretty_printer *pp, tree arg, bool verbose)
+{
+ pp_printf (pp, "%r%s%R",
+ "type-diff",
+ (arg
+ ? arg_to_string (arg, verbose)
+ : G_("(no argument)")));
+}
+
+/* As type_to_string, but for a template, potentially colorizing/eliding
+ in comparison with PEER.
+ For example, if TYPE is map<int,double> and PEER is map<int,int>,
+ then the resulting string would be:
+ map<[...],double>
+ with type elision, and:
+ map<int,double>
+ without type elision.
+
+ In both cases the parts of TYPE that differ from PEER will be colorized
+ if SHOW_COLOR is true. In the above example, this would be "double".
+
+ Only non-default template arguments are printed.
+
+ The resulting string is in a GC-allocated buffer. */
+
+static const char *
+type_to_string_with_compare (tree type, tree peer, bool verbose,
+ bool show_color)
+{
+ pretty_printer inner_pp;
+ pretty_printer *pp = &inner_pp;
+ pp_show_color (pp) = show_color;
+
+ tree tinfo = TYPE_TEMPLATE_INFO (type);
+ tree tinfo_peer = TYPE_TEMPLATE_INFO (peer);
+
+ pp_printf (pp, "%s<",
+ IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo))));
+ tree args_a = TI_ARGS (tinfo);
+ tree args_b = TI_ARGS (tinfo_peer);
+ gcc_assert (TREE_CODE (args_a) == TREE_VEC);
+ gcc_assert (TREE_CODE (args_b) == TREE_VEC);
+ int flags = 0;
+ int len_a = get_non_default_template_args_count (args_a, flags);
+ args_a = INNERMOST_TEMPLATE_ARGS (args_a);
+ int len_b = get_non_default_template_args_count (args_b, flags);
+ args_b = INNERMOST_TEMPLATE_ARGS (args_b);
+ int len_max = MAX (len_a, len_b);
+ for (int idx = 0; idx < len_max; idx++)
+ {
+ tree arg_a = idx < len_a ? TREE_VEC_ELT (args_a, idx) : NULL;
+ tree arg_b = idx < len_b ? TREE_VEC_ELT (args_b, idx) : NULL;
+ if (arg_a == arg_b)
+ {
+ if (flag_elide_type)
+ pp_string (pp, G_("[...]"));
+ else
+ pp_string (pp, arg_to_string (arg_a, verbose));
+ }
+ else
+ {
+ if (comparable_template_types_p (arg_a, arg_b))
+ pp_string (pp, type_to_string_with_compare (arg_a, arg_b, verbose,
+ show_color));
+ else
+ print_nonequal_arg (pp, arg_a, verbose);
+ }
+ if (idx + 1 < len_max)
+ pp_character (pp, ',');
+ }
+ pp_printf (pp, ">");
+ return pp_ggc_formatted_text (pp);
+}
+
+/* Recursively print a tree-like comparison of TYPE_A and TYPE_B to PP,
+ indented by INDENT spaces.
+
+ For example given types:
+
+ vector<map<int,double>>
+
+ and
+
+ vector<map<double,float>>
+
+ the output with type elision would be:
+
+ vector<
+ map<
+ [...],
+ [double != float]>>
+
+ and without type-elision would be:
+
+ vector<
+ map<
+ int,
+ [double != float]>>
+
+ TYPE_A and TYPE_B must both be comparable template types
+ (as per comparable_template_types_p).
+
+ Only non-default template arguments are printed. */
+
+static void
+print_template_tree_comparison (pretty_printer *pp, tree type_a, tree type_b,
+ bool verbose, int indent)
+{
+ tree tinfo_a = TYPE_TEMPLATE_INFO (type_a);
+ tree tinfo_b = TYPE_TEMPLATE_INFO (type_b);
+
+ newline_and_indent (pp, indent);
+ pp_printf (pp, "%s<",
+ IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a))));
+ tree args_a = TI_ARGS (tinfo_a);
+ tree args_b = TI_ARGS (tinfo_b);
+ int flags = 0;
+ int len_a = get_non_default_template_args_count (args_a, flags);
+ args_a = INNERMOST_TEMPLATE_ARGS (args_a);
+ int len_b = get_non_default_template_args_count (args_b, flags);
+ args_b = INNERMOST_TEMPLATE_ARGS (args_b);
+ int len_max = MAX (len_a, len_b);
+ gcc_assert (TREE_CODE (args_a) == TREE_VEC);
+ gcc_assert (TREE_CODE (args_b) == TREE_VEC);
+ for (int idx = 0; idx < len_max; idx++)
+ {
+ tree arg_a = idx < len_a ? TREE_VEC_ELT (args_a, idx) : NULL;
+ tree arg_b = idx < len_b ? TREE_VEC_ELT (args_b, idx) : NULL;
+ if (arg_a == arg_b)
+ {
+ newline_and_indent (pp, indent + 2);
+ /* Can do elision here, printing "[...]". */
+ if (flag_elide_type)
+ pp_string (pp, G_("[...]"));
+ else
+ pp_string (pp, arg_to_string (arg_a, verbose));
+ }
+ else
+ {
+ if (comparable_template_types_p (arg_a, arg_b))
+ print_template_tree_comparison (pp, arg_a, arg_b, verbose,
+ indent + 2);
+ else
+ {
+ newline_and_indent (pp, indent + 2);
+ pp_character (pp, '[');
+ print_nonequal_arg (pp, arg_a, verbose);
+ pp_string (pp, " != ");
+ print_nonequal_arg (pp, arg_b, verbose);
+ pp_character (pp, ']');
+ }
+ }
+ if (idx + 1 < len_max)
+ pp_character (pp, ',');
+ }
+ pp_printf (pp, ">");
+}
+
+/* Subroutine for use in a format_postprocessor::handle
+ implementation. Adds a chunk to the end of
+ formatted output, so that it will be printed
+ by pp_output_formatted_text. */
+
+static void
+append_formatted_chunk (pretty_printer *pp, const char *content)
+{
+ output_buffer *buffer = pp_buffer (pp);
+ struct chunk_info *chunk_array = buffer->cur_chunk_array;
+ const char **args = chunk_array->args;
+
+ unsigned int chunk_idx;
+ for (chunk_idx = 0; args[chunk_idx]; chunk_idx++)
+ ;
+ args[chunk_idx++] = content;
+ args[chunk_idx] = NULL;
+}
+
+/* Create a copy of CONTENT, with quotes added, and,
+ potentially, with colorization.
+ No escaped is performed on CONTENT.
+ The result is in a GC-allocated buffer. */
+
+static const char *
+add_quotes (const char *content, bool show_color)
+{
+ pretty_printer tmp_pp;
+ pp_show_color (&tmp_pp) = show_color;
+
+ /* We have to use "%<%s%>" rather than "%qs" here in order to avoid
+ quoting colorization bytes within the results. */
+ pp_printf (&tmp_pp, "%<%s%>", content);
+
+ return pp_ggc_formatted_text (&tmp_pp);
+}
+
+/* If we had %H and %I, and hence deferred printing them,
+ print them now, storing the result into the chunk_info
+ for pp_format.
+ Also print the difference in tree form, adding it as
+ an additional chunk. */
+
+void
+cxx_format_postprocessor::handle (pretty_printer *pp)
+{
+ /* If we have one of %H and %I, the other should have
+ been present. */
+ if (m_type_a.m_tree || m_type_b.m_tree)
+ {
+ gcc_assert (m_type_a.m_tree);
+ gcc_assert (m_type_b.m_tree);
+ }
+
+ if (m_type_a.m_tree && m_type_b.m_tree)
+ {
+ /* Avoid reentrancy issues by working with a copy of
+ m_type_a and m_type_b, resetting them now. */
+ deferred_printed_type type_a = m_type_a;
+ deferred_printed_type type_b = m_type_b;
+ m_type_a = deferred_printed_type ();
+ m_type_b = deferred_printed_type ();
+
+ gcc_assert (type_a.m_tree);
+ gcc_assert (type_a.m_buffer_ptr);
+ gcc_assert (type_b.m_tree);
+ gcc_assert (type_b.m_buffer_ptr);
+
+ bool show_color = pp_show_color (pp);
+
+ const char *type_a_text;
+ const char *type_b_text;
+
+ if (comparable_template_types_p (type_a.m_tree, type_b.m_tree))
+ {
+ type_a_text
+ = type_to_string_with_compare (type_a.m_tree, type_b.m_tree,
+ type_a.m_verbose, show_color);
+ type_b_text
+ = type_to_string_with_compare (type_b.m_tree, type_a.m_tree,
+ type_b.m_verbose, show_color);
+
+ if (flag_diagnostics_show_template_tree)
+ {
+ pretty_printer inner_pp;
+ pp_show_color (&inner_pp) = pp_show_color (pp);
+ print_template_tree_comparison
+ (&inner_pp, type_a.m_tree, type_b.m_tree, type_a.m_verbose, 2);
+ append_formatted_chunk (pp, pp_ggc_formatted_text (&inner_pp));
+ }
+ }
+ else
+ {
+ /* If the types were not comparable, they are printed normally,
+ and no difference tree is printed. */
+ type_a_text = type_to_string (type_a.m_tree, type_a.m_verbose);
+ type_b_text = type_to_string (type_b.m_tree, type_b.m_verbose);
+ }
+
+ *type_a.m_buffer_ptr = add_quotes (type_a_text, show_color);
+ *type_b.m_buffer_ptr = add_quotes (type_b_text, show_color);
+ }
+}
+
+/* Subroutine for handling %H and %I, to support i18n of messages like:
+
+ error_at (loc, "could not convert %qE from %H to %I",
+ expr, type_a, type_b);
+
+ so that we can print things like:
+
+ could not convert 'foo' from 'map<int,double>' to 'map<int,int>'
+
+ and, with type-elision:
+
+ could not convert 'foo' from 'map<[...],double>' to 'map<[...],int>'
+
+ (with color-coding of the differences between the types).
+
+ The %H and %I format codes are peers: both must be present,
+ and they affect each other. Hence to handle them, we must
+ delay printing until we have both, deferring the printing to
+ pretty_printer's m_format_postprocessor hook.
+
+ This is called in phase 2 of pp_format, when it is accumulating
+ a series of formatted chunks. We stash the location of the chunk
+ we're meant to have written to, so that we can write to it in the
+ m_format_postprocessor hook. */
+
+static void
+defer_half_of_type_diff (pretty_printer *pp, char spec, tree type,
+ const char **buffer_ptr, bool verbose)
+{
+ gcc_assert (pp->m_format_postprocessor);
+ cxx_format_postprocessor *postprocessor
+ = static_cast <cxx_format_postprocessor *> (pp->m_format_postprocessor);
+
+ switch (spec)
+ {
+ case 'H':
+ gcc_assert (postprocessor->m_type_a.m_tree == NULL_TREE);
+ gcc_assert (postprocessor->m_type_a.m_buffer_ptr == NULL);
+ postprocessor->m_type_a
+ = deferred_printed_type (type, buffer_ptr, verbose);
+ break;
+
+ case 'I':
+ gcc_assert (postprocessor->m_type_b.m_tree == NULL_TREE);
+ gcc_assert (postprocessor->m_type_b.m_buffer_ptr == NULL);
+ postprocessor->m_type_b
+ = deferred_printed_type (type, buffer_ptr, verbose);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+
/* Called from output_format -- during diagnostic message processing --
to handle C++ specific format specifier with the following meanings:
%A function argument-list.
@@ -3579,10 +3984,13 @@ maybe_print_constexpr_context (diagnostic_context *context)
%S substitution (template + args)
%T type.
%V cv-qualifier.
- %X exception-specification. */
+ %X exception-specification.
+ %H type difference (from)
+ %I type difference (to). */
static bool
cp_printer (pretty_printer *pp, text_info *text, const char *spec,
- int precision, bool wide, bool set_locus, bool verbose)
+ int precision, bool wide, bool set_locus, bool verbose,
+ const char **buffer_ptr)
{
const char *result;
tree t = NULL;
@@ -3629,6 +4037,13 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
percent_K_format (text);
return true;
+ case 'H':
+ case 'I':
+ {
+ defer_half_of_type_diff (pp, *spec, next_tree, buffer_ptr, verbose);
+ return true;
+ }
+
default:
return false;
}
@@ -174,6 +174,7 @@ static struct color_cap color_dict[] =
{ "diff-hunk", SGR_SEQ (COLOR_FG_CYAN), 9, false },
{ "diff-delete", SGR_SEQ (COLOR_FG_RED), 11, false },
{ "diff-insert", SGR_SEQ (COLOR_FG_GREEN), 11, false },
+ { "type-diff", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 9, false },
{ NULL, NULL, 0, false }
};
@@ -204,8 +205,9 @@ colorize_stop (bool show_color)
/* Parse GCC_COLORS. The default would look like:
GCC_COLORS='error=01;31:warning=01;35:note=01;36:\
range1=32:range2=34:locus=01:quote=01:\
- fixit-insert=32:fixit-delete=31'\
- diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32'
+ fixit-insert=32:fixit-delete=31:'\
+ diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
+ type-diff=01;32'
No character escaping is needed or supported. */
static bool
parse_gcc_colors (void)
@@ -251,6 +251,7 @@ Objective-C and Objective-C++ Dialects}.
-fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]} @gol
-fno-diagnostics-show-option -fno-diagnostics-show-caret @gol
-fdiagnostics-parseable-fixits -fdiagnostics-generate-patch @gol
+-fdiagnostics-show-template-tree -fno-elide-type @gol
-fno-show-column}
@item Warning Options
@@ -3442,7 +3443,8 @@ The default @env{GCC_COLORS} is
@smallexample
error=01;31:warning=01;35:note=01;36:range1=32:range2=34:locus=01:\
quote=01:fixit-insert=32:fixit-delete=31:\
-diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32
+diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
+type-diff=01;32
@end smallexample
@noindent
where @samp{01;31} is bold red, @samp{01;35} is bold magenta,
@@ -3506,6 +3508,11 @@ SGR substring for deleted lines within generated patches.
@item diff-insert=
@vindex diff-insert GCC_COLORS @r{capability}
SGR substring for inserted lines within generated patches.
+
+@item type-diff=
+@vindex type-diff GCC_COLORS @r{capability}
+SGR substring for highlighting mismatching types within template
+arguments in the C++ frontend.
@end table
@item -fno-diagnostics-show-option
@@ -3578,6 +3585,47 @@ are printed. For example:
The diff may or may not be colorized, following the same rules
as for diagnostics (see @option{-fdiagnostics-color}).
+@item -fdiagnostics-show-template-tree
+@opindex fdiagnostics-show-template-tree
+
+In the C++ frontend, when printing diagnostics showing mismatching
+template types, such as:
+
+@smallexample
+ could not convert 'std::map<int, std::vector<double> >()'
+ from 'map<[...],vector<double>>' to 'map<[...],vector<float>>
+@end smallexample
+
+the @option{-fdiagnostics-show-template-tree} flag enables printing a
+tree-like structure showing the common and differing parts of the types,
+such as:
+
+@smallexample
+ map<
+ [...],
+ vector<
+ [double != float]>>
+@end smallexample
+
+The parts that differ are highlighted with color (``double'' and
+``float'' in this case).
+
+@item -fno-elide-type
+@opindex fno-elide-type
+@opindex felide-type
+By default when the C++ frontend prints diagnostics showing mismatching
+template types, common parts of the types are printed as ``[...]'' to
+simplify the error message. For example:
+
+@smallexample
+ could not convert 'std::map<int, std::vector<double> >()'
+ from 'map<[...],vector<double>>' to 'map<[...],vector<float>>
+@end smallexample
+
+Specifying the @option{-fno-elide-type} flag suppresses that behavior.
+This flag also affects the output of the
+@option{-fdiagnostics-show-template-tree} flag.
+
@item -fno-show-column
@opindex fno-show-column
Do not print column numbers in diagnostics. This may be necessary if
@@ -917,7 +917,8 @@ gfc_notify_std (int std, const char *gmsgid, ...)
*/
static bool
gfc_format_decoder (pretty_printer *pp, text_info *text, const char *spec,
- int precision, bool wide, bool set_locus, bool hash)
+ int precision, bool wide, bool set_locus, bool hash,
+ const char **buffer_ptr)
{
switch (*spec)
{
@@ -948,7 +949,7 @@ gfc_format_decoder (pretty_printer *pp, text_info *text, const char *spec,
etc. diagnostics can use the FE printer while the FE is still
active. */
return default_tree_printer (pp, text, spec, precision, wide,
- set_locus, hash);
+ set_locus, hash, buffer_ptr);
}
}
@@ -677,7 +677,8 @@ pp_format (pretty_printer *pp, text_info *text)
gcc_assert (pp_format_decoder (pp));
ok = pp_format_decoder (pp) (pp, text, p,
- precision, wide, plus, hash);
+ precision, wide, plus, hash,
+ formatters[argno]);
gcc_assert (ok);
}
}
@@ -696,6 +697,11 @@ pp_format (pretty_printer *pp, text_info *text)
for (; argno < PP_NL_ARGMAX; argno++)
gcc_assert (!formatters[argno]);
+ /* If the client supplied a postprocessing object, call its "handle"
+ hook here. */
+ if (pp->m_format_postprocessor)
+ pp->m_format_postprocessor->handle (pp);
+
/* Revert to normal obstack and wrapping mode. */
buffer->obstack = &buffer->formatted_obstack;
buffer->line_length = 0;
@@ -847,6 +853,7 @@ pretty_printer::pretty_printer (const char *p, int l)
indent_skip (),
wrapping (),
format_decoder (),
+ m_format_postprocessor (NULL),
emitted_prefix (),
need_newline (),
translate_identifiers (true),
@@ -860,6 +867,8 @@ pretty_printer::pretty_printer (const char *p, int l)
pretty_printer::~pretty_printer ()
{
+ if (m_format_postprocessor)
+ delete m_format_postprocessor;
buffer->~output_buffer ();
XDELETE (buffer);
}
@@ -180,11 +180,20 @@ struct pp_wrapping_mode_t
A client-supplied formatter returns true if everything goes well,
otherwise it returns false. */
typedef bool (*printer_fn) (pretty_printer *, text_info *, const char *,
- int, bool, bool, bool);
+ int, bool, bool, bool, const char **);
/* Client supplied function used to decode formats. */
#define pp_format_decoder(PP) (PP)->format_decoder
+/* Base class for an optional client-supplied object for doing additional
+ processing between stages 2 and 3 of formatted printing. */
+class format_postprocessor
+{
+ public:
+ virtual ~format_postprocessor () {}
+ virtual void handle (pretty_printer *) = 0;
+};
+
/* TRUE if a newline character needs to be added before further
formatting. */
#define pp_needs_newline(PP) (PP)->need_newline
@@ -239,9 +248,16 @@ struct pretty_printer
If the BUFFER needs additional characters from the format string, it
should advance the TEXT->format_spec as it goes. When FORMAT_DECODER
returns, TEXT->format_spec should point to the last character processed.
- */
+ The BUFFER_PTR is passed in, to allow for deferring-handling of format
+ codes (e.g. %H and %I in the C++ frontend). */
printer_fn format_decoder;
+ /* If non-NULL, this is called by pp_format once after all format codes
+ have been processed, to allow for client-specific postprocessing.
+ This is used by the C++ frontend for handling the %H and %I
+ format codes (which interract with each other). */
+ format_postprocessor *m_format_postprocessor;
+
/* Nonzero if current PREFIX was emitted at least once. */
bool emitted_prefix;
@@ -65,6 +65,9 @@ set plugin_test_list [list \
{ def_plugin.c def-plugin-test.C } \
{ ../../gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c \
diagnostic-test-expressions-1.C } \
+ { show_template_tree_color_plugin.c \
+ show-template-tree-color.C \
+ show-template-tree-color-no-elide-type.C } \
]
foreach plugin_test $plugin_test_list {
new file mode 100644
@@ -0,0 +1,30 @@
+/* Verify colorization of the output of -fdiagnostics-show-template-tree,
+ and within the %H and %I format codes.
+ Doing so requires a plugin; see the comments in the plugin for the
+ rationale. */
+
+// { dg-options "-fdiagnostics-show-template-tree -fdiagnostics-color=always -fno-elide-type" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+
+void test ()
+{
+ fn_1 (vector<double> ());
+ /* { dg-begin-multiline-output "" }
+could not convert '[01m[Kvector<double>()[m[K' from '[01m[Kvector<[01;32m[Kdouble[m[K>[m[K' to '[01m[Kvector<[01;32m[Kint[m[K>[m[K'
+ vector<
+ [[01;32m[Kdouble[m[K != [01;32m[Kint[m[K]>
+ { dg-end-multiline-output "" } */
+
+ fn_2 (map<int, double>());
+ /* { dg-begin-multiline-output "" }
+could not convert '[01m[Kmap<int, double>()[m[K' from '[01m[Kmap<int,[01;32m[Kdouble[m[K>[m[K' to '[01m[Kmap<int,[01;32m[Kint[m[K>[m[K'
+ map<
+ int,
+ [[01;32m[Kdouble[m[K != [01;32m[Kint[m[K]>
+ { dg-end-multiline-output "" } */
+}
new file mode 100644
@@ -0,0 +1,30 @@
+/* Verify colorization of the output of -fdiagnostics-show-template-tree,
+ and within the %H and %I format codes.
+ Doing so requires a plugin; see the comments in the plugin for the
+ rationale. */
+
+// { dg-options "-fdiagnostics-show-template-tree -fdiagnostics-color=always" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+
+void test ()
+{
+ fn_1 (vector<double> ());
+ /* { dg-begin-multiline-output "" }
+could not convert '[01m[Kvector<double>()[m[K' from '[01m[Kvector<[01;32m[Kdouble[m[K>[m[K' to '[01m[Kvector<[01;32m[Kint[m[K>[m[K'
+ vector<
+ [[01;32m[Kdouble[m[K != [01;32m[Kint[m[K]>
+ { dg-end-multiline-output "" } */
+
+ fn_2 (map<int, double>());
+ /* { dg-begin-multiline-output "" }
+could not convert '[01m[Kmap<int, double>()[m[K' from '[01m[Kmap<[...],[01;32m[Kdouble[m[K>[m[K' to '[01m[Kmap<[...],[01;32m[Kint[m[K>[m[K'
+ map<
+ [...],
+ [[01;32m[Kdouble[m[K != [01;32m[Kint[m[K]>
+ { dg-end-multiline-output "" } */
+}
new file mode 100644
@@ -0,0 +1,38 @@
+/* We want to verify the colorized output of cxx_format_postprocessor,
+ but turning on colorization for everything confuses "dg-error" etc.
+ The color codes in the generated messages would also need escaping
+ for use within dg-error.
+
+ Hence the simplest approach is to provide a custom diagnostic_starter_fn,
+ which does nothing.
+
+ The resulting messages lack the "FILENAME:LINE:COL: error: " prefix
+ and can thus be tested using dg-begin/end-multiline-output. */
+
+/* { dg-options "-O" } */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+
+int plugin_is_GPL_compatible;
+
+void
+noop_starter_fn (diagnostic_context *, diagnostic_info *)
+{
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+ struct plugin_gcc_version *version)
+{
+ if (!plugin_default_version_check (version, &gcc_version))
+ return 1;
+
+ diagnostic_starter (global_dc) = noop_starter_fn;
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,118 @@
+// Tests of -fdiagnostics-show-template-tree with variadic templates
+// { dg-options "-fdiagnostics-show-template-tree -std=c++11" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+template<typename ... Types> struct var {};
+
+void fn_0(var<>);
+void fn_1(var<int>);
+void fn_2(var<int, int>);
+void fn_3(vector<var<> >);
+void fn_4(vector<var<int> >);
+void fn_5(vector<var<int, int> >);
+
+void test_fn_0 ()
+{
+ fn_0 (var<> ());
+ fn_0 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'var<>'" }
+ /* { dg-begin-multiline-output "" }
+ var<
+ [int != ]>
+ { dg-end-multiline-output "" } */
+ fn_0 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'var<>'" }
+ /* { dg-begin-multiline-output "" }
+ var<
+ [int, int != ]>
+ { dg-end-multiline-output "" } */
+ fn_0 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<>'" }
+ fn_0 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<>'" }
+}
+
+void test_fn_1 ()
+{
+ fn_1 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'var<int>'" }
+ /* { dg-begin-multiline-output "" }
+ var<
+ [ != int]>
+ { dg-end-multiline-output "" } */
+ fn_1 (var<int> ());
+ fn_1 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'var<int>'" }
+ /* { dg-begin-multiline-output "" }
+ var<
+ [int, int != int]>
+ { dg-end-multiline-output "" } */
+ fn_1 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<int>'" }
+ fn_1 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<int>'" }
+}
+
+void test_fn_2 ()
+{
+ fn_2 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'var<int, int>'" }
+ /* { dg-begin-multiline-output "" }
+ var<
+ [ != int, int]>
+ { dg-end-multiline-output "" } */
+ fn_2 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'var<int, int>'" }
+ /* { dg-begin-multiline-output "" }
+ var<
+ [int != int, int]>
+ { dg-end-multiline-output "" } */
+ fn_2 (var<int, int> ());
+ fn_2 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<int, int>'" }
+ fn_2 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<int, int>'" }
+}
+
+void test_fn_3 ()
+{
+ fn_3 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<> >'" }
+ fn_3 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'vector<var<> >'" }
+ fn_3 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<> >'" }
+ fn_3 (vector<var<> >());
+ fn_3 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int>>' to 'vector<var<>>'" }
+ /* { dg-begin-multiline-output "" }
+ vector<
+ var<
+ [int != ]>>
+ { dg-end-multiline-output "" } */
+ fn_3 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int>>' to 'vector<var<>>'" }
+ /* { dg-begin-multiline-output "" }
+ vector<
+ var<
+ [int, int != ]>>
+ { dg-end-multiline-output "" } */
+}
+
+void test_fn_4 ()
+{
+ fn_4 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<int> >'" }
+ fn_4 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'vector<var<int> >'" }
+ fn_4 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<int> >'" }
+ fn_4 (vector<var<> >()); // { dg-error "could not convert .* from 'vector<var<>>' to 'vector<var<int>>'" }
+ /* { dg-begin-multiline-output "" }
+ vector<
+ var<
+ [ != int]>>
+ { dg-end-multiline-output "" } */
+ fn_4 (vector<var<int> >());
+ fn_4 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int>>' to 'vector<var<int>>'" }
+ /* { dg-begin-multiline-output "" }
+ vector<
+ var<
+ [int, int != int]>>
+ { dg-end-multiline-output "" } */
+}
+
+void test_fn_5 ()
+{
+ fn_5 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<int, int> >'" }
+ fn_5 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'vector<var<int, int> >'" }
+ fn_5 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<int, int> >'" }
+ fn_5 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int>>' to 'vector<var<int, int>>'" }
+ /* { dg-begin-multiline-output "" }
+ vector<
+ var<
+ [int != int, int]>>
+ { dg-end-multiline-output "" } */
+ fn_5 (vector<var<int, int> >());
+}
new file mode 100644
@@ -0,0 +1,37 @@
+/* End-to-end test of -fdiagnostics-show-template-tree and -felide-type
+ using the STL.
+ In particular, ensure that we don't print the default arguments e.g.
+ rather than printing
+ from 'vector<double,allocator<double>>' to 'vector<float,allocator<float>>'
+ (albeit with differences nicely color-coded), we want to print:
+ from 'vector<double>' to 'vector<float>'
+ (again, with the "double" and "float" highlighted, though we can't test
+ for that in this case). */
+
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+#include <map>
+#include <vector>
+
+using std::vector;
+using std::map;
+
+void takes_vf (vector<float> v);
+void takes_mivf (map<int, vector<float> > v);
+
+int test ()
+{
+ takes_vf (vector<double> ()); // { dg-error "could not convert '.*' from 'vector<double>' to 'vector<float>'" }
+ /* { dg-begin-multiline-output "" }
+ vector<
+ [double != float]>
+ { dg-end-multiline-output "" } */
+
+ takes_mivf (map<int, vector<double> > ()); // { dg-error "could not convert '.*' from 'map<.\\.\\.\\..,vector<double>>' to 'map<.\\.\\.\\..,vector<float>>'" }
+ /* { dg-begin-multiline-output "" }
+ map<
+ [...],
+ vector<
+ [double != float]>>
+ { dg-end-multiline-output "" } */
+}
new file mode 100644
@@ -0,0 +1,24 @@
+// { dg-options "-fdiagnostics-show-template-tree -fno-elide-type" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+void fn_3(vector<map<int, float> >);
+
+void test ()
+{
+ fn_1 (vector<double> ()); // { dg-error "could not convert .* from 'vector<double>' to 'vector<int>'" }
+ /* { dg-begin-multiline-output "" }
+ vector<
+ [double != int]>
+ { dg-end-multiline-output "" } */
+
+ fn_2 (map<int, double>()); // { dg-error "could not convert .* from 'map<int,double>' to 'map<int,int>'" }
+ /* { dg-begin-multiline-output "" }
+ map<
+ int,
+ [double != int]>
+ { dg-end-multiline-output "" } */
+}
new file mode 100644
@@ -0,0 +1,43 @@
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+void fn_3(vector<map<int, float> >);
+
+void test ()
+{
+ fn_1 (vector<int> ());
+ fn_1 (42); // { dg-error "could not convert '42' from 'int' to 'vector<int>'" }
+ fn_1 (vector<double> ()); // { dg-error "could not convert .* from 'vector<double>' to 'vector<int>'" }
+ /* { dg-begin-multiline-output "" }
+ vector<
+ [double != int]>
+ { dg-end-multiline-output "" } */
+ fn_1 (map<int, int> ()); // { dg-error "could not convert .* from 'map<int, int>' to 'vector<int>'" }
+
+ fn_2 (map<int, int>());
+ fn_2 (map<int, double>()); // { dg-error "could not convert .* from 'map<.\\.\\.\\..,double>. to .map<.\\.\\.\\..,int>'" }
+ /* { dg-begin-multiline-output "" }
+ map<
+ [...],
+ [double != int]>
+ { dg-end-multiline-output "" } */
+ fn_2 (map<double, double>()); // { dg-error "could not convert .* from .map<double,double>. to .map<int,int>." }
+ /* { dg-begin-multiline-output "" }
+ map<
+ [double != int],
+ [double != int]>
+ { dg-end-multiline-output "" } */
+
+ fn_3 (vector<map<int, float> >());
+ fn_3 (vector<map<int, double> >()); // { dg-error "could not convert .* from 'vector<map<.\\.\\.\\..,double>>' to 'vector<map<.\\.\\.\\..,float>>'" }
+ /* { dg-begin-multiline-output "" }
+ vector<
+ map<
+ [...],
+ [double != float]>>
+ { dg-end-multiline-output "" } */
+}
@@ -245,7 +245,8 @@ virt_loc_aware_diagnostic_finalizer (diagnostic_context *context,
/* Default tree printer. Handles declarations only. */
bool
default_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
- int precision, bool wide, bool set_locus, bool hash)
+ int precision, bool wide, bool set_locus, bool hash,
+ const char **)
{
tree t;
@@ -55,6 +55,6 @@ void virt_loc_aware_diagnostic_finalizer (diagnostic_context *,
void tree_diagnostics_defaults (diagnostic_context *context);
bool default_tree_printer (pretty_printer *, text_info *, const char *,
- int, bool, bool, bool);
+ int, bool, bool, bool, const char **);
#endif /* ! GCC_TREE_DIAGNOSTIC_H */