aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2024-05-17 14:51:47 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2024-05-17 14:51:47 -0400
commit770657d02c986c8724214dba51f21bb3c299ebae (patch)
treebf526b3e02309e78abf63aacadff15b50f938e7a
parent6daed961542a9d5e83f9b94a3ee91f918f4268a5 (diff)
downloadgcc-770657d02c986c8724214dba51f21bb3c299ebae.zip
gcc-770657d02c986c8724214dba51f21bb3c299ebae.tar.gz
gcc-770657d02c986c8724214dba51f21bb3c299ebae.tar.bz2
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>
-rw-r--r--gcc/analyzer/checker-event.h3
-rw-r--r--gcc/analyzer/infinite-loop.cc18
-rw-r--r--gcc/common.opt4
-rw-r--r--gcc/diagnostic-label-effects.h58
-rw-r--r--gcc/diagnostic-path.h16
-rw-r--r--gcc/diagnostic-show-locus.cc387
-rw-r--r--gcc/diagnostic.cc12
-rw-r--r--gcc/diagnostic.h17
-rw-r--r--gcc/doc/invoke.texi29
-rw-r--r--gcc/lto-wrapper.cc3
-rw-r--r--gcc/opts-common.cc3
-rw-r--r--gcc/opts.cc5
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/event-links-ascii.c61
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/event-links-color.c66
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/event-links-disabled.c55
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/event-links-unicode.c62
-rw-r--r--gcc/text-art/theme.cc30
-rw-r--r--gcc/text-art/theme.h11
-rw-r--r--gcc/toplev.cc2
-rw-r--r--gcc/tree-diagnostic-path.cc1178
-rw-r--r--libcpp/include/rich-location.h7
21 files changed, 1959 insertions, 68 deletions
diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h
index d2fb87f..7a4510e 100644
--- a/gcc/analyzer/checker-event.h
+++ b/gcc/analyzer/checker-event.h
@@ -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;
diff --git a/gcc/analyzer/infinite-loop.cc b/gcc/analyzer/infinite-loop.cc
index e277a83..04346cd 100644
--- a/gcc/analyzer/infinite-loop.cc
+++ b/gcc/analyzer/infinite-loop.cc
@@ -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,
diff --git a/gcc/common.opt b/gcc/common.opt
index 40d90817..2c078fd 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -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.
diff --git a/gcc/diagnostic-label-effects.h b/gcc/diagnostic-label-effects.h
new file mode 100644
index 0000000..e640f3d
--- /dev/null
+++ b/gcc/diagnostic-label-effects.h
@@ -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 */
diff --git a/gcc/diagnostic-path.h b/gcc/diagnostic-path.h
index 696991c..982d68b 100644
--- a/gcc/diagnostic-path.h
+++ b/gcc/diagnostic-path.h
@@ -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;
diff --git a/gcc/diagnostic-show-locus.cc b/gcc/diagnostic-show-locus.cc
index f42006c..007acc4 100644
--- a/gcc/diagnostic-show-locus.cc
+++ b/gcc/diagnostic-show-locus.cc
@@ -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;
}
+/* 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::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. Otherwise, do nothing. */
+ 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) const
+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));
}
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index 6ffd623..1f30d1d 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -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)
{
}
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 065ac78..7431f5a 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -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
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index bcf518a..b9408ec 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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
diff --git a/gcc/lto-wrapper.cc b/gcc/lto-wrapper.cc
index cfded75..6dcf8b4 100644
--- a/gcc/lto-wrapper.cc
+++ b/gcc/lto-wrapper.cc
@@ -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:
diff --git a/gcc/opts-common.cc b/gcc/opts-common.cc
index 2d1e86f..14084d0 100644
--- a/gcc/opts-common.cc
+++ b/gcc/opts-common.cc
@@ -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;
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 14d1767..f80d5d4b 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -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_:
diff --git a/gcc/testsuite/gcc.dg/analyzer/event-links-ascii.c b/gcc/testsuite/gcc.dg/analyzer/event-links-ascii.c
new file mode 100644
index 0000000..fd68944
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/event-links-ascii.c
@@ -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 "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/event-links-color.c b/gcc/testsuite/gcc.dg/analyzer/event-links-color.c
new file mode 100644
index 0000000..3e5ef56
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/event-links-color.c
@@ -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 ".*" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/event-links-disabled.c b/gcc/testsuite/gcc.dg/analyzer/event-links-disabled.c
new file mode 100644
index 0000000..e9cbbfb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/event-links-disabled.c
@@ -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 "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/event-links-unicode.c b/gcc/testsuite/gcc.dg/analyzer/event-links-unicode.c
new file mode 100644
index 0000000..42f4b6f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/event-links-unicode.c
@@ -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 "" } */
diff --git a/gcc/text-art/theme.cc b/gcc/text-art/theme.cc
index cba4c58..1ee86c6 100644
--- a/gcc/text-art/theme.cc
+++ b/gcc/text-art/theme.cc
@@ -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 */
}
}
diff --git a/gcc/text-art/theme.h b/gcc/text-art/theme.h
index dd50f5a..e41fcc8 100644
--- a/gcc/text-art/theme.h
+++ b/gcc/text-art/theme.h
@@ -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;
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index bed1b0b..f715f97 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -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
diff --git a/gcc/tree-diagnostic-path.cc b/gcc/tree-diagnostic-path.cc
index 9ae5191..743a8c2 100644
--- a/gcc/tree-diagnostic-path.cc
+++ b/gcc/tree-diagnostic-path.cc
@@ -19,6 +19,9 @@ along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
+#define INCLUDE_ALGORITHM
+#define INCLUDE_MEMORY
+#define INCLUDE_STRING
#define INCLUDE_VECTOR
#include "system.h"
#include "coretypes.h"
@@ -34,6 +37,7 @@ along with GCC; see the file COPYING3. If not see
#include "gcc-rich-location.h"
#include "diagnostic-color.h"
#include "diagnostic-event-id.h"
+#include "diagnostic-label-effects.h"
#include "selftest.h"
#include "selftest-diagnostic.h"
#include "text-art/theme.h"
@@ -50,7 +54,7 @@ class path_label : public range_label
{
public:
path_label (const diagnostic_path *path, unsigned start_idx)
- : m_path (path), m_start_idx (start_idx)
+ : m_path (path), m_start_idx (start_idx), m_effects (*this)
{}
label_text get_text (unsigned range_idx) const final override
@@ -95,9 +99,53 @@ class path_label : public range_label
return result;
}
+ const label_effects *get_effects (unsigned /*range_idx*/) const
+ {
+ return &m_effects;
+ }
+
private:
+ class path_label_effects : public label_effects
+ {
+ public:
+ path_label_effects (const path_label &path_label)
+ : m_path_label (path_label)
+ {
+ }
+ bool has_in_edge (unsigned range_idx) const final override
+ {
+ if (const diagnostic_event *prev_event
+ = m_path_label.get_prev_event (range_idx))
+ return prev_event->connect_to_next_event_p ();
+ return false;
+ }
+ bool has_out_edge (unsigned range_idx) const final override
+ {
+ const diagnostic_event &event = m_path_label.get_event (range_idx);
+ return event.connect_to_next_event_p ();
+ }
+
+ private:
+ const path_label &m_path_label;
+ };
+
+ const diagnostic_event &get_event (unsigned range_idx) const
+ {
+ unsigned event_idx = m_start_idx + range_idx;
+ return m_path->get_event (event_idx);
+ }
+
+ const diagnostic_event *get_prev_event (unsigned range_idx) const
+ {
+ if (m_start_idx + range_idx == 0)
+ return nullptr;
+ unsigned event_idx = m_start_idx + range_idx - 1;
+ return &m_path->get_event (event_idx);
+ }
+
const diagnostic_path *m_path;
unsigned m_start_idx;
+ path_label_effects m_effects;
};
/* Return true if E1 and E2 can be consolidated into the same run of events
@@ -150,6 +198,7 @@ public:
per_thread_summary (label_text name, unsigned swimlane_idx)
: m_name (std::move (name)),
m_swimlane_idx (swimlane_idx),
+ m_last_event (nullptr),
m_min_depth (INT_MAX),
m_max_depth (INT_MIN)
{}
@@ -170,6 +219,7 @@ public:
private:
friend struct path_summary;
friend class thread_event_printer;
+ friend struct event_range;
const label_text m_name;
@@ -179,6 +229,9 @@ private:
// The event ranges specific to this thread:
auto_vec<event_range *> m_event_ranges;
+
+ const diagnostic_event *m_last_event;
+
int m_min_depth;
int m_max_depth;
};
@@ -188,9 +241,95 @@ private:
to print with a single call to diagnostic_show_locus. */
struct event_range
{
+ /* A struct for tracking the mergability of labels on a particular
+ source line. In particular, track information about links between
+ labels to ensure that we only consolidate events involving links
+ that the source-printing code is able to handle (splitting them
+ otherwise). */
+ struct per_source_line_info
+ {
+ void init (int line)
+ {
+ m_line = line;
+ m_has_in_edge = false;
+ m_has_out_edge = false;
+ m_min_label_source_column = INT_MAX;
+ m_max_label_source_column = INT_MIN;
+ }
+
+ /* Return true if our source-printing/labelling/linking code can handle
+ the events already on this source line, *and* a new event at COLUMN. */
+ bool
+ can_add_label_for_event_p (bool has_in_edge,
+ const diagnostic_event *prev_event,
+ bool has_out_edge,
+ int column) const
+ {
+ /* Any existing in-edge has to be the left-most label on its
+ source line. */
+ if (m_has_in_edge && column < m_min_label_source_column)
+ return false;
+ /* Any existing out-edge has to be the right-most label on its
+ source line. */
+ if (m_has_out_edge && column > m_max_label_source_column)
+ return false;
+ /* Can't have more than one in-edge. */
+ if (m_has_in_edge && has_in_edge)
+ return false;
+ /* Can't have more than one out-edge. */
+ if (m_has_out_edge && has_out_edge)
+ return false;
+
+ if (has_in_edge)
+ {
+ /* Any new in-edge needs to be the left-most label on its
+ source line. */
+ if (column > m_min_label_source_column)
+ return false;
+
+ gcc_assert (prev_event);
+ const location_t prev_loc = prev_event->get_location ();
+ expanded_location prev_exploc
+ = linemap_client_expand_location_to_spelling_point
+ (line_table, prev_loc, LOCATION_ASPECT_CARET);
+ /* The destination in-edge's line number has to be <= the
+ source out-edge's line number (if any). */
+ if (prev_exploc.line >= m_line)
+ return false;
+ }
+
+ /* Any new out-edge needs to be the right-most label on its
+ source line. */
+ if (has_out_edge)
+ if (column < m_max_label_source_column)
+ return false;
+
+ /* All checks passed; we can add the new event at COLUMN. */
+ return true;
+ }
+
+ void
+ add_label_for_event (bool has_in_edge, bool has_out_edge, int column)
+ {
+ if (has_in_edge)
+ m_has_in_edge = true;
+ if (has_out_edge)
+ m_has_out_edge = true;
+ m_min_label_source_column = std::min (m_min_label_source_column, column);
+ m_max_label_source_column = std::max (m_max_label_source_column, column);
+ }
+
+ int m_line;
+ bool m_has_in_edge;
+ bool m_has_out_edge;
+ int m_min_label_source_column;
+ int m_max_label_source_column;
+ };
+
event_range (const diagnostic_path *path, unsigned start_idx,
const diagnostic_event &initial_event,
- const per_thread_summary &t)
+ per_thread_summary &t,
+ bool show_event_links)
: m_path (path),
m_initial_event (initial_event),
m_fndecl (initial_event.get_fndecl ()),
@@ -199,8 +338,39 @@ struct event_range
m_path_label (path, start_idx),
m_richloc (initial_event.get_location (), &m_path_label),
m_thread_id (initial_event.get_thread_id ()),
- m_per_thread_summary (t)
- {}
+ m_per_thread_summary (t),
+ m_show_event_links (show_event_links)
+ {
+ if (m_show_event_links)
+ {
+ expanded_location exploc
+ = linemap_client_expand_location_to_spelling_point
+ (line_table, initial_event.get_location (), LOCATION_ASPECT_CARET);
+ per_source_line_info &source_line_info
+ = get_per_source_line_info (exploc.line);
+
+ const diagnostic_event *prev_thread_event = t.m_last_event;
+ const bool has_in_edge
+ = (prev_thread_event
+ ? prev_thread_event->connect_to_next_event_p ()
+ : false);
+ const bool has_out_edge = initial_event.connect_to_next_event_p ();
+
+ source_line_info.add_label_for_event
+ (has_in_edge, has_out_edge, exploc.column);
+ }
+ }
+
+ per_source_line_info &
+ get_per_source_line_info (int source_line)
+ {
+ bool existed = false;
+ per_source_line_info &result
+ = m_source_line_info_map.get_or_insert (source_line, &existed);
+ if (!existed)
+ result.init (source_line);
+ return result;
+ }
bool maybe_add_event (const diagnostic_event &new_ev, unsigned idx,
bool check_rich_locations)
@@ -208,18 +378,48 @@ struct event_range
if (!can_consolidate_events (m_initial_event, new_ev,
check_rich_locations))
return false;
+
+ /* Verify compatibility of the new label and existing labels
+ with respect to the link-printing code. */
+ expanded_location exploc
+ = linemap_client_expand_location_to_spelling_point
+ (line_table, new_ev.get_location (), LOCATION_ASPECT_CARET);
+ per_source_line_info &source_line_info
+ = get_per_source_line_info (exploc.line);
+ const diagnostic_event *prev_event = nullptr;
+ if (idx > 0)
+ prev_event = &m_path->get_event (idx - 1);
+ const bool has_in_edge = (prev_event
+ ? prev_event->connect_to_next_event_p ()
+ : false);
+ const bool has_out_edge = new_ev.connect_to_next_event_p ();
+ if (m_show_event_links)
+ if (!source_line_info.can_add_label_for_event_p
+ (has_in_edge, prev_event,
+ has_out_edge, exploc.column))
+ return false;
+
+ /* Potentially verify that the locations are sufficiently close. */
if (check_rich_locations)
if (!m_richloc.add_location_if_nearby (new_ev.get_location (),
false, &m_path_label))
return false;
+
m_end_idx = idx;
+ m_per_thread_summary.m_last_event = &new_ev;
+
+ if (m_show_event_links)
+ source_line_info.add_label_for_event
+ (has_in_edge, has_out_edge, exploc.column);
+
return true;
}
/* Print the events in this range to DC, typically as a single
call to the printer's diagnostic_show_locus. */
- void print (diagnostic_context *dc, pretty_printer *pp)
+ void print (diagnostic_context *dc, pretty_printer *pp,
+ diagnostic_source_effect_info *effect_info)
{
location_t initial_loc = m_initial_event.get_location ();
@@ -256,7 +456,8 @@ struct event_range
}
/* Call diagnostic_show_locus to show the events using labels. */
- diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH, pp);
+ diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH, pp,
+ effect_info);
/* If we have a macro expansion, show the expansion to the user. */
if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
@@ -275,7 +476,10 @@ struct event_range
path_label m_path_label;
gcc_rich_location m_richloc;
diagnostic_thread_id_t m_thread_id;
- const per_thread_summary &m_per_thread_summary;
+ per_thread_summary &m_per_thread_summary;
+ hash_map<int_hash<int, -1, -2>,
+ per_source_line_info> m_source_line_info_map;
+ bool m_show_event_links;
};
/* A struct for grouping together the events in a diagnostic_path into
@@ -284,7 +488,9 @@ struct event_range
struct path_summary
{
- path_summary (const diagnostic_path &path, bool check_rich_locations);
+ path_summary (const diagnostic_path &path,
+ bool check_rich_locations,
+ bool show_event_links = true);
unsigned get_num_ranges () const { return m_ranges.length (); }
bool multithreaded_p () const { return m_per_thread_summary.length () > 1; }
@@ -342,7 +548,8 @@ per_thread_summary::interprocedural_p () const
/* path_summary's ctor. */
path_summary::path_summary (const diagnostic_path &path,
- bool check_rich_locations)
+ bool check_rich_locations,
+ bool show_event_links)
{
const unsigned num_events = path.num_events ();
@@ -360,9 +567,11 @@ path_summary::path_summary (const diagnostic_path &path,
if (cur_event_range->maybe_add_event (event, idx, check_rich_locations))
continue;
- cur_event_range = new event_range (&path, idx, event, pts);
+ cur_event_range = new event_range (&path, idx, event, pts,
+ show_event_links);
m_ranges.safe_push (cur_event_range);
pts.m_event_ranges.safe_push (cur_event_range);
+ pts.m_last_event = &event;
}
}
@@ -428,9 +637,11 @@ public:
return nullptr;
}
- void print_swimlane_for_event_range (diagnostic_context *dc,
- pretty_printer *pp,
- event_range *range)
+ void
+ print_swimlane_for_event_range (diagnostic_context *dc,
+ pretty_printer *pp,
+ event_range *range,
+ diagnostic_source_effect_info *effect_info)
{
const char *const line_color = "path";
const char *start_line_color
@@ -508,7 +719,7 @@ public:
}
pp_set_prefix (pp, prefix);
pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
- range->print (dc, pp);
+ range->print (dc, pp, effect_info);
pp_set_prefix (pp, saved_prefix);
write_indent (pp, m_cur_indent + per_frame_indent);
@@ -518,7 +729,7 @@ public:
pp_newline (pp);
}
else
- range->print (dc, pp);
+ range->print (dc, pp, effect_info);
if (const event_range *next_range = get_any_next_range ())
{
@@ -648,6 +859,7 @@ print_path_summary_as_text (const path_summary *ps, diagnostic_context *dc,
unsigned i;
event_range *range;
+ int last_out_edge_column = -1;
FOR_EACH_VEC_ELT (ps->m_ranges, i, range)
{
const int swimlane_idx
@@ -662,7 +874,12 @@ print_path_summary_as_text (const path_summary *ps, diagnostic_context *dc,
pp_newline (pp);
}
thread_event_printer &tep = thread_event_printers[swimlane_idx];
- tep.print_swimlane_for_event_range (dc, pp, range);
+ /* Wire up any trailing out-edge from previous range to leading in-edge
+ of this range. */
+ diagnostic_source_effect_info effect_info;
+ effect_info.m_leading_in_edge_column = last_out_edge_column;
+ tep.print_swimlane_for_event_range (dc, pp, range, &effect_info);
+ last_out_edge_column = effect_info.m_trailing_out_edge_column;
}
}
@@ -721,7 +938,8 @@ default_tree_diagnostic_path_printer (diagnostic_context *context,
case DPF_INLINE_EVENTS:
{
/* Consolidate related events. */
- path_summary summary (*path, true);
+ path_summary summary (*path, true,
+ context->m_source_printing.show_event_links_p);
char *saved_prefix = pp_take_prefix (context->printer);
pp_set_prefix (context->printer, NULL);
print_path_summary_as_text (&summary, context,
@@ -776,6 +994,27 @@ default_tree_make_json_for_path (diagnostic_context *context,
namespace selftest {
+/* Return true iff all events in PATH have locations for which column data
+ is available, so that selftests that require precise string output can
+ bail out for awkward line_table cases. */
+
+static bool
+path_events_have_column_data_p (const diagnostic_path &path)
+{
+ for (unsigned idx = 0; idx < path.num_events (); idx++)
+ {
+ location_t event_loc = path.get_event (idx).get_location ();
+ if (line_table->get_pure_location (event_loc)
+ > LINE_MAP_MAX_LOCATION_WITH_COLS)
+ return false;
+ if (line_table->get_start (event_loc) > LINE_MAP_MAX_LOCATION_WITH_COLS)
+ return false;
+ if (line_table->get_finish (event_loc) > LINE_MAP_MAX_LOCATION_WITH_COLS)
+ return false;
+ }
+ return true;
+}
+
/* A subclass of simple_diagnostic_path that adds member functions
for adding test events. */
@@ -1172,20 +1411,909 @@ test_recursion (pretty_printer *event_pp)
}
}
+/* Helper class for writing tests of control flow visualization. */
+
+class control_flow_test
+{
+public:
+ control_flow_test (const location &loc,
+ const line_table_case &case_,
+ const char *content)
+ : m_tmp_file (loc, ".c", content,
+ /* gcc_rich_location::add_location_if_nearby implicitly
+ uses global_dc's file_cache, so we need to evict
+ tmp when we're done. */
+ &global_dc->get_file_cache ()),
+ m_ltt (case_)
+ {
+ m_ord_map
+ = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
+ m_tmp_file.get_filename (), 0));
+ linemap_line_start (line_table, 1, 100);
+ }
+
+ location_t get_line_and_column (int line, int column)
+ {
+ return linemap_position_for_line_and_column (line_table, m_ord_map,
+ line, column);
+ }
+
+ location_t get_line_and_columns (int line, int first_column, int last_column)
+ {
+ return get_line_and_columns (line,
+ first_column, first_column, last_column);
+ }
+
+ location_t get_line_and_columns (int line,
+ int first_column,
+ int caret_column,
+ int last_column)
+ {
+ return make_location (get_line_and_column (line, caret_column),
+ get_line_and_column (line, first_column),
+ get_line_and_column (line, last_column));
+ }
+
+private:
+ temp_source_file m_tmp_file;
+ line_table_test m_ltt;
+ const line_map_ordinary *m_ord_map;
+};
+
+/* Example of event edges where all events can go in the same layout,
+ testing the 6 combinations of:
+ - ASCII vs Unicode vs event links off
+ - line numbering on and off. */
+
+static void
+test_control_flow_1 (const line_table_case &case_,
+ pretty_printer *event_pp)
+{
+ /* Create a tempfile and write some text to it.
+ ...000000000111111111122222222223333333333.
+ ...123456789012345678901234567890123456789. */
+ const char *content
+ = ("int test (int *p)\n" /* line 1. */
+ "{\n" /* line 2. */
+ " if (p)\n" /* line 3. */
+ " return 0;\n" /* line 4. */
+ " return *p;\n" /* line 5. */
+ "}\n"); /* line 6. */
+
+ control_flow_test t (SELFTEST_LOCATION, case_, content);
+
+ const location_t conditional = t.get_line_and_column (3, 7);
+ const location_t cfg_dest = t.get_line_and_column (5, 10);
+
+ test_diagnostic_path path (event_pp);
+ path.add_event (conditional, NULL_TREE, 0,
+ "following %qs branch (when %qs is NULL)...",
+ "false", "p");
+ path.connect_to_next_event ();
+
+ path.add_event (cfg_dest, NULL_TREE, 0,
+ "...to here");
+ path.add_event (cfg_dest, NULL_TREE, 0,
+ "dereference of NULL %qs",
+ "p");
+
+ if (!path_events_have_column_data_p (path))
+ return;
+
+ path_summary summary (path, true /*false*/);
+
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
+ dc.m_source_printing.show_event_links_p = true;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-3\n"
+ "FILENAME:3:7:\n"
+ " if (p)\n"
+ " ^\n"
+ " |\n"
+ " (1) following `false' branch (when `p' is NULL)... ->-+\n"
+ " |\n"
+ "FILENAME:5:10:\n"
+ " |\n"
+ "+------------------------------------------------------------+\n"
+ "| return *p;\n"
+ "| ~\n"
+ "| |\n"
+ "+-------->(2) ...to here\n"
+ " (3) dereference of NULL `p'\n",
+ pp_formatted_text (dc.printer));
+ }
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
+ dc.m_source_printing.show_event_links_p = false;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-3\n"
+ "FILENAME:3:7:\n"
+ " if (p)\n"
+ " ^\n"
+ " |\n"
+ " (1) following `false' branch (when `p' is NULL)...\n"
+ "FILENAME:5:10:\n"
+ " return *p;\n"
+ " ~\n"
+ " |\n"
+ " (2) ...to here\n"
+ " (3) dereference of NULL `p'\n",
+ pp_formatted_text (dc.printer));
+ }
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
+ dc.m_source_printing.show_line_numbers_p = true;
+ dc.m_source_printing.show_event_links_p = true;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-3\n"
+ "FILENAME:3:7:\n"
+ " 3 | if (p)\n"
+ " | ^\n"
+ " | |\n"
+ " | (1) following `false' branch (when `p' is NULL)... ->-+\n"
+ " | |\n"
+ " | |\n"
+ " |+------------------------------------------------------------+\n"
+ " 4 || return 0;\n"
+ " 5 || return *p;\n"
+ " || ~\n"
+ " || |\n"
+ " |+-------->(2) ...to here\n"
+ " | (3) dereference of NULL `p'\n",
+ pp_formatted_text (dc.printer));
+ }
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
+ dc.m_source_printing.show_line_numbers_p = true;
+ dc.m_source_printing.show_event_links_p = false;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-3\n"
+ "FILENAME:3:7:\n"
+ " 3 | if (p)\n"
+ " | ^\n"
+ " | |\n"
+ " | (1) following `false' branch (when `p' is NULL)...\n"
+ " 4 | return 0;\n"
+ " 5 | return *p;\n"
+ " | ~\n"
+ " | |\n"
+ " | (2) ...to here\n"
+ " | (3) dereference of NULL `p'\n",
+ pp_formatted_text (dc.printer));
+ }
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
+ dc.m_source_printing.show_event_links_p = true;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-3\n"
+ "FILENAME:3:7:\n"
+ " if (p)\n"
+ " ^\n"
+ " |\n"
+ " (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
+ " │\n"
+ "FILENAME:5:10:\n"
+ " │\n"
+ "┌────────────────────────────────────────────────────────────┘\n"
+ "│ return *p;\n"
+ "│ ~\n"
+ "│ |\n"
+ "└────────>(2) ...to here\n"
+ " (3) dereference of NULL `p'\n",
+ pp_formatted_text (dc.printer));
+ }
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
+ dc.m_source_printing.show_event_links_p = true;
+ dc.m_source_printing.show_line_numbers_p = true;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-3\n"
+ "FILENAME:3:7:\n"
+ " 3 | if (p)\n"
+ " | ^\n"
+ " | |\n"
+ " | (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
+ " | │\n"
+ " | │\n"
+ " |┌────────────────────────────────────────────────────────────┘\n"
+ " 4 |│ return 0;\n"
+ " 5 |│ return *p;\n"
+ " |│ ~\n"
+ " |│ |\n"
+ " |└────────>(2) ...to here\n"
+ " | (3) dereference of NULL `p'\n",
+ pp_formatted_text (dc.printer));
+ }
+}
+
+/* Complex example involving a backedge. */
+
+static void
+test_control_flow_2 (const line_table_case &case_,
+ pretty_printer *event_pp)
+{
+ /* Create a tempfile and write some text to it.
+ ...000000000111111111122222222223333333333.
+ ...123456789012345678901234567890123456789. */
+ const char *content
+ = ("int for_loop_noop_next (struct node *n)\n" /* <--------- line 1. */
+ "{\n" /* <----------------------------------------------- line 2. */
+ " int sum = 0;\n" /* <---------------------------------- line 3. */
+ " for (struct node *iter = n; iter; iter->next)\n" /* <- line 4. */
+ " sum += n->val;\n" /* <------------------------------ line 5. */
+ " return sum;\n" /* <----------------------------------- line 6. */
+ "}\n"); /* <-------------------------------------------- line 7. */
+ /* Adapted from infinite-loop-linked-list.c where
+ "iter->next" should be "iter = iter->next". */
+
+ control_flow_test t (SELFTEST_LOCATION, case_, content);
+
+ const location_t iter_test = t.get_line_and_columns (4, 31, 34);
+ const location_t loop_body_start = t.get_line_and_columns (5, 12, 17);
+ const location_t loop_body_end = t.get_line_and_columns (5, 5, 9, 17);
+
+ test_diagnostic_path path (event_pp);
+ path.add_event (iter_test, NULL_TREE, 0, "infinite loop here");
+
+ path.add_event (iter_test, NULL_TREE, 0, "looping from here...");
+ path.connect_to_next_event ();
+
+ path.add_event (loop_body_start, NULL_TREE, 0, "...to here");
+
+ path.add_event (loop_body_end, NULL_TREE, 0, "looping back...");
+ path.connect_to_next_event ();
+
+ path.add_event (iter_test, NULL_TREE, 0, "...to here");
+
+ if (!path_events_have_column_data_p (path))
+ return;
+
+ path_summary summary (path, true);
+
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
+ dc.m_source_printing.show_event_links_p = true;
+ dc.m_source_printing.show_line_numbers_p = true;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-3\n"
+ "FILENAME:4:31:\n"
+ " 4 | for (struct node *iter = n; iter; iter->next)\n"
+ " | ^~~~\n"
+ " | |\n"
+ " | (1) infinite loop here\n"
+ " | (2) looping from here... ->-+\n"
+ " | |\n"
+ " | |\n"
+ " |+----------------------------------------------------------+\n"
+ " 5 || sum += n->val;\n"
+ " || ~~~~~~ \n"
+ " || |\n"
+ " |+---------->(3) ...to here\n"
+ /* We need to start an new event_range here as event (4) is to the
+ left of event (3), and thus (4) would mess up the in-edge to (3). */
+ " event 4\n"
+ " 5 | sum += n->val;\n"
+ " | ~~~~^~~~~~~~~\n"
+ " | |\n"
+ " | (4) looping back... ->-+\n"
+ " | |\n"
+ /* We need to start an new event_range here as event (4) with an
+ out-edge is on a later line (line 5) than its destination event (5),
+ on line 4. */
+ " event 5\n"
+ " | |\n"
+ " |+-------------------------------+\n"
+ " 4 || for (struct node *iter = n; iter; iter->next)\n"
+ " || ^~~~\n"
+ " || |\n"
+ " |+----------------------------->(5) ...to here\n",
+ pp_formatted_text (dc.printer));
+ }
+}
+
+/* Complex example involving a backedge and both an in-edge and out-edge
+ on the same line. */
+
+static void
+test_control_flow_3 (const line_table_case &case_,
+ pretty_printer *event_pp)
+{
+ /* Create a tempfile and write some text to it.
+ ...000000000111111111122222222223333333333.
+ ...123456789012345678901234567890123456789. */
+ const char *content
+ = ("void test_missing_comparison_in_for_condition_1 (int n)\n"
+ "{\n" /* <------------------------- line 2. */
+ " for (int i = 0; n; i++)\n" /* <- line 3. */
+ " {\n" /* <--------------------- line 4. */
+ " }\n" /* <--------------------- line 5. */
+ "}\n"); /* <----------------------- line 6. */
+ /* Adapted from infinite-loop-1.c where the condition should have been
+ "i < n", rather than just "n". */
+
+ control_flow_test t (SELFTEST_LOCATION, case_, content);
+
+ const location_t iter_test = t.get_line_and_column (3, 19);
+ const location_t iter_next = t.get_line_and_columns (3, 22, 24);
+
+ test_diagnostic_path path (event_pp);
+ path.add_event (iter_test, NULL_TREE, 0, "infinite loop here");
+
+ path.add_event (iter_test, NULL_TREE, 0, "looping from here...");
+ path.connect_to_next_event ();
+
+ path.add_event (iter_next, NULL_TREE, 0, "...to here");
+
+ path.add_event (iter_next, NULL_TREE, 0, "looping back...");
+ path.connect_to_next_event ();
+
+ path.add_event (iter_test, NULL_TREE, 0, "...to here");
+
+ if (!path_events_have_column_data_p (path))
+ return;
+
+ path_summary summary (path, true);
+
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
+ dc.m_source_printing.show_event_links_p = true;
+ dc.m_source_printing.show_line_numbers_p = true;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-2\n"
+ "FILENAME:3:19:\n"
+ " 3 | for (int i = 0; n; i++)\n"
+ " | ^\n"
+ " | |\n"
+ " | (1) infinite loop here\n"
+ " | (2) looping from here... ->-+\n"
+ " | |\n"
+ " events 3-4\n"
+ " | |\n"
+ " |+----------------------------------------------+\n"
+ " 3 || for (int i = 0; n; i++)\n"
+ " || ^~~\n"
+ " || |\n"
+ " |+-------------------->(3) ...to here\n"
+ " | (4) looping back... ->-+\n"
+ " | |\n"
+ /* We need to start an new event_range here as event (4) with an
+ out-edge is on the same line as its destination event (5), but
+ to the right, which we can't handle as a single event_range. */
+ " event 5\n"
+ " | |\n"
+ " |+--------------------------------------------+\n"
+ " 3 || for (int i = 0; n; i++)\n"
+ " || ^\n"
+ " || |\n"
+ " |+----------------->(5) ...to here\n",
+ pp_formatted_text (dc.printer));
+ }
+}
+
+/* Implementation of ASSERT_CFG_EDGE_PATH_STREQ. */
+
+static void
+assert_cfg_edge_path_streq (const location &loc,
+ pretty_printer *event_pp,
+ const location_t src_loc,
+ const location_t dst_loc,
+ const char *expected_str)
+{
+ test_diagnostic_path path (event_pp);
+ path.add_event (src_loc, NULL_TREE, 0, "from here...");
+ path.connect_to_next_event ();
+
+ path.add_event (dst_loc, NULL_TREE, 0, "...to here");
+
+ if (!path_events_have_column_data_p (path))
+ return;
+
+ path_summary summary (path, true);
+
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
+ dc.m_source_printing.show_event_links_p = true;
+ dc.m_source_printing.show_line_numbers_p = true;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ_AT (loc, expected_str,
+ pp_formatted_text (dc.printer));
+}
+
+/* Assert that if we make a path with an event with "from here..." at SRC_LOC
+ leading to an event "...to here" at DST_LOC that we print the path
+ as EXPECTED_STR. */
+
+#define ASSERT_CFG_EDGE_PATH_STREQ(SRC_LOC, DST_LOC, EXPECTED_STR) \
+ assert_cfg_edge_path_streq ((SELFTEST_LOCATION), (event_pp), \
+ (SRC_LOC), (DST_LOC), (EXPECTED_STR))
+
+/* Various examples of edge, trying to cover all combinations of:
+ - relative x positive of src label and dst label
+ - relative y position of labels:
+ - on same line
+ - on next line
+ - on line after next
+ - big gap, where src is before dst
+ - big gap, where src is after dst
+ and other awkward cases. */
+
+static void
+test_control_flow_4 (const line_table_case &case_,
+ pretty_printer *event_pp)
+{
+ std::string many_lines;
+ for (int i = 1; i <= 100; i++)
+ /* ............000000000111
+ ............123456789012. */
+ many_lines += "LHS RHS\n";
+ control_flow_test t (SELFTEST_LOCATION, case_, many_lines.c_str ());
+
+ /* Same line. */
+ {
+ /* LHS -> RHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (3, 1, 3),
+ t.get_line_and_columns (3, 10, 12),
+ (" event 1\n"
+ "FILENAME:3:1:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ " event 2\n"
+ " | |\n"
+ " |+--------------------+\n"
+ " 3 ||LHS RHS\n"
+ " || ^~~\n"
+ " || |\n"
+ " |+-------->(2) ...to here\n"));
+
+ /* RHS -> LHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (3, 10, 12),
+ t.get_line_and_columns (3, 1, 3),
+ (" event 1\n"
+ "FILENAME:3:10:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ " event 2\n"
+ " | |\n"
+ " |+-----------------------------+\n"
+ " 3 ||LHS RHS\n"
+ " ||^~~\n"
+ " |||\n"
+ " |+(2) ...to here\n"));
+ }
+
+ /* Next line. */
+ {
+ /* LHS -> RHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (3, 1, 3),
+ t.get_line_and_columns (4, 5, 7),
+ (" events 1-2\n"
+ "FILENAME:3:1:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ " | |\n"
+ " |+--------------------+\n"
+ " 4 ||LHS RHS\n"
+ " || ~~~\n"
+ " || |\n"
+ " |+--->(2) ...to here\n"));
+
+ /* RHS -> LHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (3, 10, 12),
+ t.get_line_and_columns (4, 1, 3),
+ (" events 1-2\n"
+ "FILENAME:3:10:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ " | |\n"
+ " |+-----------------------------+\n"
+ " 4 ||LHS RHS\n"
+ " ||~~~ \n"
+ " |||\n"
+ " |+(2) ...to here\n"));
+ }
+
+ /* Line after next. */
+ {
+ /* LHS -> RHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (3, 1, 3),
+ t.get_line_and_columns (5, 10, 12),
+ (" events 1-2\n"
+ "FILENAME:3:1:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ " | |\n"
+ " |+--------------------+\n"
+ " 4 ||LHS RHS\n"
+ " 5 ||LHS RHS\n"
+ " || ~~~\n"
+ " || |\n"
+ " |+-------->(2) ...to here\n"));
+
+ /* RHS -> LHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (3, 10, 12),
+ t.get_line_and_columns (5, 1, 3),
+ (" events 1-2\n"
+ "FILENAME:3:10:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ " | |\n"
+ " |+-----------------------------+\n"
+ " 4 ||LHS RHS\n"
+ " 5 ||LHS RHS\n"
+ " ||~~~ \n"
+ " |||\n"
+ " |+(2) ...to here\n"));
+ }
+
+ /* Big gap, increasing line number. */
+ {
+ /* LHS -> RHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (3, 1, 3),
+ t.get_line_and_columns (97, 10, 12),
+ (" events 1-2\n"
+ "FILENAME:3:1:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ "......\n"
+ " | |\n"
+ " |+--------------------+\n"
+ " 97 ||LHS RHS\n"
+ " || ~~~\n"
+ " || |\n"
+ " |+-------->(2) ...to here\n"));
+
+ /* RHS -> LHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (3, 10, 12),
+ t.get_line_and_columns (97, 1, 3),
+ (" events 1-2\n"
+ "FILENAME:3:10:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ "......\n"
+ " | |\n"
+ " |+-----------------------------+\n"
+ " 97 ||LHS RHS\n"
+ " ||~~~ \n"
+ " |||\n"
+ " |+(2) ...to here\n"));
+ }
+
+ /* Big gap, decreasing line number. */
+ {
+ /* LHS -> RHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (97, 1, 3),
+ t.get_line_and_columns (3, 10, 12),
+ (" event 1\n"
+ "FILENAME:97:1:\n"
+ " 97 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ " event 2\n"
+ " | |\n"
+ " |+--------------------+\n"
+ " 3 ||LHS RHS\n"
+ " || ^~~\n"
+ " || |\n"
+ " |+-------->(2) ...to here\n"));
+
+ /* RHS -> LHS. */
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (97, 10, 12),
+ t.get_line_and_columns (3, 1, 3),
+ (" event 1\n"
+ "FILENAME:97:10:\n"
+ " 97 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ " event 2\n"
+ " | |\n"
+ " |+-----------------------------+\n"
+ " 3 ||LHS RHS\n"
+ " ||^~~\n"
+ " |||\n"
+ " |+(2) ...to here\n"));
+ }
+
+ /* Unknown src. */
+ {
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (UNKNOWN_LOCATION,
+ t.get_line_and_columns (3, 10, 12),
+ (" event 1\n"
+ " (1): from here...\n"
+ " event 2\n"
+ "FILENAME:3:10:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " |+-------->(2) ...to here\n"));
+ }
+
+ /* Unknown dst. */
+ {
+ ASSERT_CFG_EDGE_PATH_STREQ
+ (t.get_line_and_columns (3, 1, 3),
+ UNKNOWN_LOCATION,
+ (" event 1\n"
+ "FILENAME:3:1:\n"
+ " 3 | LHS RHS\n"
+ " | ^~~\n"
+ " | |\n"
+ " | (1) from here... ->-+\n"
+ " | |\n"
+ " event 2\n"
+ "FILENAME:\n"
+ " (2): ...to here\n"));
+ }
+}
+
+/* Another complex example, adapted from data-model-20.c. */
+
+static void
+test_control_flow_5 (const line_table_case &case_,
+ pretty_printer *event_pp)
+{
+ /* Create a tempfile and write some text to it.
+ ...000000000111111111122222222223333333333444444444455555555556666666666.
+ ...123456789012345678901234567890123456789012345678901234567890123456789. */
+ const char *content
+ = (" if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
+ " return NULL;\n" /* <------------------------- line 2. */
+ "\n" /* <----------------------------------------- line 3. */
+ " for (i = 0; i < n; i++) {\n" /* <-------------- line 4. */
+ " if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n");
+
+ control_flow_test t (SELFTEST_LOCATION, case_, content);
+
+ test_diagnostic_path path (event_pp);
+ /* (1) */
+ path.add_event (t.get_line_and_column (1, 6), NULL_TREE, 0,
+ "following %qs branch (when %qs is non-NULL)...",
+ "false", "arr");
+ path.connect_to_next_event ();
+
+ /* (2) */
+ path.add_event (t.get_line_and_columns (4, 8, 10, 12), NULL_TREE, 0,
+ "...to here");
+
+ /* (3) */
+ path.add_event (t.get_line_and_columns (4, 15, 17, 19), NULL_TREE, 0,
+ "following %qs branch (when %qs)...",
+ "true", "i < n");
+ path.connect_to_next_event ();
+
+ /* (4) */
+ path.add_event (t.get_line_and_column (5, 13), NULL_TREE, 0,
+ "...to here");
+
+ /* (5) */
+ path.add_event (t.get_line_and_columns (5, 33, 58), NULL_TREE, 0,
+ "allocated here");
+
+ if (!path_events_have_column_data_p (path))
+ return;
+
+ path_summary summary (path, true);
+
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
+ dc.m_source_printing.show_event_links_p = true;
+ dc.m_source_printing.show_line_numbers_p = true;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-5\n"
+ "FILENAME:1:6:\n"
+ " 1 | if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
+ " | ^\n"
+ " | |\n"
+ " | (1) following `false' branch (when `arr' is non-NULL)... ->-+\n"
+ " | |\n"
+ "......\n"
+ " | |\n"
+ " |+-----------------------------------------------------------------+\n"
+ " 4 || for (i = 0; i < n; i++) {\n"
+ " || ~~~~~ ~~~~~\n"
+ " || | |\n"
+ " || | (3) following `true' branch (when `i < n')... ->-+\n"
+ " |+-------->(2) ...to here |\n"
+ " | |\n"
+ " | |\n"
+ " |+-----------------------------------------------------------------+\n"
+ " 5 || if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n"
+ " || ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ " || | |\n"
+ " |+----------->(4) ...to here (5) allocated here\n",
+ pp_formatted_text (dc.printer));
+ }
+}
+
+/* Another complex example, adapted from loop-3.c. */
+
+static void
+test_control_flow_6 (const line_table_case &case_,
+ pretty_printer *event_pp)
+{
+ /* Create a tempfile and write some text to it.
+ ...000000000111111111122222222223333333.
+ ...123456789012345678901234567890123456. */
+ const char *content
+ = ("#include <stdlib.h>\n" /* <------------------ line 1. */
+ "\n" /* <------------------------------------- line 2. */
+ "void test(int c)\n" /* <--------------------- line 3. */
+ "{\n" /* <------------------------------------ line 4. */
+ " int i;\n" /* <----------------------------- line 5. */
+ " char *buffer = (char*)malloc(256);\n" /* <- line 6. */
+ "\n" /* <------------------------------------- line 7. */
+ " for (i=0; i<255; i++) {\n" /* <------------ line 8. */
+ " buffer[i] = c;\n" /* <------------------- line 9. */
+ "\n" /* <------------------------------------- line 10. */
+ " free(buffer);\n" /* <-------------------- line 11. */
+ " }\n"); /* <-------------------------------- line 12. */
+
+ control_flow_test t (SELFTEST_LOCATION, case_, content);
+
+ test_diagnostic_path path (event_pp);
+ /* (1) */
+ path.add_event (t.get_line_and_columns (6, 25, 35), NULL_TREE, 0,
+ "allocated here");
+
+ /* (2) */
+ path.add_event (t.get_line_and_columns (8, 13, 14, 17), NULL_TREE, 0,
+ "following %qs branch (when %qs)...",
+ "true", "i <= 254");
+ path.connect_to_next_event ();
+
+ /* (3) */
+ path.add_event (t.get_line_and_columns (9, 5, 15, 17), NULL_TREE, 0,
+ "...to here");
+
+ /* (4) */
+ path.add_event (t.get_line_and_columns (8, 13, 14, 17), NULL_TREE, 0,
+ "following %qs branch (when %qs)...",
+ "true", "i <= 254");
+ path.connect_to_next_event ();
+
+ /* (5) */
+ path.add_event (t.get_line_and_columns (9, 5, 15, 17), NULL_TREE, 0,
+ "...to here");
+
+ if (!path_events_have_column_data_p (path))
+ return;
+
+ path_summary summary (path, true);
+
+ {
+ test_diagnostic_context dc;
+ dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
+ dc.m_source_printing.show_event_links_p = true;
+ dc.m_source_printing.show_line_numbers_p = true;
+ print_path_summary_as_text (&summary, &dc, false);
+ ASSERT_STREQ
+ (" events 1-3\n"
+ "FILENAME:6:25:\n"
+ " 6 | char *buffer = (char*)malloc(256);\n"
+ " | ^~~~~~~~~~~\n"
+ " | |\n"
+ " | (1) allocated here\n"
+ " 7 | \n"
+ " 8 | for (i=0; i<255; i++) {\n"
+ " | ~~~~~ \n"
+ " | |\n"
+ " | (2) following `true' branch (when `i <= 254')... ->-+\n"
+ " | |\n"
+ " | |\n"
+ " |+-----------------------------------------------------------------+\n"
+ " 9 || buffer[i] = c;\n"
+ " || ~~~~~~~~~~~~~ \n"
+ " || |\n"
+ " |+------------->(3) ...to here\n"
+ " events 4-5\n"
+ " 8 | for (i=0; i<255; i++) {\n"
+ " | ~^~~~\n"
+ " | |\n"
+ " | (4) following `true' branch (when `i <= 254')... ->-+\n"
+ " | |\n"
+ " | |\n"
+ " |+-----------------------------------------------------------------+\n"
+ " 9 || buffer[i] = c;\n"
+ " || ~~~~~~~~~~~~~\n"
+ " || |\n"
+ " |+------------->(5) ...to here\n",
+ pp_formatted_text (dc.printer));
+ }
+}
+
+static void
+control_flow_tests (const line_table_case &case_)
+{
+ std::unique_ptr<pretty_printer> event_pp
+ = std::unique_ptr<pretty_printer> (global_dc->printer->clone ());
+ pp_show_color (event_pp) = 0;
+
+ test_control_flow_1 (case_, event_pp.get ());
+ test_control_flow_2 (case_, event_pp.get ());
+ test_control_flow_3 (case_, event_pp.get ());
+ test_control_flow_4 (case_, event_pp.get ());
+ test_control_flow_5 (case_, event_pp.get ());
+ test_control_flow_6 (case_, event_pp.get ());
+}
+
/* Run all of the selftests within this file. */
void
tree_diagnostic_path_cc_tests ()
{
+ /* In a few places we use the global dc's printer to determine
+ colorization so ensure this off during the tests. */
+ bool saved_show_color = pp_show_color (global_dc->printer);
+ pp_show_color (global_dc->printer) = false;
+
auto_fix_quotes fix_quotes;
- pretty_printer *event_pp = global_dc->printer->clone ();
- pp_show_color (event_pp) = 0;
- test_empty_path (event_pp);
- test_intraprocedural_path (event_pp);
- test_interprocedural_path_1 (event_pp);
- test_interprocedural_path_2 (event_pp);
- test_recursion (event_pp);
- delete event_pp;
+ std::unique_ptr<pretty_printer> event_pp
+ = std::unique_ptr<pretty_printer> (global_dc->printer->clone ());
+ test_empty_path (event_pp.get ());
+ test_intraprocedural_path (event_pp.get ());
+ test_interprocedural_path_1 (event_pp.get ());
+ test_interprocedural_path_2 (event_pp.get ());
+ test_recursion (event_pp.get ());
+ for_each_line_table_case (control_flow_tests);
+
+ pp_show_color (global_dc->printer) = saved_show_color;
}
} // namespace selftest
diff --git a/libcpp/include/rich-location.h b/libcpp/include/rich-location.h
index 94c6e44..a2ece8b 100644
--- a/libcpp/include/rich-location.h
+++ b/libcpp/include/rich-location.h
@@ -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.