mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-23 10:54:07 +08:00
diagnostics: add support for nested diagnostics [PR116253]
Previously the diagnostic subsystem supported a one-deep hierarchy via auto_diagnostic_group, for associating notes with the warning/error they annotate; this only affects SARIF output, not text output. This patch adds support to the diagnostics subsystem for capturing arbitrarily deep nesting structure within diagnostic messages. This patch: * adds the ability to express nesting internally when building diagnostics * captures the nesting in SARIF output in the form documented in SG15's P3358R0 ("SARIF for Structured Diagnostics") via a "nestingLevel" property * adds a new experimental mode to text output to see the hierarchy, via: -fdiagnostics-set-output=text:experimental-nesting=yes * adds test coverage via a plugin, which with the above option emits: • note: child 0 • note: grandchild 0 0 • note: grandchild 0 1 • note: grandchild 0 2 • note: child 1 • note: grandchild 1 0 • note: grandchild 1 1 • note: grandchild 1 2 • note: child 2 • note: grandchild 2 0 • note: grandchild 2 1 • note: grandchild 2 2 using '*' rather than '•' if the text_art::theme is ascii-only. My hope is to eventually: (a) use this to improve C++'s template diagnostics (b) remove the "experimental" caveat from the the text output mode but this patch doesn't touch the C++ frontend, leaving both of these to followup work. gcc/c-family/ChangeLog: PR other/116253 * c-opts.cc (c_diagnostic_text_finalizer): Use text_output.build_indent_prefix for prefix to diagnostic_show_locus. gcc/ChangeLog: PR other/116253 * diagnostic-core.h (class auto_diagnostic_nesting_level): New. * diagnostic-format-sarif.cc (class sarif_builder): Update leading comment re nesting of diagnostics. (sarif_result::on_nested_diagnostic): Add nestingLevel property. * diagnostic-format-text.cc (on_report_diagnostic): If we're showing nested diagnostics, then print changes of location on a new line, indented, and update m_last_location. (diagnostic_text_output_format::build_prefix): If m_show_nesting, then potentially add indentation and a bullet point. (get_bullet_point_unichar): New. (use_unicode_p): New. (diagnostic_text_output_format::build_indent_prefix): New. * diagnostic-format-text.h (diagnostic_text_output_format::diagnostic_text_output_format): Initialize m_show_nesting and m_show_nesting_levels. (diagnostic_text_output_format::build_indent_prefix): New decl. (diagnostic_text_output_format::show_nesting_p): New accessor (diagnostic_text_output_format::show_locations_in_nesting_p): Likewise. (diagnostic_text_output_format::set_show_nesting): New. (diagnostic_text_output_format::set_show_locations_in_nesting): New. (diagnostic_text_output_format::set_show_nesting_levels): New. (diagnostic_text_output_format::m_show_nesting): New field. (diagnostic_text_output_format::m_show_locations_in_nesting): New field. (diagnostic_text_output_format::m_show_nesting_levels): New field. * diagnostic-global-context.cc (auto_diagnostic_nesting_level::auto_diagnostic_nesting_level): New. (auto_diagnostic_nesting_level::~auto_diagnostic_nesting_level): New. * diagnostic-show-locus.cc (layout_printer::print): Temporarily set DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE. * diagnostic.cc (diagnostic_context::initialize): Update for renaming of m_nesting_depth to m_group_nesting_depth and initialize m_diagnostic_nesting_level. (diagnostic_context::finish): Update for renaming of m_nesting_depth to m_group_nesting_depth. (diagnostic_context::report_diagnostic): Likewise. (diagnostic_context::begin_group): Likewise. (diagnostic_context::end_group): Likewise. (diagnostic_context::push_nesting_level): New. (diagnostic_context::pop_nesting_level): New. (diagnostic_context::set_diagnostic_buffer): Update for renaming of m_nesting_depth to m_group_nesting_depth. Assert that we don't have nested diagnostics. * diagnostic.h (diagnostic_context::push_nesting_level): New decl. (diagnostic_context::pop_nesting_level): New decl. (diagnostic_context::get_diagnostic_nesting_level): New accessor. (diagnostic_context::build_indent_prefix): New decl. (diagnostic_context::m_diagnostic_groups): Rename m_nesting_depth to m_group_nesting_depth and add field m_diagnostic_nesting_level. * doc/invoke.texi (fdiagnostics-add-output): Add note about "experimental" schemes, keys, and values. Add keys "experimental-nesting", "experimental-nesting-show-locations", and "experimental-nesting-show-levels" to text scheme. * opts-diagnostic.cc (text_scheme_handler::make_sink): Add keys "experimental-nesting", "experimental-nesting-show-locations", and "experimental-nesting-show-levels". gcc/testsuite/ChangeLog: PR other/116253 * gcc.dg/plugin/diagnostic-test-nesting-sarif.c: New test. * gcc.dg/plugin/diagnostic-test-nesting-sarif.py: New test. * gcc.dg/plugin/diagnostic-test-nesting-text-indented-show-levels.c: New test. * gcc.dg/plugin/diagnostic-test-nesting-text-indented-unicode.c: New test. * gcc.dg/plugin/diagnostic-test-nesting-text-indented.c: New test. * gcc.dg/plugin/diagnostic-test-nesting-text-plain.c: New test. * gcc.dg/plugin/diagnostic_plugin_test_nesting.c: New test plugin. * gcc.dg/plugin/plugin.exp: Add the above. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
parent
6d90f5d0ae
commit
a5af2ec16f
@ -176,7 +176,7 @@ c_diagnostic_text_finalizer (diagnostic_text_output_format &text_output,
|
||||
{
|
||||
pretty_printer *const pp = text_output.get_printer ();
|
||||
char *saved_prefix = pp_take_prefix (pp);
|
||||
pp_set_prefix (pp, NULL);
|
||||
pp_set_prefix (pp, text_output.build_indent_prefix (false));
|
||||
pp_newline (pp);
|
||||
diagnostic_show_locus (&text_output.get_context (),
|
||||
diagnostic->richloc, diagnostic->kind, pp);
|
||||
|
@ -48,6 +48,18 @@ class auto_diagnostic_group
|
||||
~auto_diagnostic_group ();
|
||||
};
|
||||
|
||||
/* RAII-style class for nesting hierarchical diagnostics.
|
||||
Any diagnostics emitted within the lifetime of this object
|
||||
will be treated as one level of nesting deeper than diagnostics
|
||||
emitted outside the lifetime of the object. */
|
||||
|
||||
class auto_diagnostic_nesting_level
|
||||
{
|
||||
public:
|
||||
auto_diagnostic_nesting_level ();
|
||||
~auto_diagnostic_nesting_level ();
|
||||
};
|
||||
|
||||
/* Forward decl. */
|
||||
class diagnostic_metadata; /* See diagnostic-metadata.h. */
|
||||
|
||||
|
@ -658,9 +658,13 @@ private:
|
||||
- secondary ranges without labels (as related locations)
|
||||
|
||||
Known limitations:
|
||||
- GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group),
|
||||
but we only capture location and message information from such nested
|
||||
diagnostics (e.g. we ignore fix-it hints on them)
|
||||
- GCC supports nesting of diagnostics (one-deep nesting via
|
||||
auto_diagnostic_group, and arbitrary nesting via
|
||||
auto_diagnostic_nesting_level). These are captured in the SARIF
|
||||
as related locations, and so we only capture location and message
|
||||
information from such nested diagnostics (e.g. we ignore fix-it
|
||||
hints on them). Diagnostics within an auto_diagnostic_nesting_level
|
||||
have their nesting level captured as a property.
|
||||
- although we capture command-line arguments (section 3.20.2), we don't
|
||||
yet capture response files.
|
||||
- doesn't capture "artifact.encoding" property
|
||||
@ -1222,6 +1226,12 @@ sarif_result::on_nested_diagnostic (const diagnostic_info &diagnostic,
|
||||
pp_clear_output_area (builder.get_printer ());
|
||||
location_obj->set<sarif_message> ("message", std::move (message_obj));
|
||||
|
||||
/* Add nesting level, as per "P3358R0 SARIF for Structured Diagnostics"
|
||||
https://wg21.link/P3358R0 */
|
||||
sarif_property_bag &bag = location_obj->get_or_create_properties ();
|
||||
bag.set_integer ("nestingLevel",
|
||||
builder.get_context ().get_diagnostic_nesting_level ());
|
||||
|
||||
add_related_location (std::move (location_obj), builder);
|
||||
}
|
||||
|
||||
|
@ -220,9 +220,35 @@ on_report_diagnostic (const diagnostic_info &diagnostic,
|
||||
if (m_context.m_show_option_requested)
|
||||
print_option_information (diagnostic, orig_diag_kind);
|
||||
|
||||
/* If we're showing nested diagnostics, then print the location
|
||||
on a new line, indented. */
|
||||
if (m_show_nesting && m_show_locations_in_nesting)
|
||||
{
|
||||
const int nesting_level = get_context ().get_diagnostic_nesting_level ();
|
||||
if (nesting_level > 0)
|
||||
{
|
||||
pp_set_prefix (pp, nullptr);
|
||||
char *indent_prefix = build_indent_prefix (false);
|
||||
/* Only print changes of location. */
|
||||
if (diagnostic_location (&diagnostic)
|
||||
!= get_context ().m_last_location)
|
||||
{
|
||||
const expanded_location s
|
||||
= diagnostic_expand_location (&diagnostic);
|
||||
label_text location_text = get_location_text (s);
|
||||
pp_newline (pp);
|
||||
pp_printf (pp, "%s%s", indent_prefix, location_text.get ());
|
||||
}
|
||||
pp_set_prefix (pp, indent_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
(*diagnostic_text_finalizer (&m_context)) (*this,
|
||||
&diagnostic,
|
||||
orig_diag_kind);
|
||||
|
||||
if (m_show_nesting && m_show_locations_in_nesting)
|
||||
get_context ().m_last_location = diagnostic_location (&diagnostic);
|
||||
}
|
||||
|
||||
void
|
||||
@ -257,8 +283,12 @@ after_diagnostic (const diagnostic_info &diagnostic)
|
||||
}
|
||||
|
||||
/* Return a malloc'd string describing a location and the severity of the
|
||||
diagnostic, e.g. "foo.c:42:10: error: ". The caller is responsible for
|
||||
freeing the memory. */
|
||||
diagnostic, e.g. "foo.c:42:10: error: ".
|
||||
|
||||
If m_show_nesting, then the above will be preceded by indentation to show
|
||||
the level, and a bullet point.
|
||||
|
||||
The caller is responsible for freeing the memory. */
|
||||
char *
|
||||
diagnostic_text_output_format::
|
||||
build_prefix (const diagnostic_info &diagnostic) const
|
||||
@ -275,12 +305,22 @@ build_prefix (const diagnostic_info &diagnostic) const
|
||||
text_ce = colorize_stop (pp_show_color (pp));
|
||||
}
|
||||
|
||||
const expanded_location s = diagnostic_expand_location (&diagnostic);
|
||||
label_text location_text = get_location_text (s);
|
||||
|
||||
char *result = build_message_string ("%s %s%s%s", location_text.get (),
|
||||
text_cs, text, text_ce);
|
||||
return result;
|
||||
const int nesting_level = get_context ().get_diagnostic_nesting_level ();
|
||||
if (m_show_nesting && nesting_level > 0)
|
||||
{
|
||||
char *indent_prefix = build_indent_prefix (true);
|
||||
char *result = build_message_string ("%s%s%s%s", indent_prefix,
|
||||
text_cs, text, text_ce);
|
||||
free (indent_prefix);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
const expanded_location s = diagnostic_expand_location (&diagnostic);
|
||||
label_text location_text = get_location_text (s);
|
||||
return build_message_string ("%s %s%s%s", location_text.get (),
|
||||
text_cs, text, text_ce);
|
||||
}
|
||||
}
|
||||
|
||||
/* Same as build_prefix, but only the source FILE is given. */
|
||||
@ -294,6 +334,71 @@ diagnostic_text_output_format::file_name_as_prefix (const char *f) const
|
||||
return build_message_string ("%s%s:%s ", locus_cs, f, locus_ce);
|
||||
}
|
||||
|
||||
/* Get the unicode code point for bullet points when showing
|
||||
nested diagnostics. */
|
||||
|
||||
static unsigned
|
||||
get_bullet_point_unichar (bool unicode)
|
||||
{
|
||||
if (unicode)
|
||||
return 0x2022; /* U+2022: Bullet */
|
||||
else
|
||||
return '*';
|
||||
}
|
||||
|
||||
/* Return true if DC's theme supports unicode characters. */
|
||||
|
||||
static bool
|
||||
use_unicode_p (const diagnostic_context &dc)
|
||||
{
|
||||
if (text_art::theme *theme = dc.get_diagram_theme ())
|
||||
return theme->unicode_p ();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the unicode code point for bullet points when showing
|
||||
nested diagnostics. */
|
||||
|
||||
static unsigned
|
||||
get_bullet_point_unichar (diagnostic_context &dc)
|
||||
{
|
||||
return get_bullet_point_unichar (use_unicode_p (dc));
|
||||
}
|
||||
|
||||
/* Return a malloc'd string for use as a prefix to show indentation.
|
||||
If m_show_nesting is false, or we're at the top-level, then the
|
||||
result will be the empty string.
|
||||
|
||||
If m_show_nesting, then the result will contain indentation to show
|
||||
the nesting level, then either a bullet point (if WITH_BULLET is true),
|
||||
or a space.
|
||||
|
||||
The caller is responsible for freeing the memory. */
|
||||
|
||||
char *
|
||||
diagnostic_text_output_format::build_indent_prefix (bool with_bullet) const
|
||||
{
|
||||
if (!m_show_nesting)
|
||||
return xstrdup ("");
|
||||
|
||||
const int nesting_level = get_context ().get_diagnostic_nesting_level ();
|
||||
if (nesting_level == 0)
|
||||
return xstrdup ("");
|
||||
|
||||
pretty_printer pp;
|
||||
for (int i = 0; i < nesting_level; i++)
|
||||
pp_string (&pp, " ");
|
||||
if (with_bullet)
|
||||
pp_unicode_character (&pp, get_bullet_point_unichar (get_context ()));
|
||||
else
|
||||
pp_space (&pp);
|
||||
pp_space (&pp);
|
||||
if (m_show_nesting_levels)
|
||||
pp_printf (&pp, "(level %i):", nesting_level);
|
||||
return xstrdup (pp_formatted_text (&pp));
|
||||
}
|
||||
|
||||
/* Add a purely textual note with text GMSGID and with LOCATION. */
|
||||
|
||||
void
|
||||
|
@ -39,7 +39,9 @@ public:
|
||||
m_column_policy (context),
|
||||
m_last_module (nullptr),
|
||||
m_includes_seen (nullptr),
|
||||
m_follows_reference_printer (follows_reference_printer)
|
||||
m_follows_reference_printer (follows_reference_printer),
|
||||
m_show_nesting (false),
|
||||
m_show_nesting_levels (false)
|
||||
{}
|
||||
~diagnostic_text_output_format ();
|
||||
|
||||
@ -73,6 +75,8 @@ public:
|
||||
|
||||
char *file_name_as_prefix (const char *) const;
|
||||
|
||||
char *build_indent_prefix (bool with_bullet) const;
|
||||
|
||||
void print_path (const diagnostic_path &path);
|
||||
|
||||
bool show_column_p () const { return get_context ().m_show_column; }
|
||||
@ -83,6 +87,22 @@ public:
|
||||
}
|
||||
diagnostic_location_print_policy get_location_print_policy () const;
|
||||
|
||||
bool show_nesting_p () const { return m_show_nesting; }
|
||||
bool show_locations_in_nesting_p () const
|
||||
{
|
||||
return m_show_locations_in_nesting;
|
||||
}
|
||||
|
||||
void set_show_nesting (bool show_nesting) { m_show_nesting = show_nesting; }
|
||||
void set_show_locations_in_nesting (bool val)
|
||||
{
|
||||
m_show_locations_in_nesting = val;
|
||||
}
|
||||
void set_show_nesting_levels (bool show_nesting_levels)
|
||||
{
|
||||
m_show_nesting_levels = show_nesting_levels;
|
||||
}
|
||||
|
||||
protected:
|
||||
void print_any_cwe (const diagnostic_info &diagnostic);
|
||||
void print_any_rules (const diagnostic_info &diagnostic);
|
||||
@ -112,6 +132,18 @@ protected:
|
||||
If false, this text output was created after the dc was created, and
|
||||
thus tracks its own values for color and m_url_format. */
|
||||
bool m_follows_reference_printer;
|
||||
|
||||
/* If true, then use indentation to show the nesting structure of
|
||||
nested diagnostics, and print locations on separate lines after the
|
||||
diagnostic message, rather than as a prefix to the message. */
|
||||
bool m_show_nesting;
|
||||
|
||||
/* Set to false to suppress location-printing when showing nested
|
||||
diagnostics, for use in DejaGnu tests. */
|
||||
bool m_show_locations_in_nesting;
|
||||
|
||||
/* If true, then add "(level N):" when printing nested diagnostics. */
|
||||
bool m_show_nesting_levels;
|
||||
};
|
||||
|
||||
#endif /* ! GCC_DIAGNOSTIC_FORMAT_TEXT_H */
|
||||
|
@ -576,3 +576,15 @@ auto_diagnostic_group::~auto_diagnostic_group ()
|
||||
{
|
||||
global_dc->end_group ();
|
||||
}
|
||||
|
||||
/* class auto_diagnostic_nesting_level. */
|
||||
|
||||
auto_diagnostic_nesting_level::auto_diagnostic_nesting_level ()
|
||||
{
|
||||
global_dc->push_nesting_level ();
|
||||
}
|
||||
|
||||
auto_diagnostic_nesting_level::~auto_diagnostic_nesting_level ()
|
||||
{
|
||||
global_dc->pop_nesting_level ();
|
||||
}
|
||||
|
@ -3323,6 +3323,9 @@ diagnostic_source_print_policy::print (pretty_printer &pp,
|
||||
void
|
||||
layout_printer::print (const diagnostic_source_print_policy &source_policy)
|
||||
{
|
||||
diagnostic_prefixing_rule_t saved_rule = pp_prefixing_rule (&m_pp);
|
||||
pp_prefixing_rule (&m_pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
|
||||
|
||||
if (get_options ().show_ruler_p)
|
||||
show_ruler (m_layout.m_x_offset_display + get_options ().max_width);
|
||||
|
||||
@ -3359,6 +3362,8 @@ layout_printer::print (const diagnostic_source_print_policy &source_policy)
|
||||
|
||||
if (auto effect_info = m_layout.m_effect_info)
|
||||
effect_info->m_trailing_out_edge_column = m_link_rhs_column;
|
||||
|
||||
pp_prefixing_rule (&m_pp) = saved_rule;
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
@ -281,7 +281,8 @@ diagnostic_context::initialize (int n_opts)
|
||||
m_tabstop = 8;
|
||||
m_escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
|
||||
m_edit_context_ptr = nullptr;
|
||||
m_diagnostic_groups.m_nesting_depth = 0;
|
||||
m_diagnostic_groups.m_group_nesting_depth = 0;
|
||||
m_diagnostic_groups.m_diagnostic_nesting_level = 0;
|
||||
m_diagnostic_groups.m_emission_count = 0;
|
||||
m_output_sinks.safe_push (new diagnostic_text_output_format (*this, true));
|
||||
m_set_locations_cb = nullptr;
|
||||
@ -384,7 +385,7 @@ diagnostic_context::finish ()
|
||||
/* We might be handling a fatal error.
|
||||
Close any active diagnostic groups, which may trigger flushing
|
||||
sinks. */
|
||||
while (m_diagnostic_groups.m_nesting_depth > 0)
|
||||
while (m_diagnostic_groups.m_group_nesting_depth > 0)
|
||||
end_group ();
|
||||
|
||||
set_diagnostic_buffer (nullptr);
|
||||
@ -1322,7 +1323,7 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic)
|
||||
/* Every call to report_diagnostic should be within a
|
||||
begin_group/end_group pair so that output formats can reliably
|
||||
flush diagnostics with on_end_group when the topmost group is ended. */
|
||||
gcc_assert (m_diagnostic_groups.m_nesting_depth > 0);
|
||||
gcc_assert (m_diagnostic_groups.m_group_nesting_depth > 0);
|
||||
|
||||
/* Give preference to being able to inhibit warnings, before they
|
||||
get reclassified to something else. */
|
||||
@ -1701,13 +1702,13 @@ fancy_abort (const char *file, int line, const char *function)
|
||||
void
|
||||
diagnostic_context::begin_group ()
|
||||
{
|
||||
m_diagnostic_groups.m_nesting_depth++;
|
||||
m_diagnostic_groups.m_group_nesting_depth++;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::end_group ()
|
||||
{
|
||||
if (--m_diagnostic_groups.m_nesting_depth == 0)
|
||||
if (--m_diagnostic_groups.m_group_nesting_depth == 0)
|
||||
{
|
||||
/* Handle the case where we've popped the final diagnostic group.
|
||||
If any diagnostics were emitted, give the context a chance
|
||||
@ -1719,6 +1720,18 @@ diagnostic_context::end_group ()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::push_nesting_level ()
|
||||
{
|
||||
++m_diagnostic_groups.m_diagnostic_nesting_level;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::pop_nesting_level ()
|
||||
{
|
||||
--m_diagnostic_groups.m_diagnostic_nesting_level;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_output_format::dump (FILE *out, int indent) const
|
||||
{
|
||||
@ -1824,7 +1837,11 @@ diagnostic_context::set_diagnostic_buffer (diagnostic_buffer *buffer)
|
||||
/* We don't allow changing buffering within a diagnostic group
|
||||
(to simplify handling of buffered diagnostics within the
|
||||
diagnostic_format implementations). */
|
||||
gcc_assert (m_diagnostic_groups.m_nesting_depth == 0);
|
||||
gcc_assert (m_diagnostic_groups.m_group_nesting_depth == 0);
|
||||
|
||||
/* Likewise, for simplicity, we only allow changing buffers
|
||||
at nesting level 0. */
|
||||
gcc_assert (m_diagnostic_groups.m_diagnostic_nesting_level == 0);
|
||||
|
||||
m_diagnostic_buffer = buffer;
|
||||
|
||||
|
@ -557,6 +557,9 @@ public:
|
||||
void begin_group ();
|
||||
void end_group ();
|
||||
|
||||
void push_nesting_level ();
|
||||
void pop_nesting_level ();
|
||||
|
||||
bool warning_enabled_at (location_t loc, diagnostic_option_id option_id);
|
||||
|
||||
bool option_unspecified_p (diagnostic_option_id option_id) const
|
||||
@ -723,6 +726,13 @@ public:
|
||||
const char *, const char *, va_list *,
|
||||
diagnostic_t) ATTRIBUTE_GCC_DIAG(7,0);
|
||||
|
||||
int get_diagnostic_nesting_level () const
|
||||
{
|
||||
return m_diagnostic_groups.m_diagnostic_nesting_level;
|
||||
}
|
||||
|
||||
char *build_indent_prefix () const;
|
||||
|
||||
int
|
||||
pch_save (FILE *f)
|
||||
{
|
||||
@ -933,7 +943,10 @@ private:
|
||||
/* Fields relating to diagnostic groups. */
|
||||
struct {
|
||||
/* How many diagnostic_group instances are currently alive. */
|
||||
int m_nesting_depth;
|
||||
int m_group_nesting_depth;
|
||||
|
||||
/* How many nesting levels have been pushed within this group. */
|
||||
int m_diagnostic_nesting_level;
|
||||
|
||||
/* How many diagnostics have been emitted since the bottommost
|
||||
diagnostic_group was pushed. */
|
||||
|
@ -5937,6 +5937,9 @@ by @code{:} and one or more @var{KEY}=@var{VALUE} pairs, in this form:
|
||||
|
||||
etc.
|
||||
|
||||
Schemes, keys, or values with a name prefixed ``experimental'' may change
|
||||
or be removed without notice.
|
||||
|
||||
@var{SCHEME} can be
|
||||
|
||||
@table @gcctabopt
|
||||
@ -5952,6 +5955,21 @@ Supported keys are:
|
||||
Override colorization settings from @option{-fdiagnostics-color} for this
|
||||
text output.
|
||||
|
||||
@item experimental-nesting=@r{[}yes@r{|}no@r{]}
|
||||
Enable an experimental mode that emphasizes hierarchical relationships
|
||||
within diagnostics messages, displaying location information on separate
|
||||
lines.
|
||||
|
||||
@item experimental-nesting-show-locations=@r{[}yes@r{|}no@r{]}
|
||||
If @code{experimental-nesting=yes}, then by default locations are
|
||||
shown; set this key to @code{no} to disable printing such locations.
|
||||
This exists for use by GCC developers, for writing DejaGnu test cases.
|
||||
|
||||
@item experimental-nesting-show-levels=@r{[}yes@r{|}no@r{]}
|
||||
This is a debugging option for use with @code{experimental-nesting=yes}.
|
||||
Set this key to @code{yes} to print explicit nesting levels in the output.
|
||||
This exists for use by GCC developers.
|
||||
|
||||
@end table
|
||||
|
||||
@item sarif
|
||||
|
@ -361,6 +361,9 @@ text_scheme_handler::make_sink (const context &ctxt,
|
||||
const scheme_name_and_params &parsed_arg) const
|
||||
{
|
||||
bool show_color = pp_show_color (ctxt.m_dc.get_reference_printer ());
|
||||
bool show_nesting = false;
|
||||
bool show_locations_in_nesting = true;
|
||||
bool show_levels = false;
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
@ -371,17 +374,42 @@ text_scheme_handler::make_sink (const context &ctxt,
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "experimental-nesting")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_nesting))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "experimental-nesting-show-locations")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_locations_in_nesting))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "experimental-nesting-show-levels")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_levels))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("color");
|
||||
known_keys.safe_push ("experimental-nesting");
|
||||
known_keys.safe_push ("experimental-nesting-show-locations");
|
||||
known_keys.safe_push ("experimental-nesting-show-levels");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
|
||||
known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format> sink;
|
||||
sink = ::make_unique<diagnostic_text_output_format> (ctxt.m_dc);
|
||||
auto sink = ::make_unique<diagnostic_text_output_format> (ctxt.m_dc);
|
||||
sink->set_show_nesting (show_nesting);
|
||||
sink->set_show_locations_in_nesting (show_locations_in_nesting);
|
||||
sink->set_show_nesting_levels (show_levels);
|
||||
return sink;
|
||||
}
|
||||
|
||||
|
16
gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-sarif.c
Normal file
16
gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-sarif.c
Normal file
@ -0,0 +1,16 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-fdiagnostics-add-output=sarif" } */
|
||||
|
||||
extern void foo (void);
|
||||
|
||||
void test_nesting (void)
|
||||
{
|
||||
foo (); /* { dg-error "top-level error" } */
|
||||
}
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name. */
|
||||
/* { dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest diagnostic-test-nesting-sarif.c "diagnostic-test-nesting-sarif.py" } } */
|
39
gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-sarif.py
Normal file
39
gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-sarif.py
Normal file
@ -0,0 +1,39 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
def test_basics(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json"
|
||||
|
||||
version = sarif['version']
|
||||
assert version == "2.1.0"
|
||||
|
||||
def test_nested_result(sarif):
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
results = run['results']
|
||||
|
||||
assert len(results) == 1
|
||||
|
||||
result = results[0]
|
||||
assert result['level'] == 'error'
|
||||
assert result['message']['text'] == "top-level error"
|
||||
|
||||
relatedLocations = result['relatedLocations']
|
||||
assert len(relatedLocations) == 12
|
||||
|
||||
for i in range(12):
|
||||
note = relatedLocations[i]
|
||||
text = note['message']['text']
|
||||
nestingLevel = note['properties']['nestingLevel']
|
||||
if i % 4 == 0:
|
||||
assert text == 'child %i' % (i / 4)
|
||||
assert nestingLevel == 1
|
||||
else:
|
||||
assert text == 'grandchild %i %i' % ((i / 4), (i % 4) - 1)
|
||||
assert nestingLevel == 2
|
@ -0,0 +1,24 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-levels=yes" } */
|
||||
|
||||
extern void foo (void);
|
||||
|
||||
void test_nesting (void)
|
||||
{
|
||||
foo (); /* { dg-error "top-level error" } */
|
||||
}
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
* (level 1):note: child 0
|
||||
* (level 2):note: grandchild 0 0
|
||||
* (level 2):note: grandchild 0 1
|
||||
* (level 2):note: grandchild 0 2
|
||||
* (level 1):note: child 1
|
||||
* (level 2):note: grandchild 1 0
|
||||
* (level 2):note: grandchild 1 1
|
||||
* (level 2):note: grandchild 1 2
|
||||
* (level 1):note: child 2
|
||||
* (level 2):note: grandchild 2 0
|
||||
* (level 2):note: grandchild 2 1
|
||||
* (level 2):note: grandchild 2 2
|
||||
{ dg-end-multiline-output "" } */
|
@ -0,0 +1,24 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-fdiagnostics-set-output=text:experimental-nesting=yes -fdiagnostics-text-art-charset=unicode" } */
|
||||
|
||||
extern void foo (void);
|
||||
|
||||
void test_nesting (void)
|
||||
{
|
||||
foo (); /* { dg-error "top-level error" } */
|
||||
}
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
• note: child 0
|
||||
• note: grandchild 0 0
|
||||
• note: grandchild 0 1
|
||||
• note: grandchild 0 2
|
||||
• note: child 1
|
||||
• note: grandchild 1 0
|
||||
• note: grandchild 1 1
|
||||
• note: grandchild 1 2
|
||||
• note: child 2
|
||||
• note: grandchild 2 0
|
||||
• note: grandchild 2 1
|
||||
• note: grandchild 2 2
|
||||
{ dg-end-multiline-output "" } */
|
@ -0,0 +1,24 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-fdiagnostics-set-output=text:experimental-nesting=yes" } */
|
||||
|
||||
extern void foo (void);
|
||||
|
||||
void test_nesting (void)
|
||||
{
|
||||
foo (); /* { dg-error "top-level error" } */
|
||||
}
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
* note: child 0
|
||||
* note: grandchild 0 0
|
||||
* note: grandchild 0 1
|
||||
* note: grandchild 0 2
|
||||
* note: child 1
|
||||
* note: grandchild 1 0
|
||||
* note: grandchild 1 1
|
||||
* note: grandchild 1 2
|
||||
* note: child 2
|
||||
* note: grandchild 2 0
|
||||
* note: grandchild 2 1
|
||||
* note: grandchild 2 2
|
||||
{ dg-end-multiline-output "" } */
|
@ -0,0 +1,8 @@
|
||||
/* { dg-do compile } */
|
||||
|
||||
extern void foo (void);
|
||||
|
||||
void test_nesting (void)
|
||||
{
|
||||
foo (); /* { dg-error "top-level error" } */
|
||||
}
|
145
gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_nesting.c
Normal file
145
gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_nesting.c
Normal file
@ -0,0 +1,145 @@
|
||||
/* This plugin exercises diagnostic nesting. */
|
||||
|
||||
#include "gcc-plugin.h"
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tm.h"
|
||||
#include "tree.h"
|
||||
#include "stringpool.h"
|
||||
#include "toplev.h"
|
||||
#include "basic-block.h"
|
||||
#include "hash-table.h"
|
||||
#include "vec.h"
|
||||
#include "ggc.h"
|
||||
#include "basic-block.h"
|
||||
#include "tree-ssa-alias.h"
|
||||
#include "internal-fn.h"
|
||||
#include "gimple.h"
|
||||
#include "gimple-iterator.h"
|
||||
#include "gimple-fold.h"
|
||||
#include "tree-eh.h"
|
||||
#include "gimple-expr.h"
|
||||
#include "is-a.h"
|
||||
#include "tree.h"
|
||||
#include "tree-pass.h"
|
||||
#include "intl.h"
|
||||
#include "plugin-version.h"
|
||||
#include "diagnostic.h"
|
||||
#include "context.h"
|
||||
#include "gcc-rich-location.h"
|
||||
|
||||
int plugin_is_GPL_compatible;
|
||||
|
||||
const pass_data pass_data_test_nesting =
|
||||
{
|
||||
GIMPLE_PASS, /* type */
|
||||
"test_nesting", /* name */
|
||||
OPTGROUP_NONE, /* optinfo_flags */
|
||||
TV_NONE, /* tv_id */
|
||||
PROP_ssa, /* properties_required */
|
||||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
0, /* todo_flags_finish */
|
||||
};
|
||||
|
||||
class pass_test_nesting : public gimple_opt_pass
|
||||
{
|
||||
public:
|
||||
pass_test_nesting(gcc::context *ctxt)
|
||||
: gimple_opt_pass(pass_data_test_nesting, ctxt)
|
||||
{}
|
||||
|
||||
/* opt_pass methods: */
|
||||
bool gate (function *) { return true; }
|
||||
virtual unsigned int execute (function *);
|
||||
|
||||
}; // class pass_test_nesting
|
||||
|
||||
/* Determine if STMT is a call with NUM_ARGS arguments to a function
|
||||
named FUNCNAME.
|
||||
If so, return STMT as a gcall *. Otherwise return NULL. */
|
||||
|
||||
static gcall *
|
||||
check_for_named_call (gimple *stmt,
|
||||
const char *funcname, unsigned int num_args)
|
||||
{
|
||||
gcc_assert (funcname);
|
||||
|
||||
gcall *call = dyn_cast <gcall *> (stmt);
|
||||
if (!call)
|
||||
return NULL;
|
||||
|
||||
tree fndecl = gimple_call_fndecl (call);
|
||||
if (!fndecl)
|
||||
return NULL;
|
||||
|
||||
if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname))
|
||||
return NULL;
|
||||
|
||||
if (gimple_call_num_args (call) != num_args)
|
||||
{
|
||||
error_at (stmt->location, "expected number of args: %i (got %i)",
|
||||
num_args, gimple_call_num_args (call));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
/* Exercise diagnostic_nesting. */
|
||||
|
||||
unsigned int
|
||||
pass_test_nesting::execute (function *fun)
|
||||
{
|
||||
gimple_stmt_iterator gsi;
|
||||
basic_block bb;
|
||||
|
||||
FOR_EACH_BB_FN (bb, fun)
|
||||
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
|
||||
{
|
||||
gimple *stmt = gsi_stmt (gsi);
|
||||
|
||||
if (gcall *call = check_for_named_call (stmt, "foo", 0))
|
||||
{
|
||||
location_t loc = gimple_location (call);
|
||||
auto_diagnostic_group g;
|
||||
error_at (loc, "top-level error");
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
auto_diagnostic_nesting_level sentinel;
|
||||
inform (loc, "child %i", i);
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
auto_diagnostic_nesting_level sentinel;
|
||||
inform (loc, "grandchild %i %i", i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
plugin_init (struct plugin_name_args *plugin_info,
|
||||
struct plugin_gcc_version *version)
|
||||
{
|
||||
struct register_pass_info pass_info;
|
||||
const char *plugin_name = plugin_info->base_name;
|
||||
int argc = plugin_info->argc;
|
||||
struct plugin_argument *argv = plugin_info->argv;
|
||||
|
||||
if (!plugin_default_version_check (version, &gcc_version))
|
||||
return 1;
|
||||
|
||||
pass_info.pass = new pass_test_nesting (g);
|
||||
pass_info.reference_pass_name = "ssa";
|
||||
pass_info.ref_pass_instance_number = 1;
|
||||
pass_info.pos_op = PASS_POS_INSERT_AFTER;
|
||||
register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
|
||||
&pass_info);
|
||||
|
||||
return 0;
|
||||
}
|
@ -108,6 +108,12 @@ set plugin_test_list [list \
|
||||
{ diagnostic_plugin_test_metadata.c \
|
||||
diagnostic-test-metadata.c \
|
||||
diagnostic-test-metadata-sarif.c } \
|
||||
{ diagnostic_plugin_test_nesting.c \
|
||||
diagnostic-test-nesting-text-plain.c \
|
||||
diagnostic-test-nesting-text-indented.c \
|
||||
diagnostic-test-nesting-text-indented-show-levels.c \
|
||||
diagnostic-test-nesting-text-indented-unicode.c \
|
||||
diagnostic-test-nesting-sarif.c } \
|
||||
{ diagnostic_plugin_test_paths.c \
|
||||
diagnostic-test-paths-1.c \
|
||||
diagnostic-test-paths-2.c \
|
||||
|
Loading…
Reference in New Issue
Block a user