Add diagnostic paths

This patch adds support for associating a "diagnostic_path" with a
diagnostic: a sequence of events predicted by the compiler that leads to
the problem occurring, with their locations in the user's source,
text descriptions, and stack information (for handling interprocedural
paths).

For example, the following (hypothetical) error has a 3-event
intraprocedural path:

test.c: In function 'demo':
test.c:29:5: error: passing NULL as argument 1 to 'PyList_Append' which
  requires a non-NULL parameter
   29 |     PyList_Append(list, item);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~
  'demo': events 1-3
     |
     |   25 |   list = PyList_New(0);
     |      |          ^~~~~~~~~~~~~
     |      |          |
     |      |          (1) when 'PyList_New' fails, returning NULL
     |   26 |
     |   27 |   for (i = 0; i < count; i++) {
     |      |   ~~~
     |      |   |
     |      |   (2) when 'i < count'
     |   28 |     item = PyLong_FromLong(random());
     |   29 |     PyList_Append(list, item);
     |      |     ~~~~~~~~~~~~~~~~~~~~~~~~~
     |      |     |
     |      |     (3) when calling 'PyList_Append', passing NULL from (1) as argument 1
     |

The patch adds a new "%@" format code for printing event IDs, so that
in the above, the description of event (3) mentions event (1), showing
the user where the bogus NULL value comes from (the event IDs are
colorized to draw the user's attention to them).

There is a separation between data vs presentation: the above shows how
the diagnostic-printing code has consolidated the path into a single run
of events, since all the events are near each other and within the same
function; more complicated examples (such as interprocedural paths)
might be printed as multiple runs of events.

Examples of how interprocedural paths are printed can be seen in the
test suite (which uses a plugin to exercise the code without relying
on specific warnings using this functionality).

Other output formats include
- JSON,
- printing each event as a separate "note", and
- to not emit paths.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add tree-diagnostic-path.o.
	* common.opt (fdiagnostics-path-format=): New option.
	(diagnostic_path_format): New enum.
	(fdiagnostics-show-path-depths): New option.
	* coretypes.h (diagnostic_event_id_t): New forward decl.
	* diagnostic-color.c (color_dict): Add "path".
	* diagnostic-event-id.h: New file.
	* diagnostic-format-json.cc (json_from_expanded_location): Make
	non-static.
	(json_end_diagnostic): Call context->make_json_for_path if it
	exists and the diagnostic has a path.
	(diagnostic_output_format_init): Clear context->print_path.
	* diagnostic-path.h: New file.
	* diagnostic-show-locus.c (colorizer::set_range): Special-case
	when printing a run of events in a diagnostic_path so that they
	all get the same color.
	(layout::m_diagnostic_path_p): New field.
	(layout::layout): Initialize it.
	(layout::print_any_labels): Don't colorize the label text for an
	event in a diagnostic_path.
	(gcc_rich_location::add_location_if_nearby): Add
	"restrict_to_current_line_spans" and "label" params.  Pass the
	former to layout.maybe_add_location_range; pass the latter
	when calling add_range.
	* diagnostic.c: Include "diagnostic-path.h".
	(diagnostic_initialize): Initialize context->path_format and
	context->show_path_depths.
	(diagnostic_show_any_path): New function.
	(diagnostic_path::interprocedural_p): New function.
	(diagnostic_report_diagnostic): Call diagnostic_show_any_path.
	(simple_diagnostic_path::num_events): New function.
	(simple_diagnostic_path::get_event): New function.
	(simple_diagnostic_path::add_event): New function.
	(simple_diagnostic_event::simple_diagnostic_event): New ctor.
	(simple_diagnostic_event::~simple_diagnostic_event): New dtor.
	(debug): New overload taking a diagnostic_path *.
	* diagnostic.def (DK_DIAGNOSTIC_PATH): New.
	* diagnostic.h (enum diagnostic_path_format): New enum.
	(json::value): New forward decl.
	(diagnostic_context::path_format): New field.
	(diagnostic_context::show_path_depths): New field.
	(diagnostic_context::print_path): New callback field.
	(diagnostic_context::make_json_for_path): New callback field.
	(diagnostic_show_any_path): New decl.
	(json_from_expanded_location): New decl.
	* doc/invoke.texi (-fdiagnostics-path-format=): New option.
	(-fdiagnostics-show-path-depths): New option.
	(-fdiagnostics-color): Add "path" to description of default
	GCC_COLORS; describe it.
	(-fdiagnostics-format=json): Document how diagnostic paths are
	represented in the JSON output format.
	* gcc-rich-location.h (gcc_rich_location::add_location_if_nearby):
	Add optional params "restrict_to_current_line_spans" and "label".
	* opts.c (common_handle_option): Handle
	OPT_fdiagnostics_path_format_ and
	OPT_fdiagnostics_show_path_depths.
	* pretty-print.c: Include "diagnostic-event-id.h".
	(pp_format): Implement "%@" format code for printing
	diagnostic_event_id_t *.
	(selftest::test_pp_format): Add tests for "%@".
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::tree_diagnostic_path_cc_tests.
	* selftest.h (selftest::tree_diagnostic_path_cc_tests): New decl.
	* toplev.c (general_init): Initialize global_dc->path_format and
	global_dc->show_path_depths.
	* tree-diagnostic-path.cc: New file.
	* tree-diagnostic.c (maybe_unwind_expanded_macro_loc): Make
	non-static.  Drop "diagnostic" param in favor of storing the
	original value of "where" and re-using it.
	(virt_loc_aware_diagnostic_finalizer): Update for dropped param of
	maybe_unwind_expanded_macro_loc.
	(tree_diagnostics_defaults): Initialize context->print_path and
	context->make_json_for_path.
	* tree-diagnostic.h (default_tree_diagnostic_path_printer): New
	decl.
	(default_tree_make_json_for_path): New decl.
	(maybe_unwind_expanded_macro_loc): New decl.

gcc/c-family/ChangeLog:
	* c-format.c (local_event_ptr_node): New.
	(PP_FORMAT_CHAR_TABLE): Add entry for "%@".
	(init_dynamic_diag_info): Initialize local_event_ptr_node.
	* c-format.h (T_EVENT_PTR): New define.

gcc/testsuite/ChangeLog:
	* gcc.dg/format/gcc_diag-10.c (diagnostic_event_id_t): New
	typedef.
	(test_diag): Add coverage of "%@".
	* gcc.dg/plugin/diagnostic-path-format-default.c: New test.
	* gcc.dg/plugin/diagnostic-path-format-inline-events-1.c: New test.
	* gcc.dg/plugin/diagnostic-path-format-inline-events-2.c: New test.
	* gcc.dg/plugin/diagnostic-path-format-inline-events-3.c: New test.
	* gcc.dg/plugin/diagnostic-path-format-none.c: New test.
	* gcc.dg/plugin/diagnostic-test-paths-1.c: New test.
	* gcc.dg/plugin/diagnostic-test-paths-2.c: New test.
	* gcc.dg/plugin/diagnostic-test-paths-3.c: New test.
	* gcc.dg/plugin/diagnostic-test-paths-4.c: New test.
	* gcc.dg/plugin/diagnostic_plugin_test_paths.c: New.
	* gcc.dg/plugin/plugin.exp: Add the new plugin and test cases.

libcpp/ChangeLog:
	* include/line-map.h (class diagnostic_path): New forward decl.
	(rich_location::get_path): New accessor.
	(rich_location::set_path): New function.
	(rich_location::m_path): New field.
	* line-map.c (rich_location::rich_location): Initialize m_path.

From-SVN: r280142
This commit is contained in:
David Malcolm 2020-01-10 21:22:12 +00:00 committed by David Malcolm
parent ea69031c5f
commit 4bc1899b2e
42 changed files with 2975 additions and 16 deletions

View file

@ -1,3 +1,83 @@
2020-01-10 David Malcolm <dmalcolm@redhat.com>
* Makefile.in (OBJS): Add tree-diagnostic-path.o.
* common.opt (fdiagnostics-path-format=): New option.
(diagnostic_path_format): New enum.
(fdiagnostics-show-path-depths): New option.
* coretypes.h (diagnostic_event_id_t): New forward decl.
* diagnostic-color.c (color_dict): Add "path".
* diagnostic-event-id.h: New file.
* diagnostic-format-json.cc (json_from_expanded_location): Make
non-static.
(json_end_diagnostic): Call context->make_json_for_path if it
exists and the diagnostic has a path.
(diagnostic_output_format_init): Clear context->print_path.
* diagnostic-path.h: New file.
* diagnostic-show-locus.c (colorizer::set_range): Special-case
when printing a run of events in a diagnostic_path so that they
all get the same color.
(layout::m_diagnostic_path_p): New field.
(layout::layout): Initialize it.
(layout::print_any_labels): Don't colorize the label text for an
event in a diagnostic_path.
(gcc_rich_location::add_location_if_nearby): Add
"restrict_to_current_line_spans" and "label" params. Pass the
former to layout.maybe_add_location_range; pass the latter
when calling add_range.
* diagnostic.c: Include "diagnostic-path.h".
(diagnostic_initialize): Initialize context->path_format and
context->show_path_depths.
(diagnostic_show_any_path): New function.
(diagnostic_path::interprocedural_p): New function.
(diagnostic_report_diagnostic): Call diagnostic_show_any_path.
(simple_diagnostic_path::num_events): New function.
(simple_diagnostic_path::get_event): New function.
(simple_diagnostic_path::add_event): New function.
(simple_diagnostic_event::simple_diagnostic_event): New ctor.
(simple_diagnostic_event::~simple_diagnostic_event): New dtor.
(debug): New overload taking a diagnostic_path *.
* diagnostic.def (DK_DIAGNOSTIC_PATH): New.
* diagnostic.h (enum diagnostic_path_format): New enum.
(json::value): New forward decl.
(diagnostic_context::path_format): New field.
(diagnostic_context::show_path_depths): New field.
(diagnostic_context::print_path): New callback field.
(diagnostic_context::make_json_for_path): New callback field.
(diagnostic_show_any_path): New decl.
(json_from_expanded_location): New decl.
* doc/invoke.texi (-fdiagnostics-path-format=): New option.
(-fdiagnostics-show-path-depths): New option.
(-fdiagnostics-color): Add "path" to description of default
GCC_COLORS; describe it.
(-fdiagnostics-format=json): Document how diagnostic paths are
represented in the JSON output format.
* gcc-rich-location.h (gcc_rich_location::add_location_if_nearby):
Add optional params "restrict_to_current_line_spans" and "label".
* opts.c (common_handle_option): Handle
OPT_fdiagnostics_path_format_ and
OPT_fdiagnostics_show_path_depths.
* pretty-print.c: Include "diagnostic-event-id.h".
(pp_format): Implement "%@" format code for printing
diagnostic_event_id_t *.
(selftest::test_pp_format): Add tests for "%@".
* selftest-run-tests.c (selftest::run_tests): Call
selftest::tree_diagnostic_path_cc_tests.
* selftest.h (selftest::tree_diagnostic_path_cc_tests): New decl.
* toplev.c (general_init): Initialize global_dc->path_format and
global_dc->show_path_depths.
* tree-diagnostic-path.cc: New file.
* tree-diagnostic.c (maybe_unwind_expanded_macro_loc): Make
non-static. Drop "diagnostic" param in favor of storing the
original value of "where" and re-using it.
(virt_loc_aware_diagnostic_finalizer): Update for dropped param of
maybe_unwind_expanded_macro_loc.
(tree_diagnostics_defaults): Initialize context->print_path and
context->make_json_for_path.
* tree-diagnostic.h (default_tree_diagnostic_path_printer): New
decl.
(default_tree_make_json_for_path): New decl.
(maybe_unwind_expanded_macro_loc): New decl.
2020-01-10 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/93210

View file

@ -1521,6 +1521,7 @@ OBJS = \
tree-data-ref.o \
tree-dfa.o \
tree-diagnostic.o \
tree-diagnostic-path.o \
tree-dump.o \
tree-eh.o \
tree-emutls.o \

View file

@ -1,3 +1,10 @@
2020-01-10 David Malcolm <dmalcolm@redhat.com>
* c-format.c (local_event_ptr_node): New.
(PP_FORMAT_CHAR_TABLE): Add entry for "%@".
(init_dynamic_diag_info): Initialize local_event_ptr_node.
* c-format.h (T_EVENT_PTR): New define.
2020-01-10 Martin Sebor <msebor@redhat.com>
PR c/93132

View file

@ -65,6 +65,7 @@ struct function_format_info
/* Initialized in init_dynamic_diag_info. */
static GTY(()) tree local_tree_type_node;
static GTY(()) tree local_event_ptr_node;
static GTY(()) tree local_gimple_ptr_node;
static GTY(()) tree local_cgraph_node_ptr_node;
static GTY(()) tree locus;
@ -752,6 +753,7 @@ static const format_char_info asm_fprintf_char_table[] =
{ "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "pq", "cR", NULL }, \
{ "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "c", NULL }, \
{ "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "//cR", NULL }, \
{ "@", 1, STD_C89, { T_EVENT_PTR, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "\"", NULL }, \
{ "<", 0, STD_C89, NOARGUMENTS, "", "<", NULL }, \
{ ">", 0, STD_C89, NOARGUMENTS, "", ">", NULL }, \
{ "'" , 0, STD_C89, NOARGUMENTS, "", "", NULL }, \
@ -4988,6 +4990,11 @@ init_dynamic_diag_info (void)
|| local_cgraph_node_ptr_node == void_type_node)
local_cgraph_node_ptr_node = get_named_type ("cgraph_node");
/* Similar to the above but for diagnostic_event_id_t*. */
if (!local_event_ptr_node
|| local_event_ptr_node == void_type_node)
local_event_ptr_node = get_named_type ("diagnostic_event_id_t");
static tree hwi;
if (!hwi)

View file

@ -303,6 +303,7 @@ struct format_kind_info
#define T_V &void_type_node
#define T89_G { STD_C89, NULL, &local_gimple_ptr_node }
#define T_CGRAPH_NODE { STD_C89, NULL, &local_cgraph_node_ptr_node }
#define T_EVENT_PTR { STD_C89, NULL, &local_event_ptr_node }
#define T89_T { STD_C89, NULL, &local_tree_type_node }
#define T89_V { STD_C89, NULL, T_V }
#define T_W &wchar_type_node

View file

@ -1338,6 +1338,26 @@ fdiagnostics-show-cwe
Common Var(flag_diagnostics_show_cwe) Init(1)
Print CWE identifiers for diagnostic messages, where available.
fdiagnostics-path-format=
Common Joined RejectNegative Var(flag_diagnostics_path_format) Enum(diagnostic_path_format) Init(DPF_INLINE_EVENTS)
Specify how to print any control-flow path associated with a diagnostic.
Enum
Name(diagnostic_path_format) Type(int)
EnumValue
Enum(diagnostic_path_format) String(none) Value(DPF_NONE)
EnumValue
Enum(diagnostic_path_format) String(separate-events) Value(DPF_SEPARATE_EVENTS)
EnumValue
Enum(diagnostic_path_format) String(inline-events) Value(DPF_INLINE_EVENTS)
fdiagnostics-show-path-depths
Common Var(flag_diagnostics_show_path_depths) Init(0)
Show stack depths of events in paths.
fdiagnostics-minimum-margin-width=
Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6)
Set minimum width of left margin of source code when showing source.

View file

@ -153,6 +153,7 @@ struct cl_decoded_option;
struct cl_option_handlers;
struct diagnostic_context;
class pretty_printer;
class diagnostic_event_id_t;
template<typename T> struct array_traits;

View file

@ -90,6 +90,7 @@ static struct color_cap color_dict[] =
{ "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
{ "locus", SGR_SEQ (COLOR_BOLD), 5, false },
{ "quote", SGR_SEQ (COLOR_BOLD), 5, false },
{ "path", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
{ "fixit-insert", SGR_SEQ (COLOR_FG_GREEN), 12, false },
{ "fixit-delete", SGR_SEQ (COLOR_FG_RED), 12, false },
{ "diff-filename", SGR_SEQ (COLOR_BOLD), 13, false },
@ -126,7 +127,7 @@ colorize_stop (bool show_color)
/* Parse GCC_COLORS. The default would look like:
GCC_COLORS='error=01;31:warning=01;35:note=01;36:\
range1=32:range2=34:locus=01:quote=01:\
range1=32:range2=34:locus=01:quote=01:path=01;36:\
fixit-insert=32:fixit-delete=31:'\
diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
type-diff=01;32'

61
gcc/diagnostic-event-id.h Normal file
View file

@ -0,0 +1,61 @@
/* A class for referring to events within a diagnostic_path.
Copyright (C) 2019-2020 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_EVENT_ID_H
#define GCC_DIAGNOSTIC_EVENT_ID_H
/* A class for referring to events within a diagnostic_path.
They are stored as 0-based offsets into the events, but
printed (e.g. via %@) as 1-based numbers.
For example, a 3-event path has event offsets 0, 1, and 2,
which would be shown to the user as "(1)", "(2)" and "(3)".
This has its own header so that pretty-print.c can use this
to implement "%@" without bringing in all of diagnostic_path
(which e.g. refers to "tree"). */
class diagnostic_event_id_t
{
public:
diagnostic_event_id_t () : m_index (UNKNOWN_EVENT_IDX) {}
diagnostic_event_id_t (int zero_based_idx) : m_index (zero_based_idx) {}
bool known_p () const { return m_index != UNKNOWN_EVENT_IDX; }
int one_based () const
{
gcc_assert (known_p ());
return m_index + 1;
}
private:
static const int UNKNOWN_EVENT_IDX = -1;
int m_index; // zero-based
};
/* A pointer to a diagnostic_event_id_t, for use with the "%@" format
code, which will print a 1-based representation for it, with suitable
colorization, e.g. "(1)".
The %@ format code requires that known_p be true for the event ID. */
typedef diagnostic_event_id_t *diagnostic_event_id_ptr;
#endif /* ! GCC_DIAGNOSTIC_EVENT_ID_H */

View file

@ -42,7 +42,7 @@ static json::array *cur_children_array;
/* Generate a JSON object for LOC. */
static json::object *
json::value *
json_from_expanded_location (location_t loc)
{
expanded_location exploc = expand_location (loc);
@ -232,6 +232,13 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
json::object *metadata_obj = json_from_metadata (diagnostic->metadata);
diag_obj->set ("metadata", metadata_obj);
}
const diagnostic_path *path = richloc->get_path ();
if (path && context->make_json_for_path)
{
json::value *path_value = context->make_json_for_path (context, path);
diag_obj->set ("path", path_value);
}
}
/* No-op implementation of "begin_group_cb" for JSON output. */
@ -288,6 +295,7 @@ diagnostic_output_format_init (diagnostic_context *context,
context->begin_group_cb = json_begin_group;
context->end_group_cb = json_end_group;
context->final_cb = json_final_cb;
context->print_path = NULL; /* handled in json_end_diagnostic. */
/* The metadata is handled in JSON format, rather than as text. */
context->show_cwe = false;

149
gcc/diagnostic-path.h Normal file
View file

@ -0,0 +1,149 @@
/* Paths through the code associated with a diagnostic.
Copyright (C) 2019-2020 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_PATH_H
#define GCC_DIAGNOSTIC_PATH_H
#include "diagnostic.h" /* for ATTRIBUTE_GCC_DIAG. */
#include "diagnostic-event-id.h"
/* A diagnostic_path is an optional additional piece of metadata associated
with a diagnostic (via its rich_location).
It describes a sequence of events predicted by the compiler that
lead to the problem occurring, with their locations in the user's source,
and text descriptions.
For example, the following error has a 3-event path:
test.c: In function 'demo':
test.c:29:5: error: passing NULL as argument 1 to 'PyList_Append' which
requires a non-NULL parameter
29 | PyList_Append(list, item);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
'demo': events 1-3
|
| 25 | list = PyList_New(0);
| | ^~~~~~~~~~~~~
| | |
| | (1) when 'PyList_New' fails, returning NULL
| 26 |
| 27 | for (i = 0; i < count; i++) {
| | ~~~
| | |
| | (2) when 'i < count'
| 28 | item = PyLong_FromLong(random());
| 29 | PyList_Append(list, item);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (3) when calling 'PyList_Append', passing NULL from (1) as argument 1
|
The diagnostic-printing code has consolidated the path into a single
run of events, since all the events are near each other and within the same
function; more complicated examples (such as interprocedural paths)
might be printed as multiple runs of events. */
/* Abstract base classes, describing events within a path, and the paths
themselves. */
/* One event within a diagnostic_path. */
class diagnostic_event
{
public:
virtual ~diagnostic_event () {}
virtual location_t get_location () const = 0;
virtual tree get_fndecl () const = 0;
/* Stack depth, so that consumers can visualizes the interprocedural
calls, returns, and frame nesting. */
virtual int get_stack_depth () const = 0;
/* Get a localized (and possibly colorized) description of this event. */
virtual label_text get_desc (bool can_colorize) const = 0;
};
/* Abstract base class for getting at a sequence of events. */
class diagnostic_path
{
public:
virtual ~diagnostic_path () {}
virtual unsigned num_events () const = 0;
virtual const diagnostic_event & get_event (int idx) const = 0;
bool interprocedural_p () const;
};
/* Concrete subclasses. */
/* A simple implementation of diagnostic_event. */
class simple_diagnostic_event : public diagnostic_event
{
public:
simple_diagnostic_event (location_t loc, tree fndecl, int depth,
const char *desc);
~simple_diagnostic_event ();
location_t get_location () const FINAL OVERRIDE { return m_loc; }
tree get_fndecl () const FINAL OVERRIDE { return m_fndecl; }
int get_stack_depth () const FINAL OVERRIDE { return m_depth; }
label_text get_desc (bool) const FINAL OVERRIDE
{
return label_text::borrow (m_desc);
}
private:
location_t m_loc;
tree m_fndecl;
int m_depth;
char *m_desc; // has been i18n-ed and formatted
};
/* A simple implementation of diagnostic_path, as a vector of
simple_diagnostic_event instances. */
class simple_diagnostic_path : public diagnostic_path
{
public:
simple_diagnostic_path (pretty_printer *event_pp)
: m_event_pp (event_pp) {}
unsigned num_events () const FINAL OVERRIDE;
const diagnostic_event & get_event (int idx) const FINAL OVERRIDE;
diagnostic_event_id_t add_event (location_t loc, tree fndecl, int depth,
const char *fmt, ...)
ATTRIBUTE_GCC_DIAG(5,6);
private:
auto_delete_vec<simple_diagnostic_event> m_events;
/* (for use by add_event). */
pretty_printer *m_event_pp;
};
extern void debug (diagnostic_path *path);
#endif /* ! GCC_DIAGNOSTIC_PATH_H */

View file

@ -87,7 +87,17 @@ class colorizer
diagnostic_t diagnostic_kind);
~colorizer ();
void set_range (int range_idx) { set_state (range_idx); }
void set_range (int range_idx)
{
/* Normally we emphasize the primary location, then alternate between
two colors for the secondary locations.
But if we're printing a run of events in a diagnostic path, that
makes no sense, so print all of them with the same colorization. */
if (m_diagnostic_kind == DK_DIAGNOSTIC_PATH)
set_state (0);
else
set_state (range_idx);
}
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); }
@ -385,6 +395,7 @@ class layout
bool m_colorize_source_p;
bool m_show_labels_p;
bool m_show_line_numbers_p;
bool m_diagnostic_path_p;
auto_vec <layout_range> m_layout_ranges;
auto_vec <const fixit_hint *> m_fixit_hints;
auto_vec <line_span> m_line_spans;
@ -958,6 +969,7 @@ layout::layout (diagnostic_context * context,
m_colorize_source_p (context->colorize_source_p),
m_show_labels_p (context->show_labels_p),
m_show_line_numbers_p (context->show_line_numbers_p),
m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
m_layout_ranges (richloc->get_num_locations ()),
m_fixit_hints (richloc->get_num_fixit_hints ()),
m_line_spans (1 + richloc->get_num_locations ()),
@ -1770,7 +1782,10 @@ layout::print_any_labels (linenum_type row)
{
gcc_assert (column <= label->m_column);
move_to_column (&column, label->m_column, true);
m_colorizer.set_range (label->m_state_idx);
/* Colorize the text, unless it's for events in a
diagnostic_path. */
if (!m_diagnostic_path_p)
m_colorizer.set_range (label->m_state_idx);
pp_string (m_pp, label->m_text.m_buffer);
m_colorizer.set_normal_text ();
column += label->m_display_width;
@ -2506,7 +2521,9 @@ layout::print_line (linenum_type row)
Otherwise return false. */
bool
gcc_rich_location::add_location_if_nearby (location_t loc)
gcc_rich_location::add_location_if_nearby (location_t loc,
bool restrict_to_current_line_spans,
const range_label *label)
{
/* Use the layout location-handling logic to sanitize LOC,
filtering it to the current line spans within a temporary
@ -2515,10 +2532,11 @@ 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;
if (!layout.maybe_add_location_range (&loc_range, 0, true))
if (!layout.maybe_add_location_range (&loc_range, 0,
restrict_to_current_line_spans))
return false;
add_range (loc);
add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
return true;
}

View file

@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic-color.h"
#include "diagnostic-url.h"
#include "diagnostic-metadata.h"
#include "diagnostic-path.h"
#include "edit-context.h"
#include "selftest.h"
#include "selftest-diagnostic.h"
@ -187,6 +188,8 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES; i++)
context->caret_chars[i] = '^';
context->show_cwe = false;
context->path_format = DPF_NONE;
context->show_path_depths = false;
context->show_option_requested = false;
context->abort_on_error = false;
context->show_column = false;
@ -658,6 +661,38 @@ diagnostic_report_current_module (diagnostic_context *context, location_t where)
}
}
/* If DIAGNOSTIC has a diagnostic_path and CONTEXT supports printing paths,
print the path. */
void
diagnostic_show_any_path (diagnostic_context *context,
diagnostic_info *diagnostic)
{
const diagnostic_path *path = diagnostic->richloc->get_path ();
if (!path)
return;
if (context->print_path)
context->print_path (context, path);
}
/* Return true if the events in this path involve more than one
function, or false if it is purely intraprocedural. */
bool
diagnostic_path::interprocedural_p () const
{
const unsigned num = num_events ();
for (unsigned i = 0; i < num; i++)
{
if (get_event (i).get_fndecl () != get_event (0).get_fndecl ())
return true;
if (get_event (i).get_stack_depth () != get_event (0).get_stack_depth ())
return true;
}
return false;
}
void
default_diagnostic_starter (diagnostic_context *context,
diagnostic_info *diagnostic)
@ -1123,6 +1158,8 @@ diagnostic_report_diagnostic (diagnostic_context *context,
context->lock--;
diagnostic_show_any_path (context, diagnostic);
return true;
}
@ -1758,6 +1795,95 @@ auto_diagnostic_group::~auto_diagnostic_group ()
}
}
/* Implementation of diagnostic_path::num_events vfunc for
simple_diagnostic_path: simply get the number of events in the vec. */
unsigned
simple_diagnostic_path::num_events () const
{
return m_events.length ();
}
/* Implementation of diagnostic_path::get_event vfunc for
simple_diagnostic_path: simply return the event in the vec. */
const diagnostic_event &
simple_diagnostic_path::get_event (int idx) const
{
return *m_events[idx];
}
/* Add an event to this path at LOC within function FNDECL at
stack depth DEPTH.
Use m_context's printer to format FMT, as the text of the new
event.
Return the id of the new event. */
diagnostic_event_id_t
simple_diagnostic_path::add_event (location_t loc, tree fndecl, int depth,
const char *fmt, ...)
{
pretty_printer *pp = m_event_pp;
pp_clear_output_area (pp);
text_info ti;
rich_location rich_loc (line_table, UNKNOWN_LOCATION);
va_list ap;
va_start (ap, fmt);
ti.format_spec = _(fmt);
ti.args_ptr = &ap;
ti.err_no = 0;
ti.x_data = NULL;
ti.m_richloc = &rich_loc;
pp_format (pp, &ti);
pp_output_formatted_text (pp);
va_end (ap);
simple_diagnostic_event *new_event
= new simple_diagnostic_event (loc, fndecl, depth, pp_formatted_text (pp));
m_events.safe_push (new_event);
pp_clear_output_area (pp);
return diagnostic_event_id_t (m_events.length () - 1);
}
/* struct simple_diagnostic_event. */
/* simple_diagnostic_event's ctor. */
simple_diagnostic_event::simple_diagnostic_event (location_t loc,
tree fndecl,
int depth,
const char *desc)
: m_loc (loc), m_fndecl (fndecl), m_depth (depth), m_desc (xstrdup (desc))
{
}
/* simple_diagnostic_event's dtor. */
simple_diagnostic_event::~simple_diagnostic_event ()
{
free (m_desc);
}
/* Print PATH by emitting a dummy "note" associated with it. */
DEBUG_FUNCTION
void debug (diagnostic_path *path)
{
rich_location richloc (line_table, UNKNOWN_LOCATION);
richloc.set_path (path);
inform (&richloc, "debug path");
}
/* Really call the system 'abort'. This has to go right at the end of
this file, so that there are no functions after it that call abort
and get the system abort instead of our macro. */

View file

@ -38,6 +38,11 @@ DEFINE_DIAGNOSTIC_KIND (DK_WARNING, "warning: ", "warning")
DEFINE_DIAGNOSTIC_KIND (DK_ANACHRONISM, "anachronism: ", "warning")
DEFINE_DIAGNOSTIC_KIND (DK_NOTE, "note: ", "note")
DEFINE_DIAGNOSTIC_KIND (DK_DEBUG, "debug: ", "note")
/* For use when using the diagnostic_show_locus machinery to show
a range of events within a path. */
DEFINE_DIAGNOSTIC_KIND (DK_DIAGNOSTIC_PATH, "path: ", "path")
/* These two would be re-classified as DK_WARNING or DK_ERROR, so the
prefix does not matter. */
DEFINE_DIAGNOSTIC_KIND (DK_PEDWARN, "pedwarn: ", NULL)

View file

@ -35,6 +35,23 @@ enum diagnostics_output_format
DIAGNOSTICS_OUTPUT_FORMAT_JSON
};
/* An enum for controlling how diagnostic_paths should be printed. */
enum diagnostic_path_format
{
/* Don't print diagnostic_paths. */
DPF_NONE,
/* Print diagnostic_paths by emitting a separate "note" for every event
in the path. */
DPF_SEPARATE_EVENTS,
/* Print diagnostic_paths by consolidating events together where they
are close enough, and printing such runs of events with multiple
calls to diagnostic_show_locus, showing the individual events in
each run via labels in the source. */
DPF_INLINE_EVENTS
};
/* A diagnostic is described by the MESSAGE to send, the FILE and LINE of
its context and its KIND (ice, error, warning, note, ...) See complete
list in diagnostic.def. */
@ -80,6 +97,7 @@ typedef void (*diagnostic_finalizer_fn) (diagnostic_context *,
diagnostic_t);
class edit_context;
namespace json { class value; }
/* This data structure bundles altogether any information relevant to
the context of a diagnostic message. */
@ -134,6 +152,12 @@ struct diagnostic_context
diagnostics. */
bool show_cwe;
/* How should diagnostic_path objects be printed. */
enum diagnostic_path_format path_format;
/* True if we should print stack depths when printing diagnostic paths. */
bool show_path_depths;
/* True if we should print the command line option which controls
each diagnostic, if known. */
bool show_option_requested;
@ -208,6 +232,9 @@ struct diagnostic_context
particular option. */
char *(*get_option_url) (diagnostic_context *, int);
void (*print_path) (diagnostic_context *, const diagnostic_path *);
json::value *(*make_json_for_path) (diagnostic_context *, const diagnostic_path *);
/* Auxiliary data for client. */
void *x_data;
@ -351,6 +378,7 @@ extern void diagnostic_report_current_module (diagnostic_context *, location_t);
extern void diagnostic_show_locus (diagnostic_context *,
rich_location *richloc,
diagnostic_t diagnostic_kind);
extern void diagnostic_show_any_path (diagnostic_context *, diagnostic_info *);
/* Force diagnostics controlled by OPTIDX to be kind KIND. */
extern diagnostic_t diagnostic_classify_diagnostic (diagnostic_context *,
@ -442,4 +470,6 @@ extern void diagnostic_output_format_init (diagnostic_context *,
/* Compute the number of digits in the decimal representation of an integer. */
extern int num_digits (int);
extern json::value *json_from_expanded_location (location_t loc);
#endif /* ! GCC_DIAGNOSTIC_H */

View file

@ -281,6 +281,8 @@ Objective-C and Objective-C++ Dialects}.
-fdiagnostics-minimum-margin-width=@var{width} @gol
-fdiagnostics-parseable-fixits -fdiagnostics-generate-patch @gol
-fdiagnostics-show-template-tree -fno-elide-type @gol
-fdiagnostics-path-format=@r{[}none@r{|}separate-events@r{|}inline-events@r{]} @gol
-fdiagnostics-show-path-depths @gol
-fno-show-column}
@item Warning Options
@ -3886,7 +3888,7 @@ for 88-color and 256-color modes background colors.
The default @env{GCC_COLORS} is
@smallexample
error=01;31:warning=01;35:note=01;36:range1=32:range2=34:locus=01:\
quote=01:fixit-insert=32:fixit-delete=31:\
quote=01:path=01;36:fixit-insert=32:fixit-delete=31:\
diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
type-diff=01;32
@end smallexample
@ -3910,6 +3912,12 @@ SGR substring for warning: markers.
@vindex note GCC_COLORS @r{capability}
SGR substring for note: markers.
@item path=
@vindex path GCC_COLORS @r{capability}
SGR substring for colorizing paths of control-flow events as printed
via @option{-fdiagnostics-path-format=}, such as the identifiers of
individual events and lines indicating interprocedural calls and returns.
@item range1=
@vindex range1 GCC_COLORS @r{capability}
SGR substring for first additional range.
@ -4120,6 +4128,114 @@ Specifying the @option{-fno-elide-type} flag suppresses that behavior.
This flag also affects the output of the
@option{-fdiagnostics-show-template-tree} flag.
@item -fdiagnostics-path-format=@var{KIND}
@opindex fdiagnostics-path-format
Specify how to print paths of control-flow events for diagnostics that
have such a path associated with them.
@var{KIND} is @samp{none}, @samp{separate-events}, or @samp{inline-events},
the default.
@samp{none} means to not print diagnostic paths.
@samp{separate-events} means to print a separate ``note'' diagnostic for
each event within the diagnostic. For example:
@smallexample
test.c:29:5: error: passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter
test.c:25:10: note: (1) when 'PyList_New' fails, returning NULL
test.c:27:3: note: (2) when 'i < count'
test.c:29:5: note: (3) when calling 'PyList_Append', passing NULL from (1) as argument 1
@end smallexample
@samp{inline-events} means to print the events ``inline'' within the source
code. This view attempts to consolidate the events into runs of
sufficiently-close events, printing them as labelled ranges within the source.
For example, the same events as above might be printed as:
@smallexample
'test': events 1-3
|
| 25 | list = PyList_New(0);
| | ^~~~~~~~~~~~~
| | |
| | (1) when 'PyList_New' fails, returning NULL
| 26 |
| 27 | for (i = 0; i < count; i++) @{
| | ~~~
| | |
| | (2) when 'i < count'
| 28 | item = PyLong_FromLong(random());
| 29 | PyList_Append(list, item);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (3) when calling 'PyList_Append', passing NULL from (1) as argument 1
|
@end smallexample
Interprocedural control flow is shown by grouping the events by stack frame,
and using indentation to show how stack frames are nested, pushed, and popped.
For example:
@smallexample
'test': events 1-2
|
| 133 | @{
| | ^
| | |
| | (1) entering 'test'
| 134 | boxed_int *obj = make_boxed_int (i);
| | ~~~~~~~~~~~~~~~~~~
| | |
| | (2) calling 'make_boxed_int'
|
+--> 'make_boxed_int': events 3-4
|
| 120 | @{
| | ^
| | |
| | (3) entering 'make_boxed_int'
| 121 | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (4) calling 'wrapped_malloc'
|
+--> 'wrapped_malloc': events 5-6
|
| 7 | @{
| | ^
| | |
| | (5) entering 'wrapped_malloc'
| 8 | return malloc (size);
| | ~~~~~~~~~~~~~
| | |
| | (6) calling 'malloc'
|
<-------------+
|
'test': event 7
|
| 138 | free_boxed_int (obj);
| | ^~~~~~~~~~~~~~~~~~~~
| | |
| | (7) calling 'free_boxed_int'
|
(etc)
@end smallexample
@item -fdiagnostics-show-path-depths
@opindex fdiagnostics-show-path-depths
This option provides additional information when printing control-flow paths
associated with a diagnostic.
If this is option is provided then the stack depth will be printed for
each run of events within @option{-fdiagnostics-path-format=separate-events}.
This is intended for use by GCC developers and plugin developers when
debugging diagnostics that report interprocedural control flow.
@item -fno-show-column
@opindex fno-show-column
@opindex fshow-column
@ -4315,6 +4431,53 @@ to but not including @code{next} with @code{string}'s value. Deletions
are expressed via an empty value for @code{string}, insertions by
having @code{start} equal @code{next}.
If the diagnostic has a path of control-flow events associated with it,
it has a @code{path} array of objects representing the events. Each
event object has a @code{description} string, a @code{location} object,
along with a @code{function} string and a @code{depth} number for
representing interprocedural paths. The @code{function} represents the
current function at that event, and the @code{depth} represents the
stack depth relative to some baseline: the higher, the more frames are
within the stack.
For example, the intraprocedural example shown for
@option{-fdiagnostics-path-format=} might have this JSON for its path:
@smallexample
"path": [
@{
"depth": 0,
"description": "when 'PyList_New' fails, returning NULL",
"function": "test",
"location": @{
"column": 10,
"file": "test.c",
"line": 25
@}
@},
@{
"depth": 0,
"description": "when 'i < count'",
"function": "test",
"location": @{
"column": 3,
"file": "test.c",
"line": 27
@}
@},
@{
"depth": 0,
"description": "when calling 'PyList_Append', passing NULL from (1) as argument 1",
"function": "test",
"location": @{
"column": 5,
"file": "test.c",
"line": 29
@}
@}
]
@end smallexample
@end table
@node Warning Options

View file

@ -62,7 +62,9 @@ class gcc_rich_location : public rich_location
Implemented in diagnostic-show-locus.c. */
bool add_location_if_nearby (location_t loc);
bool add_location_if_nearby (location_t loc,
bool restrict_to_current_line_spans = true,
const range_label *label = NULL);
/* Add a fix-it hint suggesting the insertion of CONTENT before
INSERTION_POINT.

View file

@ -2411,6 +2411,14 @@ common_handle_option (struct gcc_options *opts,
dc->show_cwe = value;
break;
case OPT_fdiagnostics_path_format_:
dc->path_format = (enum diagnostic_path_format)value;
break;
case OPT_fdiagnostics_show_path_depths:
dc->show_path_depths = value;
break;
case OPT_fdiagnostics_show_option:
dc->show_option_requested = value;
break;

View file

@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see
#include "intl.h"
#include "pretty-print.h"
#include "diagnostic-color.h"
#include "diagnostic-event-id.h"
#include "selftest.h"
#if HAVE_ICONV
@ -1039,6 +1040,7 @@ pp_indent (pretty_printer *pp)
%>: closing quote.
%': apostrophe (should only be used in untranslated messages;
translations should use appropriate punctuation directly).
%@: diagnostic_event_id_ptr, for which event_id->known_p () must be true.
%.*s: a substring the length of which is specified by an argument
integer.
%Ns: likewise, but length specified as constant in the format string.
@ -1428,6 +1430,21 @@ pp_format (pretty_printer *pp, text_info *text)
}
break;
case '@':
{
/* diagnostic_event_id_t *. */
diagnostic_event_id_ptr event_id
= va_arg (*text->args_ptr, diagnostic_event_id_ptr);
gcc_assert (event_id->known_p ());
pp_string (pp, colorize_start (pp_show_color (pp), "path"));
pp_character (pp, '(');
pp_decimal_int (pp, event_id->one_based ());
pp_character (pp, ')');
pp_string (pp, colorize_stop (pp_show_color (pp)));
}
break;
default:
{
bool ok;
@ -2338,6 +2355,21 @@ test_pp_format ()
assert_pp_format_colored (SELFTEST_LOCATION,
"`\33[01m\33[Kfoo\33[m\33[K' 12345678", "%qs %x",
"foo", 0x12345678);
/* Verify "%@". */
{
diagnostic_event_id_t first (2);
diagnostic_event_id_t second (7);
ASSERT_PP_FORMAT_2 ("first `free' at (3); second `free' at (8)",
"first %<free%> at %@; second %<free%> at %@",
&first, &second);
assert_pp_format_colored
(SELFTEST_LOCATION,
"first `free' at (3);"
" second `free' at (8)",
"first %<free%> at %@; second %<free%> at %@",
&first, &second);
}
/* Verify %Z. */
int v[] = { 1, 2, 3 };

View file

@ -96,6 +96,7 @@ selftest::run_tests ()
spellcheck_c_tests ();
spellcheck_tree_c_tests ();
tree_cfg_c_tests ();
tree_diagnostic_path_cc_tests ();
attribute_c_tests ();
/* This one relies on most of the above. */

View file

@ -256,6 +256,7 @@ extern void sreal_c_tests ();
extern void store_merging_c_tests ();
extern void tree_c_tests ();
extern void tree_cfg_c_tests ();
extern void tree_diagnostic_path_cc_tests ();
extern void typed_splay_tree_c_tests ();
extern void unique_ptr_tests_cc_tests ();
extern void vec_c_tests ();

View file

@ -19,6 +19,23 @@
PR inline-asm/93027
* gcc.target/i386/pr93207.c: Run it only for x86-64.
2020-01-10 David Malcolm <dmalcolm@redhat.com>
* gcc.dg/format/gcc_diag-10.c (diagnostic_event_id_t): New
typedef.
(test_diag): Add coverage of "%@".
* gcc.dg/plugin/diagnostic-path-format-default.c: New test.
* gcc.dg/plugin/diagnostic-path-format-inline-events-1.c: New test.
* gcc.dg/plugin/diagnostic-path-format-inline-events-2.c: New test.
* gcc.dg/plugin/diagnostic-path-format-inline-events-3.c: New test.
* gcc.dg/plugin/diagnostic-path-format-none.c: New test.
* gcc.dg/plugin/diagnostic-test-paths-1.c: New test.
* gcc.dg/plugin/diagnostic-test-paths-2.c: New test.
* gcc.dg/plugin/diagnostic-test-paths-3.c: New test.
* gcc.dg/plugin/diagnostic-test-paths-4.c: New test.
* gcc.dg/plugin/diagnostic_plugin_test_paths.c: New.
* gcc.dg/plugin/plugin.exp: Add the new plugin and test cases.
2020-01-10 David Malcolm <dmalcolm@redhat.com>
* lib/gcc-dg.exp (cleanup-after-saved-dg-test): Reset global

View file

@ -22,6 +22,9 @@ typedef struct gimple gimple;
/* Likewise for gimple. */
typedef struct cgraph_node cgraph_node;
/* Likewise for diagnostic_event_id_t. */
typedef struct diagnostic_event_id_t diagnostic_event_id_t;
#define FORMAT(kind) __attribute__ ((format (__gcc_## kind ##__, 1, 2)))
void diag (const char*, ...) FORMAT (diag);
@ -30,7 +33,7 @@ void tdiag (const char*, ...) FORMAT (tdiag);
void cxxdiag (const char*, ...) FORMAT (cxxdiag);
void dump (const char*, ...) FORMAT (dump_printf);
void test_diag (tree t, gimple *gc)
void test_diag (tree t, gimple *gc, diagnostic_event_id_t *event_id_ptr)
{
diag ("%<"); /* { dg-warning "unterminated quoting directive" } */
diag ("%>"); /* { dg-warning "unmatched quoting directive " } */
@ -38,6 +41,7 @@ void test_diag (tree t, gimple *gc)
diag ("%G", gc); /* { dg-warning "format" } */
diag ("%K", t); /* { dg-warning "format" } */
diag ("%@", event_id_ptr);
diag ("%R"); /* { dg-warning "unmatched color reset directive" } */
diag ("%r", ""); /* { dg-warning "unterminated color directive" } */

View file

@ -0,0 +1,142 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-show-caret" } */
#include <stdlib.h>
void *wrapped_malloc (size_t size)
{
return malloc (size);
}
void wrapped_free (void *ptr)
{
free (ptr); /* { dg-warning "double-free of 'ptr' \\\[CWE-415\\]" } */
/* { dg-begin-multiline-output "" }
free (ptr);
^~~~~~~~~~
'test': events 1-2
|
| {
| ^
| |
| (1) entering 'test'
| boxed_int *obj = make_boxed_int (i);
| ~~~~~~~~~~~~~~~~~~
| |
| (2) calling 'make_boxed_int'
|
+--> 'make_boxed_int': events 3-4
|
| {
| ^
| |
| (3) entering 'make_boxed_int'
| boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| (4) calling 'wrapped_malloc'
|
+--> 'wrapped_malloc': events 5-6
|
| {
| ^
| |
| (5) entering 'wrapped_malloc'
| return malloc (size);
| ~~~~~~~~~~~~~
| |
| (6) calling 'malloc'
|
<-------------+
|
'test': event 7
|
| free_boxed_int (obj);
| ^~~~~~~~~~~~~~~~~~~~
| |
| (7) calling 'free_boxed_int'
|
+--> 'free_boxed_int': events 8-9
|
| {
| ^
| |
| (8) entering 'free_boxed_int'
| wrapped_free (bi);
| ~~~~~~~~~~~~~~~~~
| |
| (9) calling 'wrapped_free'
|
+--> 'wrapped_free': events 10-11
|
| {
| ^
| |
| (10) entering 'wrapped_free'
| free (ptr);
| ~~~~~~~~~~
| |
| (11) calling 'free'
|
<-------------+
|
'test': event 12
|
| free_boxed_int (obj);
| ^~~~~~~~~~~~~~~~~~~~
| |
| (12) calling 'free_boxed_int'
|
+--> 'free_boxed_int': events 13-14
|
| {
| ^
| |
| (13) entering 'free_boxed_int'
| wrapped_free (bi);
| ~~~~~~~~~~~~~~~~~
| |
| (14) calling 'wrapped_free'
|
+--> 'wrapped_free': events 15-16
|
| {
| ^
| |
| (15) entering 'wrapped_free'
| free (ptr);
| ~~~~~~~~~~
| |
| (16) calling 'free'
|
{ dg-end-multiline-output "" } */
}
typedef struct boxed_int
{
int i;
} boxed_int;
boxed_int *
make_boxed_int (int i)
{
boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
result->i = i;
return result;
}
void
free_boxed_int (boxed_int *bi)
{
wrapped_free (bi);
}
void test (int i)
{
boxed_int *obj = make_boxed_int (i);
free_boxed_int (obj);
free_boxed_int (obj);
}

View file

@ -0,0 +1,142 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
#include <stdlib.h>
void *wrapped_malloc (size_t size)
{
return malloc (size);
}
void wrapped_free (void *ptr)
{
free (ptr); /* { dg-warning "double-free of 'ptr' \\\[CWE-415\\]" } */
/* { dg-begin-multiline-output "" }
free (ptr);
^~~~~~~~~~
'test': events 1-2
|
| {
| ^
| |
| (1) entering 'test'
| boxed_int *obj = make_boxed_int (i);
| ~~~~~~~~~~~~~~~~~~
| |
| (2) calling 'make_boxed_int'
|
+--> 'make_boxed_int': events 3-4
|
| {
| ^
| |
| (3) entering 'make_boxed_int'
| boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| (4) calling 'wrapped_malloc'
|
+--> 'wrapped_malloc': events 5-6
|
| {
| ^
| |
| (5) entering 'wrapped_malloc'
| return malloc (size);
| ~~~~~~~~~~~~~
| |
| (6) calling 'malloc'
|
<-------------+
|
'test': event 7
|
| free_boxed_int (obj);
| ^~~~~~~~~~~~~~~~~~~~
| |
| (7) calling 'free_boxed_int'
|
+--> 'free_boxed_int': events 8-9
|
| {
| ^
| |
| (8) entering 'free_boxed_int'
| wrapped_free (bi);
| ~~~~~~~~~~~~~~~~~
| |
| (9) calling 'wrapped_free'
|
+--> 'wrapped_free': events 10-11
|
| {
| ^
| |
| (10) entering 'wrapped_free'
| free (ptr);
| ~~~~~~~~~~
| |
| (11) calling 'free'
|
<-------------+
|
'test': event 12
|
| free_boxed_int (obj);
| ^~~~~~~~~~~~~~~~~~~~
| |
| (12) calling 'free_boxed_int'
|
+--> 'free_boxed_int': events 13-14
|
| {
| ^
| |
| (13) entering 'free_boxed_int'
| wrapped_free (bi);
| ~~~~~~~~~~~~~~~~~
| |
| (14) calling 'wrapped_free'
|
+--> 'wrapped_free': events 15-16
|
| {
| ^
| |
| (15) entering 'wrapped_free'
| free (ptr);
| ~~~~~~~~~~
| |
| (16) calling 'free'
|
{ dg-end-multiline-output "" } */
}
typedef struct boxed_int
{
int i;
} boxed_int;
boxed_int *
make_boxed_int (int i)
{
boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
result->i = i;
return result;
}
void
free_boxed_int (boxed_int *bi)
{
wrapped_free (bi);
}
void test (int i)
{
boxed_int *obj = make_boxed_int (i);
free_boxed_int (obj);
free_boxed_int (obj);
}

View file

@ -0,0 +1,154 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
/* Verify that 'inline-events' copes gracefully with events with an
unknown location. */
#include <stdlib.h>
extern void missing_location ();
void *wrapped_malloc (size_t size)
{
return malloc (size);
}
void wrapped_free (void *ptr)
{
free (ptr); /* { dg-warning "double-free of 'ptr' \\\[CWE-415\\]" } */
/* { dg-begin-multiline-output "" }
free (ptr);
^~~~~~~~~~
'test': events 1-2
|
| {
| ^
| |
| (1) entering 'test'
| boxed_int *obj = make_boxed_int (i);
| ~~~~~~~~~~~~~~~~~~
| |
| (2) calling 'make_boxed_int'
|
+--> 'make_boxed_int': events 3-4
|
| {
| ^
| |
| (3) entering 'make_boxed_int'
| boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| (4) calling 'wrapped_malloc'
|
+--> 'wrapped_malloc': events 5-6
|
| {
| ^
| |
| (5) entering 'wrapped_malloc'
| return malloc (size);
| ~~~~~~~~~~~~~
| |
| (6) calling 'malloc'
|
<-------------+
|
'test': event 7
|
| free_boxed_int (obj);
| ^~~~~~~~~~~~~~~~~~~~
| |
| (7) calling 'free_boxed_int'
|
+--> 'free_boxed_int': events 8-9
|
| {
| ^
| |
| (8) entering 'free_boxed_int'
| wrapped_free (bi);
| ~~~~~~~~~~~~~~~~~
| |
| (9) calling 'wrapped_free'
|
+--> 'wrapped_free': events 10-11
|
| {
| ^
| |
| (10) entering 'wrapped_free'
| free (ptr);
| ~~~~~~~~~~
| |
| (11) calling 'free'
|
<-------------+
|
'test': event 12
|
|cc1:
| (12): calling 'missing_location'
|
'test': event 13
|
| free_boxed_int (obj);
| ^~~~~~~~~~~~~~~~~~~~
| |
| (13) calling 'free_boxed_int'
|
+--> 'free_boxed_int': events 14-15
|
| {
| ^
| |
| (14) entering 'free_boxed_int'
| wrapped_free (bi);
| ~~~~~~~~~~~~~~~~~
| |
| (15) calling 'wrapped_free'
|
+--> 'wrapped_free': events 16-17
|
| {
| ^
| |
| (16) entering 'wrapped_free'
| free (ptr);
| ~~~~~~~~~~
| |
| (17) calling 'free'
|
{ dg-end-multiline-output "" } */
}
typedef struct boxed_int
{
int i;
} boxed_int;
boxed_int *
make_boxed_int (int i)
{
boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
result->i = i;
return result;
}
void
free_boxed_int (boxed_int *bi)
{
wrapped_free (bi);
}
void test (int i)
{
boxed_int *obj = make_boxed_int (i);
free_boxed_int (obj);
missing_location ();
free_boxed_int (obj);
}

View file

@ -0,0 +1,154 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-line-numbers -fdiagnostics-show-caret" } */
/* { dg-enable-nn-line-numbers "" } */
/* Verify the interaction of inline-events with line numbers. */
#include <stdlib.h>
extern void missing_location ();
void *wrapped_malloc (size_t size)
{
return malloc (size);
}
void wrapped_free (void *ptr)
{
free (ptr); /* { dg-warning "double-free of 'ptr' \\\[CWE-415\\]" } */
/* { dg-begin-multiline-output "" }
NN | free (ptr);
| ^~~~~~~~~~
'test': events 1-2
|
| NN | {
| | ^
| | |
| | (1) entering 'test'
| NN | boxed_int *obj = make_boxed_int (i);
| | ~~~~~~~~~~~~~~~~~~
| | |
| | (2) calling 'make_boxed_int'
|
+--> 'make_boxed_int': events 3-4
|
| NN | {
| | ^
| | |
| | (3) entering 'make_boxed_int'
| NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (4) calling 'wrapped_malloc'
|
+--> 'wrapped_malloc': events 5-6
|
| NN | {
| | ^
| | |
| | (5) entering 'wrapped_malloc'
| NN | return malloc (size);
| | ~~~~~~~~~~~~~
| | |
| | (6) calling 'malloc'
|
<-------------+
|
'test': event 7
|
| NN | free_boxed_int (obj);
| | ^~~~~~~~~~~~~~~~~~~~
| | |
| | (7) calling 'free_boxed_int'
|
+--> 'free_boxed_int': events 8-9
|
| NN | {
| | ^
| | |
| | (8) entering 'free_boxed_int'
| NN | wrapped_free (bi);
| | ~~~~~~~~~~~~~~~~~
| | |
| | (9) calling 'wrapped_free'
|
+--> 'wrapped_free': events 10-11
|
| NN | {
| | ^
| | |
| | (10) entering 'wrapped_free'
| NN | free (ptr);
| | ~~~~~~~~~~
| | |
| | (11) calling 'free'
|
<-------------+
|
'test': event 12
|
|cc1:
| (12): calling 'missing_location'
|
'test': event 13
|
| NN | free_boxed_int (obj);
| | ^~~~~~~~~~~~~~~~~~~~
| | |
| | (13) calling 'free_boxed_int'
|
+--> 'free_boxed_int': events 14-15
|
| NN | {
| | ^
| | |
| | (14) entering 'free_boxed_int'
| NN | wrapped_free (bi);
| | ~~~~~~~~~~~~~~~~~
| | |
| | (15) calling 'wrapped_free'
|
+--> 'wrapped_free': events 16-17
|
| NN | {
| | ^
| | |
| | (16) entering 'wrapped_free'
| NN | free (ptr);
| | ~~~~~~~~~~
| | |
| | (17) calling 'free'
|
{ dg-end-multiline-output "" } */
}
typedef struct boxed_int
{
int i;
} boxed_int;
boxed_int *
make_boxed_int (int i)
{
boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
result->i = i;
return result;
}
void
free_boxed_int (boxed_int *bi)
{
wrapped_free (bi);
}
void test (int i)
{
boxed_int *obj = make_boxed_int (i);
free_boxed_int (obj);
missing_location ();
free_boxed_int (obj);
}

View file

@ -0,0 +1,43 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-path-format=none" } */
#include <stdlib.h>
void *wrapped_malloc (size_t size)
{
return malloc (size);
}
void wrapped_free (void *ptr)
{
free (ptr); /* { dg-warning "double-free of 'ptr' \\\[CWE-415\\]" } */
}
typedef struct boxed_int
{
int i;
} boxed_int;
boxed_int *
make_boxed_int (int i)
{
boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
result->i = i;
return result;
}
void
free_boxed_int (boxed_int *bi)
{
wrapped_free (bi);
}
void test (int i)
{
boxed_int *obj = make_boxed_int (i);
free_boxed_int (obj);
free_boxed_int (obj);
}

View file

@ -0,0 +1,44 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-path-format=separate-events" } */
#include <stdlib.h>
void *wrapped_malloc (size_t size)
{
return malloc (size);
}
void wrapped_free (void *ptr)
{
free (ptr); /* { dg-warning "double-free of 'ptr' \\\[CWE-415\\]" } */
}
typedef struct boxed_int
{
int i;
} boxed_int;
boxed_int *
make_boxed_int (int i)
{
boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
result->i = i;
return result;
}
void
free_boxed_int (boxed_int *bi)
{
wrapped_free (bi);
}
void test (int i)
{ /* { dg-message "\\(1\\) entering 'test'" } */
boxed_int *obj = make_boxed_int (i); /* { dg-message "\\(2\\) calling 'make_boxed_int'" } */
/* etc */
free_boxed_int (obj);
free_boxed_int (obj);
}

View file

@ -0,0 +1,38 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-path-format=separate-events" } */
#include <stddef.h>
#include <stdlib.h>
/* Minimal reimplementation of cpython API. */
typedef struct PyObject {} PyObject;
extern int PyArg_ParseTuple (PyObject *args, const char *fmt, ...);
extern PyObject *PyList_New (int);
extern PyObject *PyLong_FromLong(long);
extern void PyList_Append(PyObject *list, PyObject *item);
PyObject *
make_a_list_of_random_ints_badly(PyObject *self,
PyObject *args)
{
PyObject *list, *item;
long count, i;
if (!PyArg_ParseTuple(args, "i", &count)) {
return NULL;
}
list = PyList_New(0); /* { dg-line PyList_New } */
for (i = 0; i < count; i++) { /* { dg-line for } */
item = PyLong_FromLong(random());
PyList_Append(list, item); /* { dg-line PyList_Append } */
}
return list;
/* { dg-error "passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter" "" { target *-*-* } PyList_Append } */
/* { dg-message "\\(1\\) when 'PyList_New' fails, returning NULL" "" { target *-*-* } PyList_New } */
/* { dg-message "\\(2\\) when 'i < count'" "" { target *-*-* } for } */
/* { dg-message "\\(3\\) when calling 'PyList_Append', passing NULL from \\(1\\) as argument 1" "" { target *-*-* } PyList_Append } */
}

View file

@ -0,0 +1,56 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-show-caret -fdiagnostics-show-line-numbers" } */
#include <stddef.h>
#include <stdlib.h>
/* Minimal reimplementation of cpython API. */
typedef struct PyObject {} PyObject;
extern int PyArg_ParseTuple (PyObject *args, const char *fmt, ...);
extern PyObject *PyList_New (int);
extern PyObject *PyLong_FromLong(long);
extern void PyList_Append(PyObject *list, PyObject *item);
PyObject *
make_a_list_of_random_ints_badly(PyObject *self,
PyObject *args)
{
PyObject *list, *item;
long count, i;
if (!PyArg_ParseTuple(args, "i", &count)) {
return NULL;
}
list = PyList_New(0); /* { dg-line PyList_New } */
for (i = 0; i < count; i++) {
item = PyLong_FromLong(random());
PyList_Append(list, item); /* { dg-line PyList_Append } */
}
return list;
/* { dg-error "passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter" "" { target *-*-* } PyList_Append } */
/* { dg-begin-multiline-output "" }
29 | PyList_Append(list, item);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
'make_a_list_of_random_ints_badly': events 1-3
|
| 25 | list = PyList_New(0);
| | ^~~~~~~~~~~~~
| | |
| | (1) when 'PyList_New' fails, returning NULL
| 26 |
| 27 | for (i = 0; i < count; i++) {
| | ~~~
| | |
| | (2) when 'i < count'
| 28 | item = PyLong_FromLong(random());
| 29 | PyList_Append(list, item);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (3) when calling 'PyList_Append', passing NULL from (1) as argument 1
|
{ dg-end-multiline-output "" } */
}

View file

@ -0,0 +1,38 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-format=json" } */
#include <stddef.h>
#include <stdlib.h>
/* Minimal reimplementation of cpython API. */
typedef struct PyObject {} PyObject;
extern int PyArg_ParseTuple (PyObject *args, const char *fmt, ...);
extern PyObject *PyList_New (int);
extern PyObject *PyLong_FromLong(long);
extern void PyList_Append(PyObject *list, PyObject *item);
PyObject *
make_a_list_of_random_ints_badly(PyObject *self,
PyObject *args)
{
PyObject *list, *item;
long count, i;
if (!PyArg_ParseTuple(args, "i", &count)) {
return NULL;
}
list = PyList_New(0);
for (i = 0; i < count; i++) {
item = PyLong_FromLong(random());
PyList_Append(list, item);
}
return list;
}
/* FIXME: test the events within a path. */
/* { dg-regexp "\"kind\": \"error\"" } */
/* { dg-regexp "\"path\": " } */
/* { dg-regexp ".*" } */

View file

@ -0,0 +1,84 @@
/* { dg-do compile } */
/* { dg-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fdiagnostics-show-line-numbers" } */
/* { dg-enable-nn-line-numbers "" } */
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
extern void body_of_program(void);
void custom_logger(const char *msg)
{
fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
}
static void int_handler(int signum)
{
custom_logger("got signal");
}
static void register_handler ()
{
signal(SIGINT, int_handler);
}
void test (void)
{
register_handler ();
body_of_program();
}
/* { dg-begin-multiline-output "" }
NN | fprintf(stderr, "LOG: %s", msg);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'test': events 1-2
|
| NN | {
| | ^
| | |
| | (1) entering 'test'
| NN | register_handler ();
| | ~~~~~~~~~~~~~~~~~~~
| | |
| | (2) calling 'register_handler'
|
+--> 'register_handler': events 3-4
|
| NN | {
| | ^
| | |
| | (3) entering 'register_handler'
| NN | signal(SIGINT, int_handler);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (4) registering 'int_handler' as signal handler
|
event 5
|
|cc1:
| (5): later on, when the signal is delivered to the process
|
+--> 'int_handler': events 6-7
|
| NN | {
| | ^
| | |
| | (6) entering 'int_handler'
| NN | custom_logger("got signal");
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (7) calling 'custom_logger'
|
+--> 'custom_logger': events 8-9
|
| NN | {
| | ^
| | |
| | (8) entering 'custom_logger'
| NN | fprintf(stderr, "LOG: %s", msg);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (9) calling 'fprintf'
|
{ dg-end-multiline-output "" } */

View file

@ -0,0 +1,460 @@
/* { dg-options "-O" } */
/* This plugin exercises the path-printing code.
The goal is to unit-test the path-printing code without needing any
specific tests within the compiler's IR. We can't use any real
diagnostics for this, so we have to fake it, hence this plugin. */
#include "gcc-plugin.h"
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "stringpool.h"
#include "toplev.h"
#include "basic-block.h"
#include "hash-table.h"
#include "vec.h"
#include "ggc.h"
#include "basic-block.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-fold.h"
#include "tree-eh.h"
#include "gimple-expr.h"
#include "is-a.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "tree.h"
#include "tree-pass.h"
#include "intl.h"
#include "plugin-version.h"
#include "diagnostic.h"
#include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "context.h"
#include "print-tree.h"
#include "gcc-rich-location.h"
#include "cgraph.h"
int plugin_is_GPL_compatible;
const pass_data pass_data_test_show_path =
{
IPA_PASS, /* type */
"test_show_path", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_NONE, /* tv_id */
PROP_ssa, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
class pass_test_show_path : public ipa_opt_pass_d
{
public:
pass_test_show_path(gcc::context *ctxt)
: ipa_opt_pass_d (pass_data_test_show_path, ctxt,
NULL, /* generate_summary */
NULL, /* write_summary */
NULL, /* read_summary */
NULL, /* write_optimization_summary */
NULL, /* read_optimization_summary */
NULL, /* stmt_fixup */
0, /* function_transform_todo_flags_start */
NULL, /* function_transform */
NULL) /* variable_transform */
{}
/* opt_pass methods: */
bool gate (function *) { return true; }
virtual unsigned int execute (function *);
}; // class pass_test_show_path
/* Determine if STMT is a call with NUM_ARGS arguments to a function
named FUNCNAME.
If so, return STMT as a gcall *. Otherwise return NULL. */
static gcall *
check_for_named_call (gimple *stmt,
const char *funcname, unsigned int num_args)
{
gcc_assert (funcname);
gcall *call = dyn_cast <gcall *> (stmt);
if (!call)
return NULL;
tree fndecl = gimple_call_fndecl (call);
if (!fndecl)
return NULL;
if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname))
return NULL;
if (gimple_call_num_args (call) != num_args)
{
error_at (stmt->location, "expected number of args: %i (got %i)",
num_args, gimple_call_num_args (call));
return NULL;
}
return call;
}
/* Example 1: a purely intraprocedural path. */
static void
example_1 ()
{
gimple_stmt_iterator gsi;
basic_block bb;
gcall *call_to_PyList_Append = NULL;
gcall *call_to_PyList_New = NULL;
gcond *for_cond = NULL;
function *example_a_fun = NULL;
cgraph_node *node;
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
{
function *fun = node->get_fun ();
FOR_EACH_BB_FN (bb, fun)
{
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple *stmt = gsi_stmt (gsi);
if (gcall *call = check_for_named_call (stmt, "PyList_New", 1))
{
call_to_PyList_New = call;
example_a_fun = fun;
}
if (gcall *call = check_for_named_call (stmt, "PyList_Append", 2))
call_to_PyList_Append = call;
if (gcond *cond = dyn_cast <gcond *> (stmt))
for_cond = cond;
}
}
}
if (call_to_PyList_New && for_cond && call_to_PyList_Append)
{
auto_diagnostic_group d;
gcc_rich_location richloc (gimple_location (call_to_PyList_Append));
simple_diagnostic_path path (global_dc->printer);
diagnostic_event_id_t alloc_event_id
= path.add_event (gimple_location (call_to_PyList_New),
example_a_fun->decl, 0,
"when %qs fails, returning NULL",
"PyList_New");
path.add_event (gimple_location (for_cond),
example_a_fun->decl, 0,
"when %qs", "i < count");
path.add_event (gimple_location (call_to_PyList_Append),
example_a_fun->decl, 0,
"when calling %qs, passing NULL from %@ as argument %i",
"PyList_Append", &alloc_event_id, 1);
richloc.set_path (&path);
error_at (&richloc,
"passing NULL as argument %i to %qs"
" which requires a non-NULL parameter",
1, "PyList_Append");
}
}
/* A (function, location_t) pair. */
struct event_location_t
{
event_location_t ()
: m_fun (NULL), m_loc (UNKNOWN_LOCATION)
{}
event_location_t (function *fun, location_t loc)
: m_fun (fun), m_loc (loc)
{}
void set (const gimple *stmt, function *fun)
{
m_fun = fun;
m_loc = gimple_location (stmt);
}
function *m_fun;
location_t m_loc;
};
/* If FUN's name matches FUNCNAME, write the function and its start location
into *OUT_ENTRY. */
static void
check_for_named_function (function *fun, const char *funcname,
event_location_t *out_entry)
{
gcc_assert (fun);
gcc_assert (funcname);
if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fun->decl)), funcname))
return;
*out_entry = event_location_t (fun, fun->function_start_locus);
}
/* Example 2: an interprocedural path. */
class test_diagnostic_path : public simple_diagnostic_path
{
public:
test_diagnostic_path (pretty_printer *event_pp)
: simple_diagnostic_path (event_pp)
{
}
void add_entry (event_location_t evloc, int stack_depth,
const char *funcname)
{
gcc_assert (evloc.m_fun);
add_event (evloc.m_loc, evloc.m_fun->decl, stack_depth,
"entering %qs", funcname);
}
void add_call (event_location_t call_evloc, int caller_stack_depth,
event_location_t callee_entry_evloc, const char *callee)
{
gcc_assert (call_evloc.m_fun);
add_event (call_evloc.m_loc, call_evloc.m_fun->decl, caller_stack_depth,
"calling %qs", callee);
add_entry (callee_entry_evloc, caller_stack_depth + 1, callee);
}
void add_leaf_call (event_location_t call_evloc, int caller_stack_depth,
const char *callee)
{
gcc_assert (call_evloc.m_fun);
add_event (call_evloc.m_loc, call_evloc.m_fun->decl, caller_stack_depth,
"calling %qs", callee);
}
};
static void
example_2 ()
{
gimple_stmt_iterator gsi;
basic_block bb;
event_location_t entry_to_wrapped_malloc;
event_location_t call_to_malloc;
event_location_t entry_to_wrapped_free;
event_location_t call_to_free;
event_location_t entry_to_make_boxed_int;
event_location_t call_to_wrapped_malloc;
event_location_t entry_to_free_boxed_int;
event_location_t call_to_wrapped_free;
event_location_t entry_to_test;
event_location_t call_to_make_boxed_int;
event_location_t call_to_free_boxed_int;
event_location_t call_to_missing_location;
cgraph_node *node;
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
{
function *fun = node->get_fun ();
FOR_EACH_BB_FN (bb, fun)
{
check_for_named_function (fun, "wrapped_malloc",
&entry_to_wrapped_malloc);
check_for_named_function (fun, "wrapped_free",
&entry_to_wrapped_free);
check_for_named_function (fun, "make_boxed_int",
&entry_to_make_boxed_int);
check_for_named_function (fun, "free_boxed_int",
&entry_to_free_boxed_int);
check_for_named_function (fun, "test",
&entry_to_test);
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple *stmt = gsi_stmt (gsi);
if (gcall *call = check_for_named_call (stmt, "malloc", 1))
call_to_malloc.set (call, fun);
if (gcall *call = check_for_named_call (stmt, "free", 1))
call_to_free.set (call, fun);
if (gcall *call = check_for_named_call (stmt, "wrapped_malloc", 1))
call_to_wrapped_malloc.set (call, fun);
if (gcall *call = check_for_named_call (stmt, "wrapped_free", 1))
call_to_wrapped_free.set (call, fun);
if (gcall *call = check_for_named_call (stmt, "make_boxed_int", 1))
call_to_make_boxed_int.set (call, fun);
if (gcall *call = check_for_named_call (stmt, "free_boxed_int", 1))
call_to_free_boxed_int.set (call, fun);
if (gcall *call = check_for_named_call (stmt, "missing_location", 0))
{
call_to_missing_location.set (call, fun);
/* Simulate an event that's missing a useful location_t. */
call_to_missing_location.m_loc = UNKNOWN_LOCATION;
}
}
}
}
if (call_to_malloc.m_fun)
{
auto_diagnostic_group d;
gcc_rich_location richloc (call_to_free.m_loc);
test_diagnostic_path path (global_dc->printer);
path.add_entry (entry_to_test, 0, "test");
path.add_call (call_to_make_boxed_int, 0,
entry_to_make_boxed_int, "make_boxed_int");
path.add_call (call_to_wrapped_malloc, 1,
entry_to_wrapped_malloc, "wrapped_malloc");
path.add_leaf_call (call_to_malloc, 2, "malloc");
for (int i = 0; i < 2; i++)
{
path.add_call (call_to_free_boxed_int, 0,
entry_to_free_boxed_int, "free_boxed_int");
path.add_call (call_to_wrapped_free, 1,
entry_to_wrapped_free, "wrapped_free");
path.add_leaf_call (call_to_free, 2, "free");
if (i == 0 && call_to_missing_location.m_fun)
path.add_leaf_call (call_to_missing_location, 0, "missing_location");
}
richloc.set_path (&path);
diagnostic_metadata m;
m.add_cwe (415); /* CWE-415: Double Free. */
warning_at (&richloc, m, 0,
"double-free of %qs", "ptr");
}
}
/* Example 3: an interprocedural path with a callback. */
static void
example_3 ()
{
gimple_stmt_iterator gsi;
basic_block bb;
event_location_t entry_to_custom_logger;
event_location_t call_to_fprintf;
event_location_t entry_to_int_handler;
event_location_t call_to_custom_logger;
event_location_t entry_to_register_handler;
event_location_t call_to_signal;
event_location_t entry_to_test;
event_location_t call_to_register_handler;
cgraph_node *node;
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
{
function *fun = node->get_fun ();
FOR_EACH_BB_FN (bb, fun)
{
check_for_named_function (fun, "custom_logger",
&entry_to_custom_logger);
check_for_named_function (fun, "int_handler",
&entry_to_int_handler);
check_for_named_function (fun, "register_handler",
&entry_to_register_handler);
check_for_named_function (fun, "test",
&entry_to_test);
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple *stmt = gsi_stmt (gsi);
if (gcall *call = check_for_named_call (stmt, "fprintf", 3))
call_to_fprintf.set (call, fun);
if (gcall *call = check_for_named_call (stmt, "custom_logger", 1))
call_to_custom_logger.set (call, fun);
if (gcall *call = check_for_named_call (stmt, "register_handler",
0))
call_to_register_handler.set (call, fun);
if (gcall *call = check_for_named_call (stmt, "signal", 2))
call_to_signal.set (call, fun);
}
}
}
if (call_to_fprintf.m_fun)
{
auto_diagnostic_group d;
gcc_rich_location richloc (call_to_fprintf.m_loc);
test_diagnostic_path path (global_dc->printer);
path.add_entry (entry_to_test, 1, "test");
path.add_call (call_to_register_handler, 1,
entry_to_register_handler, "register_handler");
path.add_event (call_to_signal.m_loc, call_to_signal.m_fun->decl,
2, "registering 'int_handler' as signal handler");
path.add_event (UNKNOWN_LOCATION, NULL_TREE, 0,
"later on, when the signal is delivered to the process");
path.add_entry (entry_to_int_handler, 1, "int_handler");
path.add_call (call_to_custom_logger, 1,
entry_to_custom_logger, "custom_logger");
path.add_leaf_call (call_to_fprintf, 2, "fprintf");
richloc.set_path (&path);
diagnostic_metadata m;
/* CWE-479: Signal Handler Use of a Non-reentrant Function. */
m.add_cwe (479);
warning_at (&richloc, m, 0,
"call to %qs from within signal handler",
"fprintf");
}
}
unsigned int
pass_test_show_path::execute (function *)
{
example_1 ();
example_2 ();
example_3 ();
return 0;
}
static opt_pass *
make_pass_test_show_path (gcc::context *ctxt)
{
return new pass_test_show_path (ctxt);
}
int
plugin_init (struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version)
{
struct register_pass_info pass_info;
const char *plugin_name = plugin_info->base_name;
int argc = plugin_info->argc;
struct plugin_argument *argv = plugin_info->argv;
if (!plugin_default_version_check (version, &gcc_version))
return 1;
pass_info.pass = make_pass_test_show_path (g);
pass_info.reference_pass_name = "whole-program";
pass_info.ref_pass_instance_number = 1;
pass_info.pos_op = PASS_POS_INSERT_BEFORE;
register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
&pass_info);
return 0;
}

View file

@ -95,6 +95,17 @@ set plugin_test_list [list \
diagnostic-test-inlining-3.c \
diagnostic-test-inlining-4.c } \
{ diagnostic_plugin_test_metadata.c diagnostic-test-metadata.c } \
{ diagnostic_plugin_test_paths.c \
diagnostic-test-paths-1.c \
diagnostic-test-paths-2.c \
diagnostic-test-paths-3.c \
diagnostic-test-paths-4.c \
diagnostic-path-format-default.c \
diagnostic-path-format-none.c \
diagnostic-path-format-separate-events.c \
diagnostic-path-format-inline-events-1.c \
diagnostic-path-format-inline-events-2.c \
diagnostic-path-format-inline-events-3.c } \
{ location_overflow_plugin.c \
location-overflow-test-1.c \
location-overflow-test-2.c \

View file

@ -1181,6 +1181,10 @@ general_init (const char *argv0, bool init_signals)
= global_options_init.x_flag_diagnostics_show_line_numbers;
global_dc->show_cwe
= global_options_init.x_flag_diagnostics_show_cwe;
global_dc->path_format
= (enum diagnostic_path_format)global_options_init.x_flag_diagnostics_path_format;
global_dc->show_path_depths
= global_options_init.x_flag_diagnostics_show_path_depths;
global_dc->show_option_requested
= global_options_init.x_flag_diagnostics_show_option;
global_dc->min_margin_width

820
gcc/tree-diagnostic-path.cc Normal file
View file

@ -0,0 +1,820 @@
/* Paths through the code associated with a diagnostic.
Copyright (C) 2019-2020 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/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "diagnostic.h"
#include "tree-pretty-print.h"
#include "gimple-pretty-print.h"
#include "tree-diagnostic.h"
#include "langhooks.h"
#include "intl.h"
#include "diagnostic-path.h"
#include "json.h"
#include "gcc-rich-location.h"
#include "diagnostic-color.h"
#include "diagnostic-event-id.h"
#include "selftest.h"
#include "selftest-diagnostic.h"
/* Anonymous namespace for path-printing code. */
namespace {
/* Subclass of range_label for showing a particular event
when showing a consecutive run of events within a diagnostic_path as
labelled ranges within one gcc_rich_location. */
class path_label : public range_label
{
public:
path_label (const diagnostic_path *path, unsigned start_idx)
: m_path (path), m_start_idx (start_idx)
{}
label_text get_text (unsigned range_idx) const FINAL OVERRIDE
{
unsigned event_idx = m_start_idx + range_idx;
const diagnostic_event &event = m_path->get_event (event_idx);
/* Get the description of the event, perhaps with colorization:
normally, we don't colorize within a range_label, but this
is special-cased for diagnostic paths. */
bool colorize = pp_show_color (global_dc->printer);
label_text event_text (event.get_desc (colorize));
gcc_assert (event_text.m_buffer);
pretty_printer pp;
pp_show_color (&pp) = pp_show_color (global_dc->printer);
diagnostic_event_id_t event_id (event_idx);
pp_printf (&pp, "%@ %s", &event_id, event_text.m_buffer);
event_text.maybe_free ();
label_text result = label_text::take (xstrdup (pp_formatted_text (&pp)));
return result;
}
private:
const diagnostic_path *m_path;
unsigned m_start_idx;
};
/* Return true if E1 and E2 can be consolidated into the same run of events
when printing a diagnostic_path. */
static bool
can_consolidate_events (const diagnostic_event &e1,
const diagnostic_event &e2,
bool check_locations)
{
if (e1.get_fndecl () != e2.get_fndecl ())
return false;
if (e1.get_stack_depth () != e2.get_stack_depth ())
return false;
if (check_locations)
{
location_t loc1 = e1.get_location ();
location_t loc2 = e2.get_location ();
if (loc1 < RESERVED_LOCATION_COUNT
|| loc2 < RESERVED_LOCATION_COUNT)
return false;
/* Neither can be macro-based. */
if (linemap_location_from_macro_expansion_p (line_table, loc1))
return false;
if (linemap_location_from_macro_expansion_p (line_table, loc2))
return false;
}
/* Passed all the tests. */
return true;
}
/* A class for grouing together the events in a diagnostic_path into
ranges of events, partitioned by stack frame (i.e. by fndecl and
stack depth). */
class path_summary
{
/* A range of consecutive events within a diagnostic_path,
all with the same fndecl and stack_depth, and which are suitable
to print with a single call to diagnostic_show_locus. */
struct event_range
{
event_range (const diagnostic_path *path, unsigned start_idx,
const diagnostic_event &initial_event)
: m_path (path),
m_initial_event (initial_event),
m_fndecl (initial_event.get_fndecl ()),
m_stack_depth (initial_event.get_stack_depth ()),
m_start_idx (start_idx), m_end_idx (start_idx),
m_path_label (path, start_idx),
m_richloc (initial_event.get_location (), &m_path_label)
{}
bool maybe_add_event (const diagnostic_event &new_ev, unsigned idx,
bool check_rich_locations)
{
if (!can_consolidate_events (m_initial_event, new_ev,
check_rich_locations))
return false;
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;
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)
{
location_t initial_loc = m_initial_event.get_location ();
/* Emit a span indicating the filename (and line/column) if the
line has changed relative to the last call to
diagnostic_show_locus. */
if (dc->show_caret)
{
expanded_location exploc
= linemap_client_expand_location_to_spelling_point
(initial_loc, LOCATION_ASPECT_CARET);
if (exploc.file != LOCATION_FILE (dc->last_location))
dc->start_span (dc, exploc);
}
/* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
primary location for an event, diagnostic_show_locus won't print
anything.
In particular the label for the event won't get printed.
Fail more gracefully in this case by showing the event
index and text, at no particular location. */
if (initial_loc <= BUILTINS_LOCATION)
{
for (unsigned i = m_start_idx; i <= m_end_idx; i++)
{
const diagnostic_event &iter_event = m_path->get_event (i);
diagnostic_event_id_t event_id (i);
label_text event_text (iter_event.get_desc (true));
pretty_printer *pp = dc->printer;
pp_printf (pp, " %@: %s", &event_id, event_text.m_buffer);
pp_newline (pp);
event_text.maybe_free ();
}
return;
}
/* Call diagnostic_show_locus to show the events using labels. */
diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH);
/* If we have a macro expansion, show the expansion to the user. */
if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
{
gcc_assert (m_start_idx == m_end_idx);
maybe_unwind_expanded_macro_loc (dc, initial_loc);
}
}
const diagnostic_path *m_path;
const diagnostic_event &m_initial_event;
tree m_fndecl;
int m_stack_depth;
unsigned m_start_idx;
unsigned m_end_idx;
path_label m_path_label;
gcc_rich_location m_richloc;
};
public:
path_summary (const diagnostic_path &path, bool check_rich_locations);
void print (diagnostic_context *dc, bool show_depths) const;
unsigned get_num_ranges () const { return m_ranges.length (); }
private:
auto_delete_vec <event_range> m_ranges;
};
/* path_summary's ctor. */
path_summary::path_summary (const diagnostic_path &path,
bool check_rich_locations)
{
const unsigned num_events = path.num_events ();
event_range *cur_event_range = NULL;
for (unsigned idx = 0; idx < num_events; idx++)
{
const diagnostic_event &event = path.get_event (idx);
if (cur_event_range)
if (cur_event_range->maybe_add_event (event, idx, check_rich_locations))
continue;
cur_event_range = new event_range (&path, idx, event);
m_ranges.safe_push (cur_event_range);
}
}
/* Write SPACES to PP. */
static void
write_indent (pretty_printer *pp, int spaces)
{
for (int i = 0; i < spaces; i++)
pp_space (pp);
}
/* Print FNDDECL to PP, quoting it if QUOTED is true.
We can't use "%qE" here since we can't guarantee the capabilities
of PP. */
static void
print_fndecl (pretty_printer *pp, tree fndecl, bool quoted)
{
const char *n = DECL_NAME (fndecl)
? identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2))
: _("<anonymous>");
if (quoted)
pp_printf (pp, "%qs", n);
else
pp_string (pp, n);
}
/* Print this path_summary to DC, giving an overview of the interprocedural
calls and returns.
Print the event descriptions in a nested form, printing the event
descriptions within calls to diagnostic_show_locus, using labels to
show the events:
'foo' (events 1-2)
| NN |
| |
+--> 'bar' (events 3-4)
| NN |
| |
+--> 'baz' (events 5-6)
| NN |
| |
<------------ +
|
'foo' (events 7-8)
| NN |
| |
+--> 'bar' (events 9-10)
| NN |
| |
+--> 'baz' (events 11-12)
| NN |
| |
If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
of events.
For events with UNKNOWN_LOCATION, print a summary of each the event. */
void
path_summary::print (diagnostic_context *dc, bool show_depths) const
{
pretty_printer *pp = dc->printer;
const int per_frame_indent = 2;
const char *const line_color = "path";
const char *start_line_color
= colorize_start (pp_show_color (pp), line_color);
const char *end_line_color = colorize_stop (pp_show_color (pp));
/* Keep track of column numbers of existing '|' characters for
stack depths we've already printed. */
const int EMPTY = -1;
const int DELETED = -2;
typedef int_hash <int, EMPTY, DELETED> vbar_hash;
hash_map <vbar_hash, int> vbar_column_for_depth;
/* Print the ranges. */
const int base_indent = 2;
int cur_indent = base_indent;
unsigned i;
event_range *range;
FOR_EACH_VEC_ELT (m_ranges, i, range)
{
write_indent (pp, cur_indent);
if (i > 0)
{
const path_summary::event_range *prev_range
= m_ranges[i - 1];
if (range->m_stack_depth > prev_range->m_stack_depth)
{
/* Show pushed stack frame(s). */
const char *push_prefix = "+--> ";
pp_string (pp, start_line_color);
pp_string (pp, push_prefix);
pp_string (pp, end_line_color);
cur_indent += strlen (push_prefix);
}
}
if (range->m_fndecl)
{
print_fndecl (pp, range->m_fndecl, true);
pp_string (pp, ": ");
}
if (range->m_start_idx == range->m_end_idx)
pp_printf (pp, "event %i",
range->m_start_idx + 1);
else
pp_printf (pp, "events %i-%i",
range->m_start_idx + 1, range->m_end_idx + 1);
if (show_depths)
pp_printf (pp, " (depth %i)", range->m_stack_depth);
pp_newline (pp);
/* Print a run of events. */
{
write_indent (pp, cur_indent + per_frame_indent);
pp_string (pp, start_line_color);
pp_string (pp, "|");
pp_string (pp, end_line_color);
pp_newline (pp);
char *saved_prefix = pp_take_prefix (pp);
char *prefix;
{
pretty_printer tmp_pp;
write_indent (&tmp_pp, cur_indent + per_frame_indent);
pp_string (&tmp_pp, start_line_color);
pp_string (&tmp_pp, "|");
pp_string (&tmp_pp, end_line_color);
prefix = xstrdup (pp_formatted_text (&tmp_pp));
}
pp_set_prefix (pp, prefix);
pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
range->print (dc);
pp_set_prefix (pp, saved_prefix);
write_indent (pp, cur_indent + per_frame_indent);
pp_string (pp, start_line_color);
pp_string (pp, "|");
pp_string (pp, end_line_color);
pp_newline (pp);
}
if (i < m_ranges.length () - 1)
{
const path_summary::event_range *next_range
= m_ranges[i + 1];
if (range->m_stack_depth > next_range->m_stack_depth)
{
if (vbar_column_for_depth.get (next_range->m_stack_depth))
{
/* Show returning from stack frame(s), by printing
something like:
" |\n"
" <------------ +\n"
" |\n". */
int vbar_for_next_frame
= *vbar_column_for_depth.get (next_range->m_stack_depth);
int indent_for_next_frame
= vbar_for_next_frame - per_frame_indent;
write_indent (pp, vbar_for_next_frame);
pp_string (pp, start_line_color);
pp_character (pp, '<');
for (int i = indent_for_next_frame + per_frame_indent;
i < cur_indent + per_frame_indent - 1; i++)
pp_character (pp, '-');
pp_character (pp, '+');
pp_string (pp, end_line_color);
pp_newline (pp);
cur_indent = indent_for_next_frame;
write_indent (pp, vbar_for_next_frame);
pp_string (pp, start_line_color);
pp_printf (pp, "|");
pp_string (pp, end_line_color);
pp_newline (pp);
}
else
{
/* Handle disjoint paths (e.g. a callback at some later
time). */
cur_indent = base_indent;
}
}
else if (range->m_stack_depth < next_range->m_stack_depth)
{
/* Prepare to show pushed stack frame. */
gcc_assert (range->m_stack_depth != EMPTY);
gcc_assert (range->m_stack_depth != DELETED);
vbar_column_for_depth.put (range->m_stack_depth,
cur_indent + per_frame_indent);
cur_indent += per_frame_indent;
}
}
}
}
} /* end of anonymous namespace for path-printing code. */
/* Print PATH to CONTEXT, according to CONTEXT's path_format. */
void
default_tree_diagnostic_path_printer (diagnostic_context *context,
const diagnostic_path *path)
{
gcc_assert (path);
const unsigned num_events = path->num_events ();
switch (context->path_format)
{
case DPF_NONE:
/* Do nothing. */
return;
case DPF_SEPARATE_EVENTS:
{
/* A note per event. */
for (unsigned i = 0; i < num_events; i++)
{
const diagnostic_event &event = path->get_event (i);
label_text event_text (event.get_desc (false));
gcc_assert (event_text.m_buffer);
diagnostic_event_id_t event_id (i);
inform (event.get_location (),
"%@ %s", &event_id, event_text.m_buffer);
event_text.maybe_free ();
}
}
break;
case DPF_INLINE_EVENTS:
{
/* Consolidate related events. */
path_summary summary (*path, true);
char *saved_prefix = pp_take_prefix (context->printer);
pp_set_prefix (context->printer, NULL);
summary.print (context, context->show_path_depths);
pp_flush (context->printer);
pp_set_prefix (context->printer, saved_prefix);
}
}
}
/* This has to be here, rather than diagnostic-format-json.cc,
since diagnostic-format-json.o is within OBJS-libcommon and thus
doesn't have access to trees (for m_fndecl). */
json::value *
default_tree_make_json_for_path (diagnostic_context *,
const diagnostic_path *path)
{
json::array *path_array = new json::array ();
for (unsigned i = 0; i < path->num_events (); i++)
{
const diagnostic_event &event = path->get_event (i);
json::object *event_obj = new json::object ();
if (event.get_location ())
event_obj->set ("location",
json_from_expanded_location (event.get_location ()));
label_text event_text (event.get_desc (false));
event_obj->set ("description", new json::string (event_text.m_buffer));
event_text.maybe_free ();
if (tree fndecl = event.get_fndecl ())
{
const char *function
= identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2));
event_obj->set ("function", new json::string (function));
}
event_obj->set ("depth",
new json::integer_number (event.get_stack_depth ()));
path_array->append (event_obj);
}
return path_array;
}
#if CHECKING_P
namespace selftest {
/* A subclass of simple_diagnostic_path that adds member functions
for adding test events. */
class test_diagnostic_path : public simple_diagnostic_path
{
public:
test_diagnostic_path (pretty_printer *event_pp)
: simple_diagnostic_path (event_pp)
{
}
void add_entry (tree fndecl, int stack_depth)
{
add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
"entering %qE", fndecl);
}
void add_return (tree fndecl, int stack_depth)
{
add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
"returning to %qE", fndecl);
}
void add_call (tree caller, int caller_stack_depth, tree callee)
{
add_event (UNKNOWN_LOCATION, caller, caller_stack_depth,
"calling %qE", callee);
add_entry (callee, caller_stack_depth + 1);
}
};
/* Verify that empty paths are handled gracefully. */
static void
test_empty_path (pretty_printer *event_pp)
{
test_diagnostic_path path (event_pp);
ASSERT_FALSE (path.interprocedural_p ());
path_summary summary (path, false);
ASSERT_EQ (summary.get_num_ranges (), 0);
test_diagnostic_context dc;
summary.print (&dc, true);
ASSERT_STREQ ("",
pp_formatted_text (dc.printer));
}
/* Verify that print_path_summary works on a purely intraprocedural path. */
static void
test_intraprocedural_path (pretty_printer *event_pp)
{
tree fntype_void_void
= build_function_type_array (void_type_node, 0, NULL);
tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
test_diagnostic_path path (event_pp);
path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free");
path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free");
ASSERT_FALSE (path.interprocedural_p ());
path_summary summary (path, false);
ASSERT_EQ (summary.get_num_ranges (), 1);
test_diagnostic_context dc;
summary.print (&dc, true);
ASSERT_STREQ (" `foo': events 1-2 (depth 0)\n"
" |\n"
" | (1): first `free'\n"
" | (2): double `free'\n"
" |\n",
pp_formatted_text (dc.printer));
}
/* Verify that print_path_summary works on an interprocedural path. */
static void
test_interprocedural_path_1 (pretty_printer *event_pp)
{
/* Build fndecls. The types aren't quite right, but that
doesn't matter for the purposes of this test. */
tree fntype_void_void
= build_function_type_array (void_type_node, 0, NULL);
tree fndecl_test = build_fn_decl ("test", fntype_void_void);
tree fndecl_make_boxed_int
= build_fn_decl ("make_boxed_int", fntype_void_void);
tree fndecl_wrapped_malloc
= build_fn_decl ("wrapped_malloc", fntype_void_void);
tree fndecl_free_boxed_int
= build_fn_decl ("free_boxed_int", fntype_void_void);
tree fndecl_wrapped_free
= build_fn_decl ("wrapped_free", fntype_void_void);
test_diagnostic_path path (event_pp);
path.add_entry (fndecl_test, 0);
path.add_call (fndecl_test, 0, fndecl_make_boxed_int);
path.add_call (fndecl_make_boxed_int, 1, fndecl_wrapped_malloc);
path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_malloc, 2, "calling malloc");
path.add_return (fndecl_test, 0);
path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
path.add_return (fndecl_test, 0);
path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
ASSERT_EQ (path.num_events (), 18);
ASSERT_TRUE (path.interprocedural_p ());
path_summary summary (path, false);
ASSERT_EQ (summary.get_num_ranges (), 9);
test_diagnostic_context dc;
summary.print (&dc, true);
ASSERT_STREQ
(" `test': events 1-2 (depth 0)\n"
" |\n"
" | (1): entering `test'\n"
" | (2): calling `make_boxed_int'\n"
" |\n"
" +--> `make_boxed_int': events 3-4 (depth 1)\n"
" |\n"
" | (3): entering `make_boxed_int'\n"
" | (4): calling `wrapped_malloc'\n"
" |\n"
" +--> `wrapped_malloc': events 5-6 (depth 2)\n"
" |\n"
" | (5): entering `wrapped_malloc'\n"
" | (6): calling malloc\n"
" |\n"
" <-------------+\n"
" |\n"
" `test': events 7-8 (depth 0)\n"
" |\n"
" | (7): returning to `test'\n"
" | (8): calling `free_boxed_int'\n"
" |\n"
" +--> `free_boxed_int': events 9-10 (depth 1)\n"
" |\n"
" | (9): entering `free_boxed_int'\n"
" | (10): calling `wrapped_free'\n"
" |\n"
" +--> `wrapped_free': events 11-12 (depth 2)\n"
" |\n"
" | (11): entering `wrapped_free'\n"
" | (12): calling free\n"
" |\n"
" <-------------+\n"
" |\n"
" `test': events 13-14 (depth 0)\n"
" |\n"
" | (13): returning to `test'\n"
" | (14): calling `free_boxed_int'\n"
" |\n"
" +--> `free_boxed_int': events 15-16 (depth 1)\n"
" |\n"
" | (15): entering `free_boxed_int'\n"
" | (16): calling `wrapped_free'\n"
" |\n"
" +--> `wrapped_free': events 17-18 (depth 2)\n"
" |\n"
" | (17): entering `wrapped_free'\n"
" | (18): calling free\n"
" |\n",
pp_formatted_text (dc.printer));
}
/* Example where we pop the stack to an intermediate frame, rather than the
initial one. */
static void
test_interprocedural_path_2 (pretty_printer *event_pp)
{
/* Build fndecls. The types aren't quite right, but that
doesn't matter for the purposes of this test. */
tree fntype_void_void
= build_function_type_array (void_type_node, 0, NULL);
tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
tree fndecl_bar = build_fn_decl ("bar", fntype_void_void);
tree fndecl_baz = build_fn_decl ("baz", fntype_void_void);
test_diagnostic_path path (event_pp);
path.add_entry (fndecl_foo, 0);
path.add_call (fndecl_foo, 0, fndecl_bar);
path.add_call (fndecl_bar, 1, fndecl_baz);
path.add_return (fndecl_bar, 1);
path.add_call (fndecl_bar, 1, fndecl_baz);
ASSERT_EQ (path.num_events (), 8);
ASSERT_TRUE (path.interprocedural_p ());
path_summary summary (path, false);
ASSERT_EQ (summary.get_num_ranges (), 5);
test_diagnostic_context dc;
summary.print (&dc, true);
ASSERT_STREQ
(" `foo': events 1-2 (depth 0)\n"
" |\n"
" | (1): entering `foo'\n"
" | (2): calling `bar'\n"
" |\n"
" +--> `bar': events 3-4 (depth 1)\n"
" |\n"
" | (3): entering `bar'\n"
" | (4): calling `baz'\n"
" |\n"
" +--> `baz': event 5 (depth 2)\n"
" |\n"
" | (5): entering `baz'\n"
" |\n"
" <------+\n"
" |\n"
" `bar': events 6-7 (depth 1)\n"
" |\n"
" | (6): returning to `bar'\n"
" | (7): calling `baz'\n"
" |\n"
" +--> `baz': event 8 (depth 2)\n"
" |\n"
" | (8): entering `baz'\n"
" |\n",
pp_formatted_text (dc.printer));
}
/* Verify that print_path_summary is sane in the face of a recursive
diagnostic_path. */
static void
test_recursion (pretty_printer *event_pp)
{
tree fntype_void_void
= build_function_type_array (void_type_node, 0, NULL);
tree fndecl_factorial = build_fn_decl ("factorial", fntype_void_void);
test_diagnostic_path path (event_pp);
path.add_entry (fndecl_factorial, 0);
for (int depth = 0; depth < 3; depth++)
path.add_call (fndecl_factorial, depth, fndecl_factorial);
ASSERT_EQ (path.num_events (), 7);
ASSERT_TRUE (path.interprocedural_p ());
path_summary summary (path, false);
ASSERT_EQ (summary.get_num_ranges (), 4);
test_diagnostic_context dc;
summary.print (&dc, true);
ASSERT_STREQ
(" `factorial': events 1-2 (depth 0)\n"
" |\n"
" | (1): entering `factorial'\n"
" | (2): calling `factorial'\n"
" |\n"
" +--> `factorial': events 3-4 (depth 1)\n"
" |\n"
" | (3): entering `factorial'\n"
" | (4): calling `factorial'\n"
" |\n"
" +--> `factorial': events 5-6 (depth 2)\n"
" |\n"
" | (5): entering `factorial'\n"
" | (6): calling `factorial'\n"
" |\n"
" +--> `factorial': event 7 (depth 3)\n"
" |\n"
" | (7): entering `factorial'\n"
" |\n",
pp_formatted_text (dc.printer));
}
/* Run all of the selftests within this file. */
void
tree_diagnostic_path_cc_tests ()
{
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;
}
} // namespace selftest
#endif /* #if CHECKING_P */

View file

@ -96,9 +96,8 @@ struct loc_map_pair
unwound macro expansion trace. That's the part generated by this
function. */
static void
void
maybe_unwind_expanded_macro_loc (diagnostic_context *context,
const diagnostic_info *diagnostic,
location_t where)
{
const struct line_map *map;
@ -106,6 +105,8 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
unsigned ix;
loc_map_pair loc, *iter;
const location_t original_loc = where;
map = linemap_lookup (line_table, where);
if (!linemap_macro_expansion_map_p (map))
return;
@ -142,7 +143,7 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
first macro which expansion triggered this trace was expanded
inside a system header. */
int saved_location_line =
expand_location_to_spelling_point (diagnostic_location (diagnostic)).line;
expand_location_to_spelling_point (original_loc).line;
if (!LINEMAP_SYSP (ord_map))
FOR_EACH_VEC_ELT (loc_vec, ix, iter)
@ -238,8 +239,7 @@ void
virt_loc_aware_diagnostic_finalizer (diagnostic_context *context,
diagnostic_info *diagnostic)
{
maybe_unwind_expanded_macro_loc (context, diagnostic,
diagnostic_location (diagnostic));
maybe_unwind_expanded_macro_loc (context, diagnostic_location (diagnostic));
}
/* Default tree printer. Handles declarations only. */
@ -312,4 +312,6 @@ tree_diagnostics_defaults (diagnostic_context *context)
diagnostic_starter (context) = default_tree_diagnostic_starter;
diagnostic_finalizer (context) = default_diagnostic_finalizer;
diagnostic_format_decoder (context) = default_tree_printer;
context->print_path = default_tree_diagnostic_path_printer;
context->make_json_for_path = default_tree_make_json_for_path;
}

View file

@ -57,4 +57,12 @@ void tree_diagnostics_defaults (diagnostic_context *context);
bool default_tree_printer (pretty_printer *, text_info *, const char *,
int, bool, bool, bool, bool *, const char **);
extern void default_tree_diagnostic_path_printer (diagnostic_context *,
const diagnostic_path *);
extern json::value *default_tree_make_json_for_path (diagnostic_context *,
const diagnostic_path *);
extern void maybe_unwind_expanded_macro_loc (diagnostic_context *context,
location_t where);
#endif /* ! GCC_TREE_DIAGNOSTIC_H */

View file

@ -1,3 +1,11 @@
2020-01-10 David Malcolm <dmalcolm@redhat.com>
* include/line-map.h (class diagnostic_path): New forward decl.
(rich_location::get_path): New accessor.
(rich_location::set_path): New function.
(rich_location::m_path): New field.
* line-map.c (rich_location::rich_location): Initialize m_path.
2020-01-01 Jakub Jelinek <jakub@redhat.com>
Update copyright years.

View file

@ -1432,6 +1432,7 @@ semi_embedded_vec<T, NUM_EMBEDDED>::truncate (int len)
}
class fixit_hint;
class diagnostic_path;
/* A "rich" source code location, for use when printing diagnostics.
A rich_location has one or more carets&ranges, where the carets
@ -1727,6 +1728,10 @@ class rich_location
return !m_fixits_cannot_be_auto_applied;
}
/* An optional path through the code. */
const diagnostic_path *get_path () const { return m_path; }
void set_path (const diagnostic_path *path) { m_path = path; }
private:
bool reject_impossible_fixit (location_t where);
void stop_supporting_fixits ();
@ -1751,6 +1756,8 @@ protected:
bool m_seen_impossible_fixit;
bool m_fixits_cannot_be_auto_applied;
const diagnostic_path *m_path;
};
/* A struct for the result of range_label::get_text: a NUL-terminated buffer

View file

@ -2006,7 +2006,8 @@ rich_location::rich_location (line_maps *set, location_t loc,
m_have_expanded_location (false),
m_fixit_hints (),
m_seen_impossible_fixit (false),
m_fixits_cannot_be_auto_applied (false)
m_fixits_cannot_be_auto_applied (false),
m_path (NULL)
{
add_range (loc, SHOW_RANGE_WITH_CARET, label);
}