diagnostics, analyzer: add CFG edge visualization to path-printing

This patch adds some ability for links between labelled ranges when
quoting the user's source code, and uses this to add links between
events when printing diagnostic_paths, chopping them up further into
event ranges that can be printed together.
It adds links to the various "from..." - "...to" events in the
analyzer.

For example, previously we emitted this for
c-c++-common/analyzer/infinite-loop-linked-list.c's
while_loop_missing_next':

infinite-loop-linked-list.c:30:10: warning: infinite loop [CWE-835] [-Wanalyzer-infinite-loop]
   30 |   while (n)
      |          ^
  'while_loop_missing_next': events 1-5
   30 |   while (n)
      |          ^
      |          |
      |          (1) infinite loop here
      |          (2) when 'n' is non-NULL: always following 'true' branch...
      |          (5) ...to here
   31 |     {
   32 |       sum += n->val;
      |       ~~~~~~~~~~~~~
      |           |   |
      |           |   (3) ...to here
      |           (4) looping back...

whereas with the patch we now emit:

infinite-loop-linked-list.c:30:10: warning: infinite loop [CWE-835] [-Wanalyzer-infinite-loop]
   30 |   while (n)
      |          ^
  'while_loop_missing_next': events 1-3
   30 |   while (n)
      |          ^
      |          |
      |          (1) infinite loop here
      |          (2) when 'n' is non-NULL: always following 'true' branch... ->-+
      |                                                                         |
      |                                                                         |
      |+------------------------------------------------------------------------+
   31 ||    {
   32 ||      sum += n->val;
      ||             ~~~~~~
      ||              |
      |+------------->(3) ...to here
  'while_loop_missing_next': event 4
   32 |       sum += n->val;
      |       ~~~~^~~~~~~~~
      |           |
      |           (4) looping back... ->-+
      |                                  |
  'while_loop_missing_next': event 5
      |                                  |
      |+---------------------------------+
   30 ||  while (n)
      ||         ^
      ||         |
      |+-------->(5) ...to here

which I believe is easier to understand.

The patch also implements the use of unicode characters and colorization
for the lines (not shown in the above example).

There is a new option -fno-diagnostics-show-event-links for getting
back the old behavior (added to -fdiagnostics-plain-output).

gcc/analyzer/ChangeLog:
	* checker-event.h (checker_event::connect_to_next_event_p):
	Implement new diagnostic_event::connect_to_next_event_p vfunc.
	(start_cfg_edge_event::connect_to_next_event_p): Likewise.
	(start_consolidated_cfg_edges_event::connect_to_next_event_p):
	Likewise.
	* infinite-loop.cc (class looping_back_event): New subclass.
	(infinite_loop_diagnostic::add_final_event): Use it.

gcc/ChangeLog:
	* common.opt (fdiagnostics-show-event-links): New option.
	* diagnostic-label-effects.h: New file.
	* diagnostic-path.h (diagnostic_event::connect_to_next_event_p):
	New pure virtual function.
	(simple_diagnostic_event::connect_to_next_event_p): Implement it.
	(simple_diagnostic_event::connect_to_next_event): New.
	(simple_diagnostic_event::m_connected_to_next_event): New field.
	(simple_diagnostic_path::connect_to_next_event): New decl.
	* diagnostic-show-locus.cc: Include "text-art/theme.h" and
	"diagnostic-label-effects.h".
	(colorizer::set_cfg_edge): New.
	(layout::m_fallback_theme): New field.
	(layout::m_theme): New field.
	(layout::m_effect_info): New field.
	(layout::m_link_lhs_state): New enum and field.
	(layout::m_link_rhs_column): New field.
	(layout_range::has_in_edge): New.
	(layout_range::has_out_edge): New.
	(layout::layout): Add "effect_info" optional param.  Initialize
	m_theme, m_link_lhs_state, and m_link_rhs_column.
	(layout::maybe_add_location_range): Remove stray "FIXME" from
	leading comment.
	(layout::print_source_line): Replace space after margin with a
	call to print_leftmost_column.
	(layout::print_leftmost_column): New.
	(layout::start_annotation_line): Make non-const.  Gain
	responsibility for printing the leftmost column after the margin.
	(layout::print_annotation_line): Drop pp_space, as this is now
	added by start_annotation_line.
	(line_label::line_label): Add "has_in_edge" and "has_out_edge"
	params and initialize...
	(line_label::m_has_in_edge): New field.
	(line_label::m_has_out_edge): New field.
	(layout::print_any_labels): Pass edge information to line_label
	ctor.  Keep track of in-edges and out-edges, adding visualizations
	of these links between labels.
	(layout::print_leading_fixits):  Drop pp_character, as this is now
	added by start_annotation_line.
	(layout::print_trailing_fixits): Fix off-by-one errors in column
	calculation.
	(layout::move_to_column): Add comment about debugging.
	(layout::show_ruler): Make non-const.  Drop pp_space calls, as
	this is now added by start_annotation_line.
	(layout::print_line): Call print_any_right_to_left_edge_lines.
	(layout::print_any_right_to_left_edge_lines): New.
	(layout::update_any_effects): New.
	(gcc_rich_location::add_location_if_nearby): Initialize
	loc_range.m_label.
	(diagnostic_context::maybe_show_locus): Add "effects" param and
	pass it to diagnostic_context::show_locus.
	(diagnostic_context::show_locus): Add "effects" param, passing it
	to layout's ctor.  Call update_any_effects on the layout after
	printing the lines.
	(selftest::test_layout_x_offset_display_utf8): Update expected
	result for eliminated trailing newline.
	(selftest::test_layout_x_offset_display_utf8): Likewise.
	(selftest::test_layout_x_offset_display_tab): Likewise.
	* diagnostic.cc (diagnostic_context::initialize): Initialize
	m_source_printing.show_event_links_p.
	(simple_diagnostic_path::connect_to_next_event): New.
	(simple_diagnostic_event::simple_diagnostic_event): Initialize
	m_connected_to_next_event.
	* diagnostic.h (class diagnostic_source_effect_info): New forward
	decl.
	(diagnostic_source_printing_options::show_event_links_p): New
	field.
	(diagnostic_context::maybe_show_locus): Add optional "effect_info"
	param.
	(diagnostic_context::show_locus): Add "effect_info" param.
	(diagnostic_show_locus): Add optional "effect_info" param.
	* doc/invoke.texi: Add -fno-diagnostics-show-event-links.
	* lto-wrapper.cc (merge_and_complain): Add
	OPT_fdiagnostics_show_event_links to switch.
	(append_compiler_options): Likewise.
	(append_diag_options): Likewise.
	* opts-common.cc (decode_cmdline_options_to_array): Add
	"-fno-diagnostics-show-event-links" to -fdiagnostics-plain-output.
	* opts.cc (common_handle_option): Add case for
	OPT_fdiagnostics_show_event_links.
	* text-art/theme.cc (ascii_theme::get_cppchar): Handle
	cell_kind::CFG_*.
	(unicode_theme::get_cppchar): Likewise.
	* text-art/theme.h (theme::cell_kind): Add CFG_*.
	* toplev.cc (general_init): Initialize
	global_dc->m_source_printing.show_event_links_p.
	* tree-diagnostic-path.cc: Define INCLUDE_ALGORITHM,
	INCLUDE_MEMORY, and INCLUDE_STRING.  Include
	"diagnostic-label-effects.h".
	(path_label::path_label): Initialize m_effects.
	(path_label::get_effects): New.
	(class path_label::path_label_effects): New.
	(path_label::m_effects): New field.
	(class per_thread_summary): Add "friend struct event_range;".
	(per_thread_summary::per_thread_summary): Initialize m_last_event.
	(per_thread_summary::m_last_event): New field.
	(struct event_range::per_source_line_info): New.
	(event_range::event_range): Make "t" non-const.  Add
	"show_event_links" param and use it to initialize
	m_show_event_links.  Add info for initial event.
	(event_range::get_per_source_line_info): New.
	(event_range::maybe_add_event): Verify compatibility of the new
	label and existing labels with respect to the link-printing code.
	Update per-source-line info when an event is added.
	(event_range::print): Add"effect_info" param and pass to
	diagnostic_show_locus.
	(event_range::m_per_thread_summary): Make non-const.
	(event_range::m_source_line_info_map): New field.
	(event_range::m_show_event_links): New field.
	(path_summary::path_summary): Add "show_event_links" optional
	param, passing it to event_range ctor calls. Update
	pts.m_last_event.
	(thread_event_printer::print_swimlane_for_event_range): Add
	"effect_info" param and pass it to range->print.
	(print_path_summary_as_text): Keep track of the column for any
	out-edges at the end of printing each event_range and use as
	the leading in-edge for the next event_range.
	(default_tree_diagnostic_path_printer): Pass in show_event_links_p
	to path_summary ctor.
	(selftest::path_events_have_column_data_p): New.
	(class selftest::control_flow_test): New.
	(selftest::test_control_flow_1): New.
	(selftest::test_control_flow_2): New.
	(selftest::test_control_flow_3): New.
	(selftest::assert_cfg_edge_path_streq): New.
	(ASSERT_CFG_EDGE_PATH_STREQ): New macro.
	(selftest::test_control_flow_4): New.
	(selftest::test_control_flow_5): New.
	(selftest::test_control_flow_6): New.
	(selftest::control_flow_tests): New.
	(selftest::tree_diagnostic_path_cc_tests): Disable colorization on
	global_dc's printer.  Convert event_pp to a std::unique_ptr. Call
	control_flow_tests via for_each_line_table_case.
	(gen_command_line_string): Likewise.

gcc/testsuite/ChangeLog:
	* gcc.dg/analyzer/event-links-ascii.c: New test.
	* gcc.dg/analyzer/event-links-color.c: New test.
	* gcc.dg/analyzer/event-links-disabled.c: New test.
	* gcc.dg/analyzer/event-links-unicode.c: New test.

libcpp/ChangeLog:
	* include/rich-location.h (class label_effects): New forward decl.
	(range_label::get_effects): New vfunc.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
pull/91/merge
David Malcolm 2024-05-17 14:51:47 -04:00
parent 6daed96154
commit 770657d02c
21 changed files with 1960 additions and 69 deletions

View File

@ -113,6 +113,7 @@ public:
return NULL;
}
meaning get_meaning () const override;
bool connect_to_next_event_p () const override { return false; }
diagnostic_thread_id_t get_thread_id () const final override
{
return 0;
@ -451,6 +452,7 @@ public:
}
label_text get_desc (bool can_colorize) const override;
bool connect_to_next_event_p () const final override { return true; }
protected:
label_text maybe_describe_condition (bool can_colorize) const;
@ -534,6 +536,7 @@ public:
label_text get_desc (bool can_colorize) const final override;
meaning get_meaning () const override;
bool connect_to_next_event_p () const final override { return true; }
private:
bool m_edge_sense;

View File

@ -162,6 +162,21 @@ public:
}
};
class looping_back_event : public start_cfg_edge_event
{
public:
looping_back_event (const exploded_edge &eedge,
const event_loc_info &loc_info)
: start_cfg_edge_event (eedge, loc_info)
{
}
label_text get_desc (bool can_colorize) const final override
{
return label_text::borrow ("looping back...");
}
};
/* A subclass of pending_diagnostic for complaining about suspected
infinite loops. */
@ -300,8 +315,7 @@ public:
else if (cfg_sedge->back_edge_p ())
{
emission_path->add_event
(make_unique<precanned_custom_event>
(loc_info_from, "looping back..."));
(make_unique<looping_back_event> (*eedge, loc_info_from));
emission_path->add_event
(make_unique<end_cfg_edge_event>
(*eedge,

View File

@ -1368,6 +1368,10 @@ fdiagnostics-show-caret
Common Var(flag_diagnostics_show_caret) Init(1)
Show the source line with a caret indicating the column.
fdiagnostics-show-event-links
Common Var(flag_diagnostics_show_event_links) Init(1)
Show lines linking related events in diagnostic paths.
fdiagnostics-show-labels
Common Var(flag_diagnostics_show_labels) Init(1)
Show labels annotating ranges of source code when showing source.

View File

@ -0,0 +1,58 @@
/* Classes for adding special effects when quoting source code.
Copyright (C) 2024 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
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_DIAGNOSTIC_LABEL_EFFECTS_H
#define GCC_DIAGNOSTIC_LABEL_EFFECTS_H
/* Abstract base class for describing special effects when printing
a label when quoting source code. */
class label_effects
{
public:
virtual ~label_effects () {}
/* Adding links between labels, e.g. for visualizing control flow
in execution paths. */
virtual bool has_in_edge (unsigned range_idx) const = 0;
virtual bool has_out_edge (unsigned range_idx) const = 0;
};
/* A class to hold state when quoting a run of lines of source code. */
class diagnostic_source_effect_info
{
public:
diagnostic_source_effect_info ()
: m_leading_in_edge_column (-1),
m_trailing_out_edge_column (-1)
{
}
/* The column for an incoming link to the first label,
or -1 if no such link. */
int m_leading_in_edge_column;
/* The column for an outgoing link from the final label,
or -1 if no such link. */
int m_trailing_out_edge_column;
};
#endif /* GCC_DIAGNOSTIC_LABEL_EFFECTS_H */

View File

@ -156,6 +156,10 @@ class diagnostic_event
virtual meaning get_meaning () const = 0;
/* True iff we should draw a line connecting this event to the
next event (e.g. to highlight control flow). */
virtual bool connect_to_next_event_p () const = 0;
virtual diagnostic_thread_id_t get_thread_id () const = 0;
/* Hook for SARIF output to allow for adding diagnostic-specific
@ -224,16 +228,26 @@ class simple_diagnostic_event : public diagnostic_event
{
return meaning ();
}
bool connect_to_next_event_p () const final override
{
return m_connected_to_next_event;
}
diagnostic_thread_id_t get_thread_id () const final override
{
return m_thread_id;
}
void connect_to_next_event ()
{
m_connected_to_next_event = true;
}
private:
location_t m_loc;
tree m_fndecl;
int m_depth;
char *m_desc; // has been i18n-ed and formatted
bool m_connected_to_next_event;
diagnostic_thread_id_t m_thread_id;
};
@ -277,6 +291,8 @@ class simple_diagnostic_path : public diagnostic_path
const char *fmt, ...)
ATTRIBUTE_GCC_DIAG(6,7);
void connect_to_next_event ();
private:
auto_delete_vec<simple_diagnostic_thread> m_threads;
auto_delete_vec<simple_diagnostic_event> m_events;

View File

@ -33,6 +33,8 @@ along with GCC; see the file COPYING3. If not see
#include "selftest-diagnostic.h"
#include "cpplib.h"
#include "text-art/types.h"
#include "text-art/theme.h"
#include "diagnostic-label-effects.h"
#ifdef HAVE_TERMIOS_H
# include <termios.h>
@ -100,6 +102,7 @@ class colorizer
else
set_state (range_idx);
}
void set_cfg_edge () { set_state (0); }
void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
@ -236,6 +239,9 @@ class layout_range
enum column_unit col_unit) const;
bool intersects_line_p (linenum_type row) const;
bool has_in_edge () const;
bool has_out_edge () const;
layout_point m_start;
layout_point m_finish;
enum range_display_kind m_range_display_kind;
@ -371,7 +377,8 @@ class layout
layout (const diagnostic_context &context,
const rich_location &richloc,
diagnostic_t diagnostic_kind,
pretty_printer *pp);
pretty_printer *pp,
diagnostic_source_effect_info *effect_info = nullptr);
bool maybe_add_location_range (const location_range *loc_range,
unsigned original_idx,
@ -390,22 +397,27 @@ class layout
void print_line (linenum_type row);
void print_any_right_to_left_edge_lines ();
void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
void update_any_effects () const;
private:
bool will_show_line_p (linenum_type row) const;
void print_leading_fixits (linenum_type row);
line_bounds print_source_line (linenum_type row, const char *line,
int line_bytes);
bool should_print_annotation_line_p (linenum_type row) const;
void start_annotation_line (char margin_char = ' ') const;
void print_leftmost_column ();
void start_annotation_line (char margin_char = ' ');
void print_annotation_line (linenum_type row, const line_bounds lbounds);
void print_any_labels (linenum_type row);
void print_trailing_fixits (linenum_type row);
bool annotation_line_showed_range_p (linenum_type line, int start_column,
int finish_column) const;
void show_ruler (int max_column) const;
void show_ruler (int max_column);
bool validate_fixit_hint_p (const fixit_hint *hint);
@ -437,6 +449,9 @@ class layout
const line_maps *m_line_table;
file_cache &m_file_cache;
pretty_printer *m_pp;
const text_art::ascii_theme m_fallback_theme;
const text_art::theme &m_theme;
diagnostic_source_effect_info *m_effect_info;
char_display_policy m_policy;
location_t m_primary_loc;
exploc_with_display_col m_exploc;
@ -448,6 +463,55 @@ class layout
int m_linenum_width;
int m_x_offset_display;
bool m_escape_on_output;
/* Fields for handling links between labels (e.g. for showing CFG edges
in execution paths).
Note that the logic for printing such links makes various simplifying
assumptions about the set of labels in the rich_location, and users
of this code will need to split up labels into separate rich_location
instances to respect these assumptions, or the output will look wrong.
See the diagnostic_path-printing code for more information. */
/* An enum for describing the state of the leftmost column,
used for showing links between labels.
Consider e.g.
.x0000000001111111111222222222233333333334444444444.
.x1234567890123456789012345678901234567890123456789.
| | <- none
| (9) following false branch... ->-+ <- none
| | <- none
| | <- none
|+----------------------------------------+ <- rewinding to lhs
|| result->i = i; <- at lhs
|| ~~~~~~~~~~^~~ <- at lhs
|| | <- at lhs
|+----------->(10) ...to here <- indenting to dest
^^
||
|leftmost column ("x" above).
"margin". */
enum class link_lhs_state {
none,
rewinding_to_lhs,
at_lhs,
indenting_to_dest
} m_link_lhs_state;
/* The column of the current link on the RHS, if any, or
-1 if there is none.
Consider e.g.
.x0000000001111111111222222222233333333334444444444.
.x1234567890123456789012345678901234567890123456789.
| | <- -1
| (10) following false branch... ->-+ <- 42
| | <- 42
| | <- 42
|+-----------------------------------------+ <- 42
|| result->i = i; <- -1
|| ~~~~~~~~~~^~~ <- -1
|| | <- -1
|+----------->(11) ...to here <- -1. */
int m_link_rhs_column;
};
/* Implementation of "class colorizer". */
@ -691,6 +755,34 @@ layout_range::intersects_line_p (linenum_type row) const
return true;
}
/* Return true if this layout_range should have an in-edge. */
bool
layout_range::has_in_edge () const
{
if (!m_label)
return false;
const label_effects *effects = m_label->get_effects (m_original_idx);
if (!effects)
return false;
return effects->has_in_edge (m_original_idx);
}
/* Return true if this layout_range should have an out-edge. */
bool
layout_range::has_out_edge () const
{
if (!m_label)
return false;
const label_effects *effects = m_label->get_effects (m_original_idx);
if (!effects)
return false;
return effects->has_out_edge (m_original_idx);
}
#if CHECKING_P
/* Default for when we don't care what the tab expansion is set to. */
@ -1196,11 +1288,17 @@ make_policy (const diagnostic_context &dc,
layout::layout (const diagnostic_context &context,
const rich_location &richloc,
diagnostic_t diagnostic_kind,
pretty_printer *pp)
pretty_printer *pp,
diagnostic_source_effect_info *effect_info)
: m_options (context.m_source_printing),
m_line_table (richloc.get_line_table ()),
m_file_cache (context.get_file_cache ()),
m_pp (pp ? pp : context.printer),
/* Ensure we have a non-null m_theme. */
m_theme (context.get_diagram_theme ()
? *context.get_diagram_theme ()
: *static_cast <const text_art::theme *> (&m_fallback_theme)),
m_effect_info (effect_info),
m_policy (make_policy (context, richloc)),
m_primary_loc (richloc.get_range (0)->m_loc),
m_exploc (m_file_cache,
@ -1213,8 +1311,15 @@ layout::layout (const diagnostic_context &context,
m_line_spans (1 + richloc.get_num_locations ()),
m_linenum_width (0),
m_x_offset_display (0),
m_escape_on_output (richloc.escape_on_output_p ())
m_escape_on_output (richloc.escape_on_output_p ()),
m_link_lhs_state (link_lhs_state::none),
m_link_rhs_column (-1)
{
if (m_options.show_event_links_p)
if (effect_info)
if (effect_info->m_leading_in_edge_column)
m_link_rhs_column = effect_info->m_leading_in_edge_column;
for (unsigned int idx = 0; idx < richloc.get_num_locations (); idx++)
{
/* This diagnostic printer can only cope with "sufficiently sane" ranges.
@ -1249,7 +1354,7 @@ layout::layout (const diagnostic_context &context,
those that we can sanely print.
ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
(for use as extrinsic state by label ranges FIXME).
(for use as extrinsic state by label ranges).
If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
filtered against this layout instance's current line spans: it
@ -1718,10 +1823,10 @@ layout::print_source_line (linenum_type row, const char *line, int line_bytes)
int width = num_digits (row);
for (int i = 0; i < m_linenum_width - width; i++)
pp_space (m_pp);
pp_printf (m_pp, "%i | ", row);
pp_printf (m_pp, "%i |", row);
}
else
pp_space (m_pp);
print_leftmost_column ();
/* We will stop printing the source line at any trailing whitespace. */
line_bytes = get_line_bytes_without_trailing_whitespace (line,
@ -1824,11 +1929,59 @@ layout::should_print_annotation_line_p (linenum_type row) const
return false;
}
/* Begin an annotation line. If m_show_line_numbers_p, print the left
margin, which is empty for annotation lines. Otherwise, do nothing. */
/* Print the leftmost column after the margin, which is used for showing
links between labels (e.g. for CFG edges in execution paths). */
void
layout::start_annotation_line (char margin_char) const
layout::print_leftmost_column ()
{
if (!m_options.show_event_links_p)
gcc_assert (m_link_lhs_state == link_lhs_state::none);
switch (m_link_lhs_state)
{
default:
gcc_unreachable ();
case link_lhs_state::none:
pp_space (m_pp);
break;
case link_lhs_state::rewinding_to_lhs:
{
m_colorizer.set_cfg_edge ();
const cppchar_t ch= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_FROM_LEFT_TO_DOWN);
pp_unicode_character (m_pp, ch);
m_colorizer.set_normal_text ();
}
break;
case link_lhs_state::at_lhs:
{
m_colorizer.set_cfg_edge ();
const cppchar_t ch= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_DOWN);
pp_unicode_character (m_pp, ch);
m_colorizer.set_normal_text ();
}
break;
case link_lhs_state::indenting_to_dest:
{
m_colorizer.set_cfg_edge ();
const cppchar_t ch= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_FROM_DOWN_TO_RIGHT);
pp_unicode_character (m_pp, ch);
m_colorizer.set_normal_text ();
}
break;
}
}
/* Begin an annotation line. If m_show_line_numbers_p, print the left
margin, which is empty for annotation lines.
After any left margin, print a leftmost column, which is used for
showing links between labels (e.g. for CFG edges in execution paths). */
void
layout::start_annotation_line (char margin_char)
{
pp_emit_prefix (m_pp);
if (m_options.show_line_numbers_p)
@ -1842,6 +1995,10 @@ layout::start_annotation_line (char margin_char) const
pp_character (m_pp, margin_char);
pp_string (m_pp, " |");
}
if (margin_char == ' ')
print_leftmost_column ();
else
pp_character (m_pp, margin_char);
}
/* Print a line consisting of the caret/underlines for the given
@ -1854,7 +2011,6 @@ layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
lbounds.m_last_non_ws_disp_col);
start_annotation_line ();
pp_space (m_pp);
for (int column = 1 + m_x_offset_display; column < x_bound; column++)
{
@ -1926,9 +2082,13 @@ class line_label
{
public:
line_label (int state_idx, int column,
label_text text)
label_text text,
bool has_in_edge,
bool has_out_edge)
: m_state_idx (state_idx), m_column (column),
m_text (std::move (text)), m_label_line (0), m_has_vbar (true)
m_text (std::move (text)), m_label_line (0), m_has_vbar (true),
m_has_in_edge (has_in_edge),
m_has_out_edge (has_out_edge)
{
/* Using styled_string rather than cpp_display_width here
lets us skip SGR formatting characters for color and URLs.
@ -1959,6 +2119,8 @@ public:
size_t m_display_width;
int m_label_line;
bool m_has_vbar;
bool m_has_in_edge;
bool m_has_out_edge;
};
/* Print any labels in this row. */
@ -1996,7 +2158,9 @@ layout::print_any_labels (linenum_type row)
if (text.get () == NULL)
continue;
labels.safe_push (line_label (i, disp_col, std::move (text)));
labels.safe_push (line_label (i, disp_col, std::move (text),
range->has_in_edge (),
range->has_out_edge ()));
}
}
@ -2040,6 +2204,7 @@ layout::print_any_labels (linenum_type row)
label 1 : label line 3. */
int max_label_line = 1;
int label_line_with_in_edge = -1;
{
int next_column = INT_MAX;
line_label *label;
@ -2058,18 +2223,28 @@ layout::print_any_labels (linenum_type row)
}
label->m_label_line = max_label_line;
if (m_options.show_event_links_p)
if (label->m_has_in_edge)
label_line_with_in_edge = max_label_line;
next_column = label->m_column;
}
}
gcc_assert (labels.length () > 0);
/* Print the "label lines". For each label within the line, print
either a vertical bar ('|') for the labels that are lower down, or the
labels themselves once we've reached their line. */
{
for (int label_line = 0; label_line <= max_label_line; label_line++)
{
if (label_line == label_line_with_in_edge)
{
gcc_assert (m_options.show_event_links_p);
m_link_lhs_state = link_lhs_state::indenting_to_dest;
}
start_annotation_line ();
pp_space (m_pp);
int column = 1 + m_x_offset_display;
line_label *label;
FOR_EACH_VEC_ELT (labels, i, label)
@ -2081,7 +2256,35 @@ layout::print_any_labels (linenum_type row)
if (label_line == label->m_label_line)
{
gcc_assert (column <= label->m_column);
move_to_column (&column, label->m_column, true);
if (label_line == label_line_with_in_edge)
{
/* Print a prefix showing an incoming
link from another label.
.|+----------->(10) ...to here
. ^~~~~~~~~~~~~
. this text. */
gcc_assert (m_options.show_event_links_p);
m_colorizer.set_cfg_edge ();
const cppchar_t right= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_RIGHT);
while (column < label->m_column - 1)
{
pp_unicode_character (m_pp, right);
column++;
}
if (column == label->m_column - 1)
{
pp_character (m_pp, '>');
column++;
}
m_colorizer.set_normal_text ();
m_link_lhs_state = link_lhs_state::none;
label_line_with_in_edge = -1;
}
else
move_to_column (&column, label->m_column, true);
gcc_assert (column == label->m_column);
/* Colorize the text, unless it's for events in a
diagnostic_path. */
if (!m_diagnostic_path_p)
@ -2089,6 +2292,29 @@ layout::print_any_labels (linenum_type row)
pp_string (m_pp, label->m_text.m_buffer);
m_colorizer.set_normal_text ();
column += label->m_display_width;
if (m_options.show_event_links_p && label->m_has_out_edge)
{
/* Print a suffix showing the start of a linkage
to another label e.g. " ->-+" which will be the
first part of e.g.
. (9) following false branch... ->-+ <- HERE
. |
. |
. */
const cppchar_t right= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_RIGHT);
const cppchar_t from_right_to_down= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_FROM_RIGHT_TO_DOWN);
m_colorizer.set_cfg_edge ();
pp_space (m_pp);
pp_unicode_character (m_pp, right);
pp_unicode_character (m_pp, '>');
pp_unicode_character (m_pp, right);
pp_unicode_character (m_pp, from_right_to_down);
m_colorizer.set_normal_text ();
column += 5;
m_link_rhs_column = column - 1;
}
}
else if (label->m_has_vbar)
{
@ -2100,10 +2326,38 @@ layout::print_any_labels (linenum_type row)
column++;
}
}
/* If we have a vertical link line on the RHS, print the
'|' on this annotation line after the labels. */
if (m_link_rhs_column != -1 && column < m_link_rhs_column)
{
move_to_column (&column, m_link_rhs_column, true);
m_colorizer.set_cfg_edge ();
const cppchar_t down= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_DOWN);
pp_unicode_character (m_pp, down);
m_colorizer.set_normal_text ();
}
print_newline ();
}
}
/* If we have a vertical link line on the RHS, print a trailing
annotation line showing the vertical line. */
if (m_link_rhs_column != -1)
{
int column = 1 + m_x_offset_display;
start_annotation_line ();
move_to_column (&column, m_link_rhs_column, true);
m_colorizer.set_cfg_edge ();
const cppchar_t down= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_DOWN);
pp_unicode_character (m_pp, down);
m_colorizer.set_normal_text ();
print_newline ();
}
/* Clean up. */
{
line_label *label;
@ -2139,7 +2393,6 @@ layout::print_leading_fixits (linenum_type row)
the surrounding text. */
m_colorizer.set_normal_text ();
start_annotation_line ('+');
pp_character (m_pp, '+');
m_colorizer.set_fixit_insert ();
/* Print all but the trailing newline of the fix-it hint.
We have to print the newline separately to avoid
@ -2598,7 +2851,7 @@ layout::print_trailing_fixits (linenum_type row)
/* Now print the corrections. */
unsigned i;
correction *c;
int column = m_x_offset_display;
int column = 1 + m_x_offset_display;
if (!corrections.m_corrections.is_empty ())
start_annotation_line ();
@ -2649,7 +2902,7 @@ layout::print_trailing_fixits (linenum_type row)
}
/* Add a trailing newline, if necessary. */
move_to_column (&column, 0, false);
move_to_column (&column, 1 + m_x_offset_display, false);
}
/* Disable any colorization and emit a newline. */
@ -2766,11 +3019,15 @@ layout::move_to_column (int *column, int dest_column, bool add_left_margin)
print_newline ();
if (add_left_margin)
start_annotation_line ();
*column = m_x_offset_display;
*column = 1 + m_x_offset_display;
}
while (*column < dest_column)
{
/* For debugging column issues, it can be helpful to replace this
pp_space call with
pp_character (m_pp, '0' + (*column % 10));
to visualize the changing value of "*column". */
pp_space (m_pp);
(*column)++;
}
@ -2780,13 +3037,12 @@ layout::move_to_column (int *column, int dest_column, bool add_left_margin)
(after the 1-column indent). */
void
layout::show_ruler (int max_column) const
layout::show_ruler (int max_column)
{
/* Hundreds. */
if (max_column > 99)
{
start_annotation_line ();
pp_space (m_pp);
for (int column = 1 + m_x_offset_display; column <= max_column; column++)
if (column % 10 == 0)
pp_character (m_pp, '0' + (column / 100) % 10);
@ -2797,7 +3053,6 @@ layout::show_ruler (int max_column) const
/* Tens. */
start_annotation_line ();
pp_space (m_pp);
for (int column = 1 + m_x_offset_display; column <= max_column; column++)
if (column % 10 == 0)
pp_character (m_pp, '0' + (column / 10) % 10);
@ -2807,7 +3062,6 @@ layout::show_ruler (int max_column) const
/* Units. */
start_annotation_line ();
pp_space (m_pp);
for (int column = 1 + m_x_offset_display; column <= max_column; column++)
pp_character (m_pp, '0' + (column % 10));
pp_newline (m_pp);
@ -2824,6 +3078,7 @@ layout::print_line (linenum_type row)
if (!line)
return;
print_any_right_to_left_edge_lines ();
print_leading_fixits (row);
const line_bounds lbounds
= print_source_line (row, line.get_buffer (), line.length ());
@ -2834,6 +3089,63 @@ layout::print_line (linenum_type row)
print_trailing_fixits (row);
}
/* If there's a link column in the RHS, print something like this:
"\n"
"┌──────────────────────────────────────────┘\n"
showing the link entering at the top right and emerging
at the bottom left. */
void
layout::print_any_right_to_left_edge_lines ()
{
if (m_link_rhs_column == -1)
/* Can also happen if the out-edge had UNKNOWN_LOCATION. */
return;
gcc_assert (m_options.show_event_links_p);
/* Print the line with "|". */
start_annotation_line ();
int column = 1 + m_x_offset_display;
move_to_column (&column, m_link_rhs_column, true);
m_colorizer.set_cfg_edge ();
const cppchar_t down= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_DOWN);
pp_unicode_character (m_pp, down);
m_colorizer.set_normal_text ();
pp_newline (m_pp);
/* Print the line with "┌──────────────────────────────────────────┘". */
m_link_lhs_state = link_lhs_state::rewinding_to_lhs;
start_annotation_line ();
m_colorizer.set_cfg_edge ();
const cppchar_t left= m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_LEFT);
for (int column = 1 + m_x_offset_display; column < m_link_rhs_column;
column++)
pp_unicode_character (m_pp, left);
const cppchar_t from_down_to_left = m_theme.get_cppchar
(text_art::theme::cell_kind::CFG_FROM_DOWN_TO_LEFT);
pp_unicode_character (m_pp, from_down_to_left);
m_colorizer.set_normal_text ();
pp_newline (m_pp);
/* We now have a link line on the LHS,
and no longer have one on the RHS. */
m_link_lhs_state = link_lhs_state::at_lhs;
m_link_rhs_column = -1;
}
/* Update this layout's m_effect_info (if any) after printing this
layout. */
void
layout::update_any_effects () const
{
if (m_effect_info)
m_effect_info->m_trailing_out_edge_column = m_link_rhs_column;
}
} /* End of anonymous namespace. */
/* If LOC is within the spans of lines that will already be printed for
@ -2853,6 +3165,7 @@ gcc_rich_location::add_location_if_nearby (location_t loc,
location_range loc_range;
loc_range.m_loc = loc;
loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
loc_range.m_label = nullptr;
if (!layout.maybe_add_location_range (&loc_range, 0,
restrict_to_current_line_spans))
return false;
@ -2867,7 +3180,8 @@ gcc_rich_location::add_location_if_nearby (location_t loc,
void
diagnostic_context::maybe_show_locus (const rich_location &richloc,
diagnostic_t diagnostic_kind,
pretty_printer *pp)
pretty_printer *pp,
diagnostic_source_effect_info *effects)
{
const location_t loc = richloc.get_loc ();
/* Do nothing if source-printing has been disabled. */
@ -2888,19 +3202,22 @@ diagnostic_context::maybe_show_locus (const rich_location &richloc,
m_last_location = loc;
show_locus (richloc, diagnostic_kind, pp);
show_locus (richloc, diagnostic_kind, pp, effects);
}
/* Print the physical source code corresponding to the location of
this diagnostic, with additional annotations.
If PP is non-null, then use it rather than this context's printer. */
If PP is non-null, then use it rather than this context's printer.
If EFFECTS is non-null, then use and update it. */
void
diagnostic_context::show_locus (const rich_location &richloc,
diagnostic_t diagnostic_kind,
pretty_printer *pp)
pretty_printer *pp,
diagnostic_source_effect_info *effects)
{
layout layout (*this, richloc, diagnostic_kind, pp);
layout layout (*this, richloc, diagnostic_kind, pp, effects);
for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
line_span_idx++)
{
@ -2929,6 +3246,8 @@ diagnostic_context::show_locus (const rich_location &richloc,
row <= last_line; row++)
layout.print_line (row);
}
layout.update_any_effects ();
}
#if CHECKING_P
@ -3137,7 +3456,7 @@ test_layout_x_offset_display_utf8 (const line_table_case &case_)
" 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
"that occupies 8 bytes and 4 display columns, starting at "
"column #102.\n"
" | ^\n\n",
" | ^\n",
pp_formatted_text (dc.printer));
}
@ -3162,7 +3481,7 @@ test_layout_x_offset_display_utf8 (const line_table_case &case_)
" 1 | \xf0\x9f\x98\x82 is a pair of emojis "
"that occupies 8 bytes and 4 display columns, starting at "
"column #102.\n"
" | ^\n\n",
" | ^\n",
pp_formatted_text (dc.printer));
}
@ -3266,11 +3585,11 @@ test_layout_x_offset_display_tab (const line_table_case &case_)
const char *output1
= " 1 | ' is a tab that occupies 1 byte and a variable number of "
"display columns, starting at column #103.\n"
" | ^\n\n";
" | ^\n";
const char *output2
= " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
"display columns, starting at column #103.\n"
" | ^\n\n";
" | ^\n";
const char *expected_output = (extra_width[tabstop] ? output1 : output2);
ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
}

View File

@ -253,6 +253,7 @@ diagnostic_context::initialize (int n_opts)
m_source_printing.show_line_numbers_p = false;
m_source_printing.min_margin_width = 0;
m_source_printing.show_ruler_p = false;
m_source_printing.show_event_links_p = false;
m_report_bug = false;
m_extra_output_kind = EXTRA_DIAGNOSTIC_OUTPUT_none;
if (const char *var = getenv ("GCC_EXTRA_DIAGNOSTIC_OUTPUT"))
@ -2627,6 +2628,16 @@ simple_diagnostic_path::add_thread_event (diagnostic_thread_id_t thread_id,
return diagnostic_event_id_t (m_events.length () - 1);
}
/* Mark the most recent event on this path (which must exist) as being
connected to the next one to be added. */
void
simple_diagnostic_path::connect_to_next_event ()
{
gcc_assert (m_events.length () > 0);
m_events[m_events.length () - 1]->connect_to_next_event ();
}
/* struct simple_diagnostic_event. */
/* simple_diagnostic_event's ctor. */
@ -2638,6 +2649,7 @@ simple_diagnostic_event (location_t loc,
const char *desc,
diagnostic_thread_id_t thread_id)
: m_loc (loc), m_fndecl (fndecl), m_depth (depth), m_desc (xstrdup (desc)),
m_connected_to_next_event (false),
m_thread_id (thread_id)
{
}

View File

@ -194,6 +194,7 @@ namespace json { class value; }
class diagnostic_client_data_hooks;
class logical_location;
class diagnostic_diagram;
class diagnostic_source_effect_info;
/* Abstract base class for a particular output format for diagnostics;
each value of -fdiagnostics-output-format= will have its own
@ -360,6 +361,11 @@ struct diagnostic_source_printing_options
/* Usable by plugins; if true, print a debugging ruler above the
source output. */
bool show_ruler_p;
/* When printing events in an inline path, should we print lines
visualizing links between related events (e.g. for CFG paths)?
Corresponds to -fdiagnostics-show-event-links. */
bool show_event_links_p;
};
/* This data structure bundles altogether any information relevant to
@ -433,7 +439,8 @@ public:
void maybe_show_locus (const rich_location &richloc,
diagnostic_t diagnostic_kind,
pretty_printer *pp);
pretty_printer *pp,
diagnostic_source_effect_info *effect_info);
void emit_diagram (const diagnostic_diagram &diagram);
@ -573,7 +580,8 @@ private:
void show_locus (const rich_location &richloc,
diagnostic_t diagnostic_kind,
pretty_printer *pp);
pretty_printer *pp,
diagnostic_source_effect_info *effect_info);
/* Data members.
Ideally, all of these would be private and have "m_" prefixes. */
@ -920,10 +928,11 @@ inline void
diagnostic_show_locus (diagnostic_context *context,
rich_location *richloc,
diagnostic_t diagnostic_kind,
pretty_printer *pp = nullptr)
pretty_printer *pp = nullptr,
diagnostic_source_effect_info *effect_info = nullptr)
{
gcc_assert (richloc);
context->maybe_show_locus (*richloc, diagnostic_kind, pp);
context->maybe_show_locus (*richloc, diagnostic_kind, pp, effect_info);
}
/* Because we read source files a second time after the frontend did it the

View File

@ -307,6 +307,7 @@ Objective-C and Objective-C++ Dialects}.
-fdiagnostics-format=@r{[}text@r{|}sarif-stderr@r{|}sarif-file@r{|}json@r{|}json-stderr@r{|}json-file@r{]}
-fno-diagnostics-json-formatting
-fno-diagnostics-show-option -fno-diagnostics-show-caret
-fno-diagnostics-show-event-links
-fno-diagnostics-show-labels -fno-diagnostics-show-line-numbers
-fno-diagnostics-show-cwe
-fno-diagnostics-show-rule
@ -5211,7 +5212,8 @@ options:
-fdiagnostics-color=never
-fdiagnostics-urls=never
-fdiagnostics-path-format=separate-events
-fdiagnostics-text-art-charset=none}
-fdiagnostics-text-art-charset=none
-fno-diagnostics-show-event-links}
In the future, if GCC changes the default appearance of its diagnostics, the
corresponding option to disable the new behavior will be added to this list.
@ -5446,6 +5448,31 @@ as the types of expressions:
This option suppresses the printing of these labels (in the example above,
the vertical bars and the ``char *'' and ``long int'' text).
@opindex fno-diagnostics-show-event-links
@opindex fdiagnostics-show-event-links
@item -fno-diagnostics-show-event-links
By default, when printing execution paths (via
@option{-fdiagnostics-path-format=inline-events}), GCC will print lines
connecting related events, such as the line connecting events 1 and 2 in:
@smallexample
3 | if (p)
| ^
| |
| (1) following `false' branch (when `p' is NULL)... ->-+
| |
| |
|+------------------------------------------------------------+
4 || return 0;
5 || return *p;
|| ~
|| |
|+-------->(2) ...to here
| (3) dereference of NULL `p'
@end smallexample
This option suppresses the printing of such connector lines.
@opindex fno-diagnostics-show-cwe
@opindex fdiagnostics-show-cwe
@item -fno-diagnostics-show-cwe

View File

@ -310,6 +310,7 @@ merge_and_complain (vec<cl_decoded_option> &decoded_options,
/* Fallthru. */
case OPT_fdiagnostics_show_caret:
case OPT_fdiagnostics_show_event_links:
case OPT_fdiagnostics_show_labels:
case OPT_fdiagnostics_show_line_numbers:
case OPT_fdiagnostics_show_option:
@ -726,6 +727,7 @@ append_compiler_options (obstack *argv_obstack, vec<cl_decoded_option> opts)
switch (option->opt_index)
{
case OPT_fdiagnostics_show_caret:
case OPT_fdiagnostics_show_event_links:
case OPT_fdiagnostics_show_labels:
case OPT_fdiagnostics_show_line_numbers:
case OPT_fdiagnostics_show_option:
@ -785,6 +787,7 @@ append_diag_options (obstack *argv_obstack, vec<cl_decoded_option> opts)
case OPT_fdiagnostics_color_:
case OPT_fdiagnostics_format_:
case OPT_fdiagnostics_show_caret:
case OPT_fdiagnostics_show_event_links:
case OPT_fdiagnostics_show_labels:
case OPT_fdiagnostics_show_line_numbers:
case OPT_fdiagnostics_show_option:

View File

@ -1090,7 +1090,8 @@ decode_cmdline_options_to_array (unsigned int argc, const char **argv,
"-fdiagnostics-color=never",
"-fdiagnostics-urls=never",
"-fdiagnostics-path-format=separate-events",
"-fdiagnostics-text-art-charset=none"
"-fdiagnostics-text-art-charset=none",
"-fno-diagnostics-show-event-links"
};
const int num_expanded = ARRAY_SIZE (expanded_args);
opt_array_len += num_expanded - 1;

View File

@ -2937,6 +2937,10 @@ common_handle_option (struct gcc_options *opts,
dc->m_source_printing.enabled = value;
break;
case OPT_fdiagnostics_show_event_links:
dc->m_source_printing.show_event_links_p = value;
break;
case OPT_fdiagnostics_show_labels:
dc->m_source_printing.show_labels_p = value;
break;
@ -3818,6 +3822,7 @@ gen_command_line_string (cl_decoded_option *options,
case OPT_fdiagnostics_show_location_:
case OPT_fdiagnostics_show_option:
case OPT_fdiagnostics_show_caret:
case OPT_fdiagnostics_show_event_links:
case OPT_fdiagnostics_show_labels:
case OPT_fdiagnostics_show_line_numbers:
case OPT_fdiagnostics_color_:

View File

@ -0,0 +1,61 @@
/* Verify that we print event links for the analyzer, using ASCII.
C only: we don't care about any C/C++ differences between source
locations here. */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events" } */
/* { dg-additional-options "-fdiagnostics-show-line-numbers" } */
/* { dg-additional-options "-fdiagnostics-show-caret" } */
/* { dg-additional-options "-fdiagnostics-show-event-links" } */
/* { dg-enable-nn-line-numbers "" } */
void test (int flag_a, int val, void *p)
{
if (flag_a)
__builtin_free (p);
switch (val)
{
default:
break;
case 41:
break;
case 42:
__builtin_free (p); /* { dg-warning "double-'free' of 'p'" } */
break;
case 43:
break;
}
}
/* { dg-begin-multiline-output "" }
NN | __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
'test': events 1-6
NN | if (flag_a)
| ^
| |
| (1) following 'true' branch (when 'flag_a != 0')... ->-+
| |
| |
|+------------------------------------------------------------+
NN || __builtin_free (p);
|| ~~~~~~~~~~~~~~~~~~
|| |
|+--->(2) ...to here
| (3) first 'free' here
NN | switch (val)
| ~~~~~~
| |
| (4) following 'case 42:' branch... ->-+
| |
......
| |
|+----------------------------------------+
NN || case 42:
|| ~~~~
|| |
|+--->(5) ...to here
NN | __builtin_free (p);
| ~~~~~~~~~~~~~~~~~~
| |
| (6) second 'free' here; first 'free' was at (3)
{ dg-end-multiline-output "" } */

View File

@ -0,0 +1,66 @@
/* Verify colorization of event links (using ASCII).
C only: we don't care about any C/C++ differences between source
locations here. */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events" } */
/* { dg-additional-options "-fdiagnostics-show-line-numbers" } */
/* { dg-additional-options "-fdiagnostics-show-caret" } */
/* { dg-additional-options "-fdiagnostics-show-event-links" } */
/* { dg-additional-options "-fdiagnostics-color=always" } */
/* { dg-enable-nn-line-numbers "" } */
void test (int flag_a, int val, void *p)
{
if (flag_a)
__builtin_free (p);
switch (val)
{
default:
break;
case 41:
break;
case 42:
__builtin_free (p);
break;
case 43:
break;
}
}
/* { dg-begin-multiline-output "" }
NN | __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
'test': events 1-6
NN | if (flag_a)
| ^
| |
| (1) following 'true' branch (when 'flag_a != 0')... ->-+
| |
| |
|+------------------------------------------------------------+
NN || __builtin_free (p);
|| ~~~~~~~~~~~~~~~~~~
|| |
|+--->(2) ...to here
| (3) first 'free' here
NN | switch (val)
| ~~~~~~
| |
| (4) following 'case 42:' branch... ->-+
| |
......
| |
|+----------------------------------------+
NN || case 42:
|| ~~~~
|| |
|+--->(5) ...to here
NN | __builtin_free (p);
| ~~~~~~~~~~~~~~~~~~
| |
| (6) second 'free' here; first 'free' was at (3)
{ dg-end-multiline-output "" } */
/* DejaGnu won't recognize the warning due to the colorization codes,
so skip it. */
/* { dg-prune-output ".*" } */

View File

@ -0,0 +1,55 @@
/* Verify that -fno-diagnostics-show-event-links works.
C only: we don't care about any C/C++ differences between source
locations here. */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events" } */
/* { dg-additional-options "-fdiagnostics-show-line-numbers" } */
/* { dg-additional-options "-fdiagnostics-show-caret" } */
/* { dg-additional-options "-fno-diagnostics-show-event-links" } */
/* { dg-enable-nn-line-numbers "" } */
void test (int flag_a, int val, void *p)
{
if (flag_a)
__builtin_free (p);
switch (val)
{
default:
break;
case 41:
break;
case 42:
__builtin_free (p); /* { dg-warning "double-'free' of 'p'" } */
break;
case 43:
break;
}
}
/* { dg-begin-multiline-output "" }
NN | __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
'test': events 1-6
NN | if (flag_a)
| ^
| |
| (1) following 'true' branch (when 'flag_a != 0')...
NN | __builtin_free (p);
| ~~~~~~~~~~~~~~~~~~
| |
| (2) ...to here
| (3) first 'free' here
NN | switch (val)
| ~~~~~~
| |
| (4) following 'case 42:' branch...
......
NN | case 42:
| ~~~~
| |
| (5) ...to here
NN | __builtin_free (p);
| ~~~~~~~~~~~~~~~~~~
| |
| (6) second 'free' here; first 'free' was at (3)
{ dg-end-multiline-output "" } */

View File

@ -0,0 +1,62 @@
/* Verify that we print event links for the analyzer, using Unicode.
C only: we don't care about any C/C++ differences between source
locations here. */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events" } */
/* { dg-additional-options "-fdiagnostics-show-line-numbers" } */
/* { dg-additional-options "-fdiagnostics-show-caret" } */
/* { dg-additional-options "-fdiagnostics-show-event-links" } */
/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
/* { dg-enable-nn-line-numbers "" } */
void test (int flag_a, int val, void *p)
{
if (flag_a)
__builtin_free (p);
switch (val)
{
default:
break;
case 41:
break;
case 42:
__builtin_free (p); /* { dg-warning "double-'free' of 'p'" } */
break;
case 43:
break;
}
}
/* { dg-begin-multiline-output "" }
NN | __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
'test': events 1-6
NN | if (flag_a)
| ^
| |
| (1) following 'true' branch (when 'flag_a != 0')... >
|
|
|
NN | __builtin_free (p);
| ~~~~~~~~~~~~~~~~~~
| |
|>(2) ...to here
| (3) first 'free' here
NN | switch (val)
| ~~~~~~
| |
| (4) following 'case 42:' branch... >
|
......
|
|
NN | case 42:
| ~~~~
| |
|>(5) ...to here
NN | __builtin_free (p);
| ~~~~~~~~~~~~~~~~~~
| |
| (6) second 'free' here; first 'free' was at (3)
{ dg-end-multiline-output "" } */

View File

@ -140,6 +140,21 @@ ascii_theme::get_cppchar (enum cell_kind kind) const
return '-';
case cell_kind::INTERPROCEDURAL_POP_FRAMES_RIGHT:
return '+';
case cell_kind::CFG_RIGHT:
return '-';
case cell_kind::CFG_FROM_RIGHT_TO_DOWN:
return '+';
case cell_kind::CFG_DOWN:
return '|';
case cell_kind::CFG_FROM_DOWN_TO_LEFT:
return '+';
case cell_kind::CFG_LEFT:
return '-';
case cell_kind::CFG_FROM_LEFT_TO_DOWN:
return '+';
case cell_kind::CFG_FROM_DOWN_TO_RIGHT:
return '+';
}
}
@ -210,5 +225,20 @@ unicode_theme::get_cppchar (enum cell_kind kind) const
return 0x2500; /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */
case cell_kind::INTERPROCEDURAL_POP_FRAMES_RIGHT:
return 0x2518; /* "┘": U+2518: BOX DRAWINGS LIGHT UP AND LEFT. */
case cell_kind::CFG_RIGHT:
return 0x2500; /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */
case cell_kind::CFG_FROM_RIGHT_TO_DOWN:
return 0x2510; /* "┐": U+2510: BOX DRAWINGS LIGHT DOWN AND LEFT */
case cell_kind::CFG_DOWN:
return 0x2502; /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */
case cell_kind::CFG_FROM_DOWN_TO_LEFT:
return 0x2518; /* "┘": U+2518: BOX DRAWINGS LIGHT UP AND LEFT. */
case cell_kind::CFG_LEFT:
return 0x2500; /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */
case cell_kind::CFG_FROM_LEFT_TO_DOWN:
return 0x250c; /* "┌" U+250C: BOX DRAWINGS LIGHT DOWN AND RIGHT */
case cell_kind::CFG_FROM_DOWN_TO_RIGHT:
return 0x2514; /* "└": U+2514: BOX DRAWINGS LIGHT UP AND RIGHT */
}
}

View File

@ -72,7 +72,16 @@ class theme
INTERPROCEDURAL_DEPTH_MARKER, /* e.g. "|". */
INTERPROCEDURAL_POP_FRAMES_LEFT, /* e.g. "<". */
INTERPROCEDURAL_POP_FRAMES_MIDDLE, /* e.g. "-". */
INTERPROCEDURAL_POP_FRAMES_RIGHT /* e.g. "+". */
INTERPROCEDURAL_POP_FRAMES_RIGHT, /* e.g. "+". */
/* CFG stuff. */
CFG_RIGHT, /* e.g. "-". */
CFG_FROM_RIGHT_TO_DOWN, /* e.g. "+". */
CFG_DOWN, /* e.g. "|". */
CFG_FROM_DOWN_TO_LEFT, /* e.g. "+". */
CFG_LEFT, /* e.g. "-". */
CFG_FROM_LEFT_TO_DOWN, /* e.g. "+". */
CFG_FROM_DOWN_TO_RIGHT /* e.g. "+". */
};
virtual ~theme () = default;

View File

@ -1029,6 +1029,8 @@ general_init (const char *argv0, bool init_signals)
global_dc->m_source_printing.enabled
= global_options_init.x_flag_diagnostics_show_caret;
global_dc->m_source_printing.show_event_links_p
= global_options_init.x_flag_diagnostics_show_event_links;
global_dc->m_source_printing.show_labels_p
= global_options_init.x_flag_diagnostics_show_labels;
global_dc->m_source_printing.show_line_numbers_p

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,7 @@ along with this program; see the file COPYING3. If not see
#define LIBCPP_RICH_LOCATION_H
class range_label;
class label_effects;
/* A hint to diagnostic_show_locus on how to print a source range within a
rich_location.
@ -641,6 +642,12 @@ class range_label
The RANGE_IDX is provided, allowing for range_label instances to be
shared by multiple ranges if need be (the "flyweight" design pattern). */
virtual label_text get_text (unsigned range_idx) const = 0;
/* Get any special effects for the label (e.g. links to other labels). */
virtual const label_effects *get_effects (unsigned /*range_idx*/) const
{
return nullptr;
}
};
/* A fix-it hint: a suggested insertion, replacement, or deletion of text.