1 /* Paths through the code associated with a diagnostic.
2 Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
22 #define INCLUDE_ALGORITHM
23 #define INCLUDE_MEMORY
24 #define INCLUDE_STRING
25 #define INCLUDE_VECTOR
27 #include "coretypes.h"
29 #include "diagnostic.h"
30 #include "tree-pretty-print.h"
31 #include "gimple-pretty-print.h"
32 #include "tree-diagnostic.h"
33 #include "langhooks.h"
35 #include "diagnostic-path.h"
37 #include "gcc-rich-location.h"
38 #include "diagnostic-color.h"
39 #include "diagnostic-event-id.h"
40 #include "diagnostic-label-effects.h"
42 #include "selftest-diagnostic.h"
43 #include "text-art/theme.h"
45 /* Anonymous namespace for path-printing code. */
49 /* Subclass of range_label for showing a particular event
50 when showing a consecutive run of events within a diagnostic_path as
51 labelled ranges within one gcc_rich_location. */
53 class path_label
: public range_label
56 path_label (const diagnostic_path
*path
, unsigned start_idx
)
57 : m_path (path
), m_start_idx (start_idx
), m_effects (*this)
60 label_text
get_text (unsigned range_idx
) const final override
62 unsigned event_idx
= m_start_idx
+ range_idx
;
63 const diagnostic_event
&event
= m_path
->get_event (event_idx
);
65 /* Get the description of the event, perhaps with colorization:
66 normally, we don't colorize within a range_label, but this
67 is special-cased for diagnostic paths. */
68 const bool colorize
= pp_show_color (global_dc
->printer
);
69 label_text
event_text (event
.get_desc (colorize
));
70 gcc_assert (event_text
.get ());
72 const diagnostic_event::meaning
meaning (event
.get_meaning ());
75 pp_show_color (&pp
) = colorize
;
76 diagnostic_event_id_t
event_id (event_idx
);
78 pp_printf (&pp
, "%@", &event_id
);
81 if (meaning
.m_verb
== diagnostic_event::VERB_danger
)
82 if (text_art::theme
*theme
= global_dc
->get_diagram_theme ())
83 if (theme
->emojis_p ())
85 pp_unicode_character (&pp
, 0x26A0); /* U+26A0 WARNING SIGN. */
86 /* Append U+FE0F VARIATION SELECTOR-16 to select the emoji
87 variation of the char. */
88 pp_unicode_character (&pp
, 0xFE0F);
89 /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
90 emoji variant is printed (by vte at least) with a 2nd half
91 overlapping the next char. Hence we add two spaces here: a space
92 to be covered by this overlap, plus another space of padding. */
96 pp_printf (&pp
, "%s", event_text
.get ());
98 label_text result
= label_text::take (xstrdup (pp_formatted_text (&pp
)));
102 const label_effects
*get_effects (unsigned /*range_idx*/) const
108 class path_label_effects
: public label_effects
111 path_label_effects (const path_label
&path_label
)
112 : m_path_label (path_label
)
115 bool has_in_edge (unsigned range_idx
) const final override
117 if (const diagnostic_event
*prev_event
118 = m_path_label
.get_prev_event (range_idx
))
119 return prev_event
->connect_to_next_event_p ();
122 bool has_out_edge (unsigned range_idx
) const final override
124 const diagnostic_event
&event
= m_path_label
.get_event (range_idx
);
125 return event
.connect_to_next_event_p ();
129 const path_label
&m_path_label
;
132 const diagnostic_event
&get_event (unsigned range_idx
) const
134 unsigned event_idx
= m_start_idx
+ range_idx
;
135 return m_path
->get_event (event_idx
);
138 const diagnostic_event
*get_prev_event (unsigned range_idx
) const
140 if (m_start_idx
+ range_idx
== 0)
142 unsigned event_idx
= m_start_idx
+ range_idx
- 1;
143 return &m_path
->get_event (event_idx
);
146 const diagnostic_path
*m_path
;
147 unsigned m_start_idx
;
148 path_label_effects m_effects
;
151 /* Return true if E1 and E2 can be consolidated into the same run of events
152 when printing a diagnostic_path. */
155 can_consolidate_events (const diagnostic_event
&e1
,
156 const diagnostic_event
&e2
,
157 bool check_locations
)
159 if (e1
.get_thread_id () != e2
.get_thread_id ())
162 if (e1
.get_fndecl () != e2
.get_fndecl ())
165 if (e1
.get_stack_depth () != e2
.get_stack_depth ())
170 location_t loc1
= e1
.get_location ();
171 location_t loc2
= e2
.get_location ();
173 if (loc1
< RESERVED_LOCATION_COUNT
174 || loc2
< RESERVED_LOCATION_COUNT
)
177 /* Neither can be macro-based. */
178 if (linemap_location_from_macro_expansion_p (line_table
, loc1
))
180 if (linemap_location_from_macro_expansion_p (line_table
, loc2
))
184 /* Passed all the tests. */
190 class thread_event_printer
;
192 /* A bundle of information about all of the events in a diagnostic_path
193 relating to a specific path, for use by path_summary. */
195 class per_thread_summary
198 per_thread_summary (label_text name
, unsigned swimlane_idx
)
199 : m_name (std::move (name
)),
200 m_swimlane_idx (swimlane_idx
),
201 m_last_event (nullptr),
202 m_min_depth (INT_MAX
),
203 m_max_depth (INT_MIN
)
206 void update_depth_limits (int stack_depth
)
208 if (stack_depth
< m_min_depth
)
209 m_min_depth
= stack_depth
;
210 if (stack_depth
> m_max_depth
)
211 m_max_depth
= stack_depth
;
214 const char *get_name () const { return m_name
.get (); }
215 unsigned get_swimlane_index () const { return m_swimlane_idx
; }
217 bool interprocedural_p () const;
220 friend struct path_summary
;
221 friend class thread_event_printer
;
222 friend struct event_range
;
224 const label_text m_name
;
226 /* The "swimlane index" is the order in which this per_thread_summary
227 was created, for use when printing the events. */
228 const unsigned m_swimlane_idx
;
230 // The event ranges specific to this thread:
231 auto_vec
<event_range
*> m_event_ranges
;
233 const diagnostic_event
*m_last_event
;
239 /* A range of consecutive events within a diagnostic_path, all within the
240 same thread, and with the same fndecl and stack_depth, and which are suitable
241 to print with a single call to diagnostic_show_locus. */
244 /* A struct for tracking the mergability of labels on a particular
245 source line. In particular, track information about links between
246 labels to ensure that we only consolidate events involving links
247 that the source-printing code is able to handle (splitting them
249 struct per_source_line_info
254 m_has_in_edge
= false;
255 m_has_out_edge
= false;
256 m_min_label_source_column
= INT_MAX
;
257 m_max_label_source_column
= INT_MIN
;
260 /* Return true if our source-printing/labelling/linking code can handle
261 the events already on this source line, *and* a new event at COLUMN. */
263 can_add_label_for_event_p (bool has_in_edge
,
264 const diagnostic_event
*prev_event
,
268 /* Any existing in-edge has to be the left-most label on its
270 if (m_has_in_edge
&& column
< m_min_label_source_column
)
272 /* Any existing out-edge has to be the right-most label on its
274 if (m_has_out_edge
&& column
> m_max_label_source_column
)
276 /* Can't have more than one in-edge. */
277 if (m_has_in_edge
&& has_in_edge
)
279 /* Can't have more than one out-edge. */
280 if (m_has_out_edge
&& has_out_edge
)
285 /* Any new in-edge needs to be the left-most label on its
287 if (column
> m_min_label_source_column
)
290 gcc_assert (prev_event
);
291 const location_t prev_loc
= prev_event
->get_location ();
292 expanded_location prev_exploc
293 = linemap_client_expand_location_to_spelling_point
294 (line_table
, prev_loc
, LOCATION_ASPECT_CARET
);
295 /* The destination in-edge's line number has to be <= the
296 source out-edge's line number (if any). */
297 if (prev_exploc
.line
>= m_line
)
301 /* Any new out-edge needs to be the right-most label on its
304 if (column
< m_max_label_source_column
)
307 /* All checks passed; we can add the new event at COLUMN. */
312 add_label_for_event (bool has_in_edge
, bool has_out_edge
, int column
)
315 m_has_in_edge
= true;
317 m_has_out_edge
= true;
318 m_min_label_source_column
= std::min (m_min_label_source_column
, column
);
319 m_max_label_source_column
= std::max (m_max_label_source_column
, column
);
325 int m_min_label_source_column
;
326 int m_max_label_source_column
;
329 event_range (const diagnostic_path
*path
, unsigned start_idx
,
330 const diagnostic_event
&initial_event
,
331 per_thread_summary
&t
,
332 bool show_event_links
)
334 m_initial_event (initial_event
),
335 m_fndecl (initial_event
.get_fndecl ()),
336 m_stack_depth (initial_event
.get_stack_depth ()),
337 m_start_idx (start_idx
), m_end_idx (start_idx
),
338 m_path_label (path
, start_idx
),
339 m_richloc (initial_event
.get_location (), &m_path_label
),
340 m_thread_id (initial_event
.get_thread_id ()),
341 m_per_thread_summary (t
),
342 m_show_event_links (show_event_links
)
344 if (m_show_event_links
)
346 expanded_location exploc
347 = linemap_client_expand_location_to_spelling_point
348 (line_table
, initial_event
.get_location (), LOCATION_ASPECT_CARET
);
349 per_source_line_info
&source_line_info
350 = get_per_source_line_info (exploc
.line
);
352 const diagnostic_event
*prev_thread_event
= t
.m_last_event
;
353 const bool has_in_edge
355 ? prev_thread_event
->connect_to_next_event_p ()
357 const bool has_out_edge
= initial_event
.connect_to_next_event_p ();
359 source_line_info
.add_label_for_event
360 (has_in_edge
, has_out_edge
, exploc
.column
);
364 per_source_line_info
&
365 get_per_source_line_info (int source_line
)
367 bool existed
= false;
368 per_source_line_info
&result
369 = m_source_line_info_map
.get_or_insert (source_line
, &existed
);
371 result
.init (source_line
);
375 bool maybe_add_event (const diagnostic_event
&new_ev
, unsigned idx
,
376 bool check_rich_locations
)
378 if (!can_consolidate_events (m_initial_event
, new_ev
,
379 check_rich_locations
))
382 /* Verify compatibility of the new label and existing labels
383 with respect to the link-printing code. */
384 expanded_location exploc
385 = linemap_client_expand_location_to_spelling_point
386 (line_table
, new_ev
.get_location (), LOCATION_ASPECT_CARET
);
387 per_source_line_info
&source_line_info
388 = get_per_source_line_info (exploc
.line
);
389 const diagnostic_event
*prev_event
= nullptr;
391 prev_event
= &m_path
->get_event (idx
- 1);
392 const bool has_in_edge
= (prev_event
393 ? prev_event
->connect_to_next_event_p ()
395 const bool has_out_edge
= new_ev
.connect_to_next_event_p ();
396 if (m_show_event_links
)
397 if (!source_line_info
.can_add_label_for_event_p
398 (has_in_edge
, prev_event
,
399 has_out_edge
, exploc
.column
))
402 /* Potentially verify that the locations are sufficiently close. */
403 if (check_rich_locations
)
404 if (!m_richloc
.add_location_if_nearby (new_ev
.get_location (),
405 false, &m_path_label
))
409 m_per_thread_summary
.m_last_event
= &new_ev
;
411 if (m_show_event_links
)
412 source_line_info
.add_label_for_event
413 (has_in_edge
, has_out_edge
, exploc
.column
);
418 /* Print the events in this range to DC, typically as a single
419 call to the printer's diagnostic_show_locus. */
421 void print (diagnostic_context
*dc
, pretty_printer
*pp
,
422 diagnostic_source_effect_info
*effect_info
)
424 location_t initial_loc
= m_initial_event
.get_location ();
426 /* Emit a span indicating the filename (and line/column) if the
427 line has changed relative to the last call to
428 diagnostic_show_locus. */
429 if (dc
->m_source_printing
.enabled
)
431 expanded_location exploc
432 = linemap_client_expand_location_to_spelling_point
433 (line_table
, initial_loc
, LOCATION_ASPECT_CARET
);
434 if (exploc
.file
!= LOCATION_FILE (dc
->m_last_location
))
435 diagnostic_start_span (dc
) (dc
, exploc
);
438 /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
439 primary location for an event, diagnostic_show_locus won't print
442 In particular the label for the event won't get printed.
443 Fail more gracefully in this case by showing the event
444 index and text, at no particular location. */
445 if (get_pure_location (initial_loc
) <= BUILTINS_LOCATION
)
447 for (unsigned i
= m_start_idx
; i
<= m_end_idx
; i
++)
449 const diagnostic_event
&iter_event
= m_path
->get_event (i
);
450 diagnostic_event_id_t
event_id (i
);
451 label_text
event_text (iter_event
.get_desc (true));
452 pp_printf (pp
, " %@: %s", &event_id
, event_text
.get ());
458 /* Call diagnostic_show_locus to show the events using labels. */
459 diagnostic_show_locus (dc
, &m_richloc
, DK_DIAGNOSTIC_PATH
, pp
,
462 /* If we have a macro expansion, show the expansion to the user. */
463 if (linemap_location_from_macro_expansion_p (line_table
, initial_loc
))
465 gcc_assert (m_start_idx
== m_end_idx
);
466 maybe_unwind_expanded_macro_loc (dc
, initial_loc
);
470 const diagnostic_path
*m_path
;
471 const diagnostic_event
&m_initial_event
;
474 unsigned m_start_idx
;
476 path_label m_path_label
;
477 gcc_rich_location m_richloc
;
478 diagnostic_thread_id_t m_thread_id
;
479 per_thread_summary
&m_per_thread_summary
;
480 hash_map
<int_hash
<int, -1, -2>,
481 per_source_line_info
> m_source_line_info_map
;
482 bool m_show_event_links
;
485 /* A struct for grouping together the events in a diagnostic_path into
486 ranges of events, partitioned by thread and by stack frame (i.e. by fndecl
491 path_summary (const diagnostic_path
&path
,
492 bool check_rich_locations
,
493 bool show_event_links
= true);
495 unsigned get_num_ranges () const { return m_ranges
.length (); }
496 bool multithreaded_p () const { return m_per_thread_summary
.length () > 1; }
498 const per_thread_summary
&get_events_for_thread_id (diagnostic_thread_id_t tid
)
500 per_thread_summary
**slot
= m_thread_id_to_events
.get (tid
);
506 auto_delete_vec
<event_range
> m_ranges
;
507 auto_delete_vec
<per_thread_summary
> m_per_thread_summary
;
508 hash_map
<int_hash
<diagnostic_thread_id_t
, -1, -2>,
509 per_thread_summary
*> m_thread_id_to_events
;
513 get_or_create_events_for_thread_id (const diagnostic_path
&path
,
514 diagnostic_thread_id_t tid
)
516 if (per_thread_summary
**slot
= m_thread_id_to_events
.get (tid
))
519 const diagnostic_thread
&thread
= path
.get_thread (tid
);
520 per_thread_summary
*pts
= new per_thread_summary (thread
.get_name (false),
521 m_per_thread_summary
.length ());
522 m_thread_id_to_events
.put (tid
, pts
);
523 m_per_thread_summary
.safe_push (pts
);
528 /* Return true iff there is more than one stack frame used by the events
532 per_thread_summary::interprocedural_p () const
534 if (m_event_ranges
.is_empty ())
536 tree first_fndecl
= m_event_ranges
[0]->m_fndecl
;
537 int first_stack_depth
= m_event_ranges
[0]->m_stack_depth
;
538 for (auto range
: m_event_ranges
)
540 if (range
->m_fndecl
!= first_fndecl
)
542 if (range
->m_stack_depth
!= first_stack_depth
)
548 /* path_summary's ctor. */
550 path_summary::path_summary (const diagnostic_path
&path
,
551 bool check_rich_locations
,
552 bool show_event_links
)
554 const unsigned num_events
= path
.num_events ();
556 event_range
*cur_event_range
= NULL
;
557 for (unsigned idx
= 0; idx
< num_events
; idx
++)
559 const diagnostic_event
&event
= path
.get_event (idx
);
560 const diagnostic_thread_id_t thread_id
= event
.get_thread_id ();
561 per_thread_summary
&pts
562 = get_or_create_events_for_thread_id (path
, thread_id
);
564 pts
.update_depth_limits (event
.get_stack_depth ());
567 if (cur_event_range
->maybe_add_event (event
, idx
, check_rich_locations
))
570 cur_event_range
= new event_range (&path
, idx
, event
, pts
,
572 m_ranges
.safe_push (cur_event_range
);
573 pts
.m_event_ranges
.safe_push (cur_event_range
);
574 pts
.m_last_event
= &event
;
578 /* Write SPACES to PP. */
581 write_indent (pretty_printer
*pp
, int spaces
)
583 for (int i
= 0; i
< spaces
; i
++)
587 /* Print FNDDECL to PP, quoting it if QUOTED is true.
589 We can't use "%qE" here since we can't guarantee the capabilities
593 print_fndecl (pretty_printer
*pp
, tree fndecl
, bool quoted
)
595 const char *n
= DECL_NAME (fndecl
)
596 ? identifier_to_locale (lang_hooks
.decl_printable_name (fndecl
, 2))
599 pp_printf (pp
, "%qs", n
);
604 static const int base_indent
= 2;
605 static const int per_frame_indent
= 2;
607 /* A bundle of state for printing event_range instances for a particular
610 class thread_event_printer
613 thread_event_printer (const per_thread_summary
&t
, bool show_depths
)
614 : m_per_thread_summary (t
),
615 m_show_depths (show_depths
),
616 m_cur_indent (base_indent
),
617 m_vbar_column_for_depth (),
622 /* Get the previous event_range within this thread, if any. */
623 const event_range
*get_any_prev_range () const
625 if (m_num_printed
> 0)
626 return m_per_thread_summary
.m_event_ranges
[m_num_printed
- 1];
631 /* Get the next event_range within this thread, if any. */
632 const event_range
*get_any_next_range () const
634 if (m_num_printed
< m_per_thread_summary
.m_event_ranges
.length () - 1)
635 return m_per_thread_summary
.m_event_ranges
[m_num_printed
+ 1];
641 print_swimlane_for_event_range (diagnostic_context
*dc
,
644 diagnostic_source_effect_info
*effect_info
)
646 const char *const line_color
= "path";
647 const char *start_line_color
648 = colorize_start (pp_show_color (pp
), line_color
);
649 const char *end_line_color
= colorize_stop (pp_show_color (pp
));
651 text_art::ascii_theme fallback_theme
;
652 text_art::theme
*theme
= dc
->get_diagram_theme ();
654 theme
= &fallback_theme
;
656 cppchar_t depth_marker_char
= theme
->get_cppchar
657 (text_art::theme::cell_kind::INTERPROCEDURAL_DEPTH_MARKER
);
660 const bool interprocedural_p
= m_per_thread_summary
.interprocedural_p ();
662 write_indent (pp
, m_cur_indent
);
663 if (const event_range
*prev_range
= get_any_prev_range ())
665 if (range
->m_stack_depth
> prev_range
->m_stack_depth
)
667 gcc_assert (interprocedural_p
);
668 /* Show pushed stack frame(s). */
669 cppchar_t left
= theme
->get_cppchar
670 (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_LEFT
);
671 cppchar_t middle
= theme
->get_cppchar
672 (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_MIDDLE
);
673 cppchar_t right
= theme
->get_cppchar
674 (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_RIGHT
);
676 pp_string (pp
, start_line_color
);
677 pp_unicode_character (pp
, left
);
678 pp_unicode_character (pp
, middle
);
679 pp_unicode_character (pp
, middle
);
680 pp_unicode_character (pp
, right
);
682 pp_string (pp
, end_line_color
);
688 print_fndecl (pp
, range
->m_fndecl
, true);
689 pp_string (pp
, ": ");
691 if (range
->m_start_idx
== range
->m_end_idx
)
692 pp_printf (pp
, "event %i",
693 range
->m_start_idx
+ 1);
695 pp_printf (pp
, "events %i-%i",
696 range
->m_start_idx
+ 1, range
->m_end_idx
+ 1);
698 pp_printf (pp
, " (depth %i)", range
->m_stack_depth
);
701 /* Print a run of events. */
702 if (interprocedural_p
)
704 write_indent (pp
, m_cur_indent
+ per_frame_indent
);
705 pp_string (pp
, start_line_color
);
706 pp_unicode_character (pp
, depth_marker_char
);
707 pp_string (pp
, end_line_color
);
710 char *saved_prefix
= pp_take_prefix (pp
);
713 pretty_printer tmp_pp
;
714 write_indent (&tmp_pp
, m_cur_indent
+ per_frame_indent
);
715 pp_string (&tmp_pp
, start_line_color
);
716 pp_unicode_character (&tmp_pp
, depth_marker_char
);
717 pp_string (&tmp_pp
, end_line_color
);
718 prefix
= xstrdup (pp_formatted_text (&tmp_pp
));
720 pp_set_prefix (pp
, prefix
);
721 pp_prefixing_rule (pp
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
722 range
->print (dc
, pp
, effect_info
);
723 pp_set_prefix (pp
, saved_prefix
);
725 write_indent (pp
, m_cur_indent
+ per_frame_indent
);
726 pp_string (pp
, start_line_color
);
727 pp_unicode_character (pp
, depth_marker_char
);
728 pp_string (pp
, end_line_color
);
732 range
->print (dc
, pp
, effect_info
);
734 if (const event_range
*next_range
= get_any_next_range ())
736 if (range
->m_stack_depth
> next_range
->m_stack_depth
)
738 if (m_vbar_column_for_depth
.get (next_range
->m_stack_depth
))
740 /* Show returning from stack frame(s), by printing
745 gcc_assert (interprocedural_p
);
746 cppchar_t left
= theme
->get_cppchar
747 (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_LEFT
);
748 cppchar_t middle
= theme
->get_cppchar
749 (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_MIDDLE
);
750 cppchar_t right
= theme
->get_cppchar
751 (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_RIGHT
);
752 int vbar_for_next_frame
753 = *m_vbar_column_for_depth
.get (next_range
->m_stack_depth
);
755 int indent_for_next_frame
756 = vbar_for_next_frame
- per_frame_indent
;
757 write_indent (pp
, vbar_for_next_frame
);
758 pp_string (pp
, start_line_color
);
759 pp_unicode_character (pp
, left
);
760 for (int i
= indent_for_next_frame
+ per_frame_indent
;
761 i
< m_cur_indent
+ per_frame_indent
- 1; i
++)
762 pp_unicode_character (pp
, middle
);
763 pp_unicode_character (pp
, right
);
764 pp_string (pp
, end_line_color
);
766 m_cur_indent
= indent_for_next_frame
;
768 write_indent (pp
, vbar_for_next_frame
);
769 pp_string (pp
, start_line_color
);
770 pp_unicode_character (pp
, depth_marker_char
);
771 pp_string (pp
, end_line_color
);
776 /* Handle disjoint paths (e.g. a callback at some later
778 m_cur_indent
= base_indent
;
781 else if (range
->m_stack_depth
< next_range
->m_stack_depth
)
783 /* Prepare to show pushed stack frame. */
784 gcc_assert (interprocedural_p
);
785 gcc_assert (range
->m_stack_depth
!= EMPTY
);
786 gcc_assert (range
->m_stack_depth
!= DELETED
);
787 m_vbar_column_for_depth
.put (range
->m_stack_depth
,
788 m_cur_indent
+ per_frame_indent
);
789 m_cur_indent
+= per_frame_indent
;
796 int get_cur_indent () const { return m_cur_indent
; }
799 const per_thread_summary
&m_per_thread_summary
;
802 /* Print the ranges. */
805 /* Keep track of column numbers of existing '|' characters for
806 stack depths we've already printed. */
807 static const int EMPTY
= -1;
808 static const int DELETED
= -2;
809 typedef int_hash
<int, EMPTY
, DELETED
> vbar_hash
;
810 hash_map
<vbar_hash
, int> m_vbar_column_for_depth
;
812 /* How many event ranges within this swimlane have we printed.
813 This is the index of the next event_range to print. */
814 unsigned m_num_printed
;
817 /* Print path_summary PS to DC, giving an overview of the interprocedural
820 Print the event descriptions in a nested form, printing the event
821 descriptions within calls to diagnostic_show_locus, using labels to
827 +--> 'bar' (events 3-4)
830 +--> 'baz' (events 5-6)
838 +--> 'bar' (events 9-10)
841 +--> 'baz' (events 11-12)
845 If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
848 For events with UNKNOWN_LOCATION, print a summary of each the event. */
851 print_path_summary_as_text (const path_summary
*ps
, diagnostic_context
*dc
,
854 pretty_printer
*pp
= dc
->printer
;
856 std::vector
<thread_event_printer
> thread_event_printers
;
857 for (auto t
: ps
->m_per_thread_summary
)
858 thread_event_printers
.push_back (thread_event_printer (*t
, show_depths
));
862 int last_out_edge_column
= -1;
863 FOR_EACH_VEC_ELT (ps
->m_ranges
, i
, range
)
865 const int swimlane_idx
866 = range
->m_per_thread_summary
.get_swimlane_index ();
867 if (ps
->multithreaded_p ())
868 if (i
== 0 || ps
->m_ranges
[i
- 1]->m_thread_id
!= range
->m_thread_id
)
872 pp_printf (pp
, "Thread: %qs",
873 range
->m_per_thread_summary
.get_name ());
876 thread_event_printer
&tep
= thread_event_printers
[swimlane_idx
];
877 /* Wire up any trailing out-edge from previous range to leading in-edge
879 diagnostic_source_effect_info effect_info
;
880 effect_info
.m_leading_in_edge_column
= last_out_edge_column
;
881 tep
.print_swimlane_for_event_range (dc
, pp
, range
, &effect_info
);
882 last_out_edge_column
= effect_info
.m_trailing_out_edge_column
;
886 } /* end of anonymous namespace for path-printing code. */
888 /* Print PATH to CONTEXT, according to CONTEXT's path_format. */
891 default_tree_diagnostic_path_printer (diagnostic_context
*context
,
892 const diagnostic_path
*path
)
896 const unsigned num_events
= path
->num_events ();
898 switch (context
->get_path_format ())
904 case DPF_SEPARATE_EVENTS
:
906 /* A note per event. */
907 for (unsigned i
= 0; i
< num_events
; i
++)
909 const diagnostic_event
&event
= path
->get_event (i
);
910 label_text
event_text (event
.get_desc (false));
911 gcc_assert (event_text
.get ());
912 diagnostic_event_id_t
event_id (i
);
913 if (context
->show_path_depths_p ())
915 int stack_depth
= event
.get_stack_depth ();
916 tree fndecl
= event
.get_fndecl ();
917 /* -fdiagnostics-path-format=separate-events doesn't print
918 fndecl information, so with -fdiagnostics-show-path-depths
919 print the fndecls too, if any. */
921 inform (event
.get_location (),
922 "%@ %s (fndecl %qD, depth %i)",
923 &event_id
, event_text
.get (),
924 fndecl
, stack_depth
);
926 inform (event
.get_location (),
928 &event_id
, event_text
.get (),
932 inform (event
.get_location (),
933 "%@ %s", &event_id
, event_text
.get ());
938 case DPF_INLINE_EVENTS
:
940 /* Consolidate related events. */
941 path_summary
summary (*path
, true,
942 context
->m_source_printing
.show_event_links_p
);
943 char *saved_prefix
= pp_take_prefix (context
->printer
);
944 pp_set_prefix (context
->printer
, NULL
);
945 print_path_summary_as_text (&summary
, context
,
946 context
->show_path_depths_p ());
947 pp_flush (context
->printer
);
948 pp_set_prefix (context
->printer
, saved_prefix
);
954 /* This has to be here, rather than diagnostic-format-json.cc,
955 since diagnostic-format-json.o is within OBJS-libcommon and thus
956 doesn't have access to trees (for m_fndecl). */
959 default_tree_make_json_for_path (diagnostic_context
*context
,
960 const diagnostic_path
*path
)
962 json::array
*path_array
= new json::array ();
963 for (unsigned i
= 0; i
< path
->num_events (); i
++)
965 const diagnostic_event
&event
= path
->get_event (i
);
967 json::object
*event_obj
= new json::object ();
968 if (event
.get_location ())
969 event_obj
->set ("location",
970 json_from_expanded_location (context
,
971 event
.get_location ()));
972 label_text
event_text (event
.get_desc (false));
973 event_obj
->set_string ("description", event_text
.get ());
974 if (tree fndecl
= event
.get_fndecl ())
977 = identifier_to_locale (lang_hooks
.decl_printable_name (fndecl
, 2));
978 event_obj
->set_string ("function", function
);
980 event_obj
->set_integer ("depth", event
.get_stack_depth ());
981 path_array
->append (event_obj
);
988 /* Disable warnings about missing quoting in GCC diagnostics for the print
989 calls in the tests below. */
991 # pragma GCC diagnostic push
992 # pragma GCC diagnostic ignored "-Wformat-diag"
997 /* Return true iff all events in PATH have locations for which column data
998 is available, so that selftests that require precise string output can
999 bail out for awkward line_table cases. */
1002 path_events_have_column_data_p (const diagnostic_path
&path
)
1004 for (unsigned idx
= 0; idx
< path
.num_events (); idx
++)
1006 location_t event_loc
= path
.get_event (idx
).get_location ();
1007 if (line_table
->get_pure_location (event_loc
)
1008 > LINE_MAP_MAX_LOCATION_WITH_COLS
)
1010 if (line_table
->get_start (event_loc
) > LINE_MAP_MAX_LOCATION_WITH_COLS
)
1012 if (line_table
->get_finish (event_loc
) > LINE_MAP_MAX_LOCATION_WITH_COLS
)
1018 /* A subclass of simple_diagnostic_path that adds member functions
1019 for adding test events. */
1021 class test_diagnostic_path
: public simple_diagnostic_path
1024 test_diagnostic_path (pretty_printer
*event_pp
)
1025 : simple_diagnostic_path (event_pp
)
1029 void add_entry (tree fndecl
, int stack_depth
)
1031 add_event (UNKNOWN_LOCATION
, fndecl
, stack_depth
,
1032 "entering %qE", fndecl
);
1035 void add_return (tree fndecl
, int stack_depth
)
1037 add_event (UNKNOWN_LOCATION
, fndecl
, stack_depth
,
1038 "returning to %qE", fndecl
);
1041 void add_call (tree caller
, int caller_stack_depth
, tree callee
)
1043 add_event (UNKNOWN_LOCATION
, caller
, caller_stack_depth
,
1044 "calling %qE", callee
);
1045 add_entry (callee
, caller_stack_depth
+ 1);
1049 /* Verify that empty paths are handled gracefully. */
1052 test_empty_path (pretty_printer
*event_pp
)
1054 test_diagnostic_path
path (event_pp
);
1055 ASSERT_FALSE (path
.interprocedural_p ());
1057 path_summary
summary (path
, false);
1058 ASSERT_EQ (summary
.get_num_ranges (), 0);
1060 test_diagnostic_context dc
;
1061 print_path_summary_as_text (&summary
, &dc
, true);
1063 pp_formatted_text (dc
.printer
));
1066 /* Verify that print_path_summary works on a purely intraprocedural path. */
1069 test_intraprocedural_path (pretty_printer
*event_pp
)
1071 tree fntype_void_void
1072 = build_function_type_array (void_type_node
, 0, NULL
);
1073 tree fndecl_foo
= build_fn_decl ("foo", fntype_void_void
);
1075 test_diagnostic_path
path (event_pp
);
1076 path
.add_event (UNKNOWN_LOCATION
, fndecl_foo
, 0, "first %qs", "free");
1077 path
.add_event (UNKNOWN_LOCATION
, fndecl_foo
, 0, "double %qs", "free");
1079 ASSERT_FALSE (path
.interprocedural_p ());
1081 path_summary
summary (path
, false);
1082 ASSERT_EQ (summary
.get_num_ranges (), 1);
1084 test_diagnostic_context dc
;
1085 print_path_summary_as_text (&summary
, &dc
, true);
1086 ASSERT_STREQ (" `foo': events 1-2 (depth 0)\n"
1087 " (1): first `free'\n"
1088 " (2): double `free'\n",
1089 pp_formatted_text (dc
.printer
));
1092 /* Verify that print_path_summary works on an interprocedural path. */
1095 test_interprocedural_path_1 (pretty_printer
*event_pp
)
1097 /* Build fndecls. The types aren't quite right, but that
1098 doesn't matter for the purposes of this test. */
1099 tree fntype_void_void
1100 = build_function_type_array (void_type_node
, 0, NULL
);
1101 tree fndecl_test
= build_fn_decl ("test", fntype_void_void
);
1102 tree fndecl_make_boxed_int
1103 = build_fn_decl ("make_boxed_int", fntype_void_void
);
1104 tree fndecl_wrapped_malloc
1105 = build_fn_decl ("wrapped_malloc", fntype_void_void
);
1106 tree fndecl_free_boxed_int
1107 = build_fn_decl ("free_boxed_int", fntype_void_void
);
1108 tree fndecl_wrapped_free
1109 = build_fn_decl ("wrapped_free", fntype_void_void
);
1111 test_diagnostic_path
path (event_pp
);
1112 path
.add_entry (fndecl_test
, 0);
1113 path
.add_call (fndecl_test
, 0, fndecl_make_boxed_int
);
1114 path
.add_call (fndecl_make_boxed_int
, 1, fndecl_wrapped_malloc
);
1115 path
.add_event (UNKNOWN_LOCATION
, fndecl_wrapped_malloc
, 2, "calling malloc");
1116 path
.add_return (fndecl_test
, 0);
1117 path
.add_call (fndecl_test
, 0, fndecl_free_boxed_int
);
1118 path
.add_call (fndecl_free_boxed_int
, 1, fndecl_wrapped_free
);
1119 path
.add_event (UNKNOWN_LOCATION
, fndecl_wrapped_free
, 2, "calling free");
1120 path
.add_return (fndecl_test
, 0);
1121 path
.add_call (fndecl_test
, 0, fndecl_free_boxed_int
);
1122 path
.add_call (fndecl_free_boxed_int
, 1, fndecl_wrapped_free
);
1123 path
.add_event (UNKNOWN_LOCATION
, fndecl_wrapped_free
, 2, "calling free");
1124 ASSERT_EQ (path
.num_events (), 18);
1126 ASSERT_TRUE (path
.interprocedural_p ());
1128 path_summary
summary (path
, false);
1129 ASSERT_EQ (summary
.get_num_ranges (), 9);
1132 test_diagnostic_context dc
;
1133 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1134 print_path_summary_as_text (&summary
, &dc
, true);
1136 (" `test': events 1-2 (depth 0)\n"
1138 " | (1): entering `test'\n"
1139 " | (2): calling `make_boxed_int'\n"
1141 " +--> `make_boxed_int': events 3-4 (depth 1)\n"
1143 " | (3): entering `make_boxed_int'\n"
1144 " | (4): calling `wrapped_malloc'\n"
1146 " +--> `wrapped_malloc': events 5-6 (depth 2)\n"
1148 " | (5): entering `wrapped_malloc'\n"
1149 " | (6): calling malloc\n"
1151 " <-------------+\n"
1153 " `test': events 7-8 (depth 0)\n"
1155 " | (7): returning to `test'\n"
1156 " | (8): calling `free_boxed_int'\n"
1158 " +--> `free_boxed_int': events 9-10 (depth 1)\n"
1160 " | (9): entering `free_boxed_int'\n"
1161 " | (10): calling `wrapped_free'\n"
1163 " +--> `wrapped_free': events 11-12 (depth 2)\n"
1165 " | (11): entering `wrapped_free'\n"
1166 " | (12): calling free\n"
1168 " <-------------+\n"
1170 " `test': events 13-14 (depth 0)\n"
1172 " | (13): returning to `test'\n"
1173 " | (14): calling `free_boxed_int'\n"
1175 " +--> `free_boxed_int': events 15-16 (depth 1)\n"
1177 " | (15): entering `free_boxed_int'\n"
1178 " | (16): calling `wrapped_free'\n"
1180 " +--> `wrapped_free': events 17-18 (depth 2)\n"
1182 " | (17): entering `wrapped_free'\n"
1183 " | (18): calling free\n"
1185 pp_formatted_text (dc
.printer
));
1188 test_diagnostic_context dc
;
1189 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE
);
1190 print_path_summary_as_text (&summary
, &dc
, true);
1192 (" `test': events 1-2 (depth 0)\n"
1194 " │ (1): entering `test'\n"
1195 " │ (2): calling `make_boxed_int'\n"
1197 " └──> `make_boxed_int': events 3-4 (depth 1)\n"
1199 " │ (3): entering `make_boxed_int'\n"
1200 " │ (4): calling `wrapped_malloc'\n"
1202 " └──> `wrapped_malloc': events 5-6 (depth 2)\n"
1204 " │ (5): entering `wrapped_malloc'\n"
1205 " │ (6): calling malloc\n"
1207 " <─────────────┘\n"
1209 " `test': events 7-8 (depth 0)\n"
1211 " │ (7): returning to `test'\n"
1212 " │ (8): calling `free_boxed_int'\n"
1214 " └──> `free_boxed_int': events 9-10 (depth 1)\n"
1216 " │ (9): entering `free_boxed_int'\n"
1217 " │ (10): calling `wrapped_free'\n"
1219 " └──> `wrapped_free': events 11-12 (depth 2)\n"
1221 " │ (11): entering `wrapped_free'\n"
1222 " │ (12): calling free\n"
1224 " <─────────────┘\n"
1226 " `test': events 13-14 (depth 0)\n"
1228 " │ (13): returning to `test'\n"
1229 " │ (14): calling `free_boxed_int'\n"
1231 " └──> `free_boxed_int': events 15-16 (depth 1)\n"
1233 " │ (15): entering `free_boxed_int'\n"
1234 " │ (16): calling `wrapped_free'\n"
1236 " └──> `wrapped_free': events 17-18 (depth 2)\n"
1238 " │ (17): entering `wrapped_free'\n"
1239 " │ (18): calling free\n"
1241 pp_formatted_text (dc
.printer
));
1246 /* Example where we pop the stack to an intermediate frame, rather than the
1250 test_interprocedural_path_2 (pretty_printer
*event_pp
)
1252 /* Build fndecls. The types aren't quite right, but that
1253 doesn't matter for the purposes of this test. */
1254 tree fntype_void_void
1255 = build_function_type_array (void_type_node
, 0, NULL
);
1256 tree fndecl_foo
= build_fn_decl ("foo", fntype_void_void
);
1257 tree fndecl_bar
= build_fn_decl ("bar", fntype_void_void
);
1258 tree fndecl_baz
= build_fn_decl ("baz", fntype_void_void
);
1260 test_diagnostic_path
path (event_pp
);
1261 path
.add_entry (fndecl_foo
, 0);
1262 path
.add_call (fndecl_foo
, 0, fndecl_bar
);
1263 path
.add_call (fndecl_bar
, 1, fndecl_baz
);
1264 path
.add_return (fndecl_bar
, 1);
1265 path
.add_call (fndecl_bar
, 1, fndecl_baz
);
1266 ASSERT_EQ (path
.num_events (), 8);
1268 ASSERT_TRUE (path
.interprocedural_p ());
1270 path_summary
summary (path
, false);
1271 ASSERT_EQ (summary
.get_num_ranges (), 5);
1274 test_diagnostic_context dc
;
1275 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1276 print_path_summary_as_text (&summary
, &dc
, true);
1278 (" `foo': events 1-2 (depth 0)\n"
1280 " | (1): entering `foo'\n"
1281 " | (2): calling `bar'\n"
1283 " +--> `bar': events 3-4 (depth 1)\n"
1285 " | (3): entering `bar'\n"
1286 " | (4): calling `baz'\n"
1288 " +--> `baz': event 5 (depth 2)\n"
1290 " | (5): entering `baz'\n"
1294 " `bar': events 6-7 (depth 1)\n"
1296 " | (6): returning to `bar'\n"
1297 " | (7): calling `baz'\n"
1299 " +--> `baz': event 8 (depth 2)\n"
1301 " | (8): entering `baz'\n"
1303 pp_formatted_text (dc
.printer
));
1306 test_diagnostic_context dc
;
1307 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE
);
1308 print_path_summary_as_text (&summary
, &dc
, true);
1310 (" `foo': events 1-2 (depth 0)\n"
1312 " │ (1): entering `foo'\n"
1313 " │ (2): calling `bar'\n"
1315 " └──> `bar': events 3-4 (depth 1)\n"
1317 " │ (3): entering `bar'\n"
1318 " │ (4): calling `baz'\n"
1320 " └──> `baz': event 5 (depth 2)\n"
1322 " │ (5): entering `baz'\n"
1326 " `bar': events 6-7 (depth 1)\n"
1328 " │ (6): returning to `bar'\n"
1329 " │ (7): calling `baz'\n"
1331 " └──> `baz': event 8 (depth 2)\n"
1333 " │ (8): entering `baz'\n"
1335 pp_formatted_text (dc
.printer
));
1339 /* Verify that print_path_summary is sane in the face of a recursive
1343 test_recursion (pretty_printer
*event_pp
)
1345 tree fntype_void_void
1346 = build_function_type_array (void_type_node
, 0, NULL
);
1347 tree fndecl_factorial
= build_fn_decl ("factorial", fntype_void_void
);
1349 test_diagnostic_path
path (event_pp
);
1350 path
.add_entry (fndecl_factorial
, 0);
1351 for (int depth
= 0; depth
< 3; depth
++)
1352 path
.add_call (fndecl_factorial
, depth
, fndecl_factorial
);
1353 ASSERT_EQ (path
.num_events (), 7);
1355 ASSERT_TRUE (path
.interprocedural_p ());
1357 path_summary
summary (path
, false);
1358 ASSERT_EQ (summary
.get_num_ranges (), 4);
1361 test_diagnostic_context dc
;
1362 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1363 print_path_summary_as_text (&summary
, &dc
, true);
1365 (" `factorial': events 1-2 (depth 0)\n"
1367 " | (1): entering `factorial'\n"
1368 " | (2): calling `factorial'\n"
1370 " +--> `factorial': events 3-4 (depth 1)\n"
1372 " | (3): entering `factorial'\n"
1373 " | (4): calling `factorial'\n"
1375 " +--> `factorial': events 5-6 (depth 2)\n"
1377 " | (5): entering `factorial'\n"
1378 " | (6): calling `factorial'\n"
1380 " +--> `factorial': event 7 (depth 3)\n"
1382 " | (7): entering `factorial'\n"
1384 pp_formatted_text (dc
.printer
));
1387 test_diagnostic_context dc
;
1388 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE
);
1389 print_path_summary_as_text (&summary
, &dc
, true);
1391 (" `factorial': events 1-2 (depth 0)\n"
1393 " │ (1): entering `factorial'\n"
1394 " │ (2): calling `factorial'\n"
1396 " └──> `factorial': events 3-4 (depth 1)\n"
1398 " │ (3): entering `factorial'\n"
1399 " │ (4): calling `factorial'\n"
1401 " └──> `factorial': events 5-6 (depth 2)\n"
1403 " │ (5): entering `factorial'\n"
1404 " │ (6): calling `factorial'\n"
1406 " └──> `factorial': event 7 (depth 3)\n"
1408 " │ (7): entering `factorial'\n"
1410 pp_formatted_text (dc
.printer
));
1414 /* Helper class for writing tests of control flow visualization. */
1416 class control_flow_test
1419 control_flow_test (const location
&loc
,
1420 const line_table_case
&case_
,
1421 const char *content
)
1422 : m_tmp_file (loc
, ".c", content
,
1423 /* gcc_rich_location::add_location_if_nearby implicitly
1424 uses global_dc's file_cache, so we need to evict
1425 tmp when we're done. */
1426 &global_dc
->get_file_cache ()),
1430 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
1431 m_tmp_file
.get_filename (), 0));
1432 linemap_line_start (line_table
, 1, 100);
1435 location_t
get_line_and_column (int line
, int column
)
1437 return linemap_position_for_line_and_column (line_table
, m_ord_map
,
1441 location_t
get_line_and_columns (int line
, int first_column
, int last_column
)
1443 return get_line_and_columns (line
,
1444 first_column
, first_column
, last_column
);
1447 location_t
get_line_and_columns (int line
,
1452 return make_location (get_line_and_column (line
, caret_column
),
1453 get_line_and_column (line
, first_column
),
1454 get_line_and_column (line
, last_column
));
1458 temp_source_file m_tmp_file
;
1459 line_table_test m_ltt
;
1460 const line_map_ordinary
*m_ord_map
;
1463 /* Example of event edges where all events can go in the same layout,
1464 testing the 6 combinations of:
1465 - ASCII vs Unicode vs event links off
1466 - line numbering on and off. */
1469 test_control_flow_1 (const line_table_case
&case_
,
1470 pretty_printer
*event_pp
)
1472 /* Create a tempfile and write some text to it.
1473 ...000000000111111111122222222223333333333.
1474 ...123456789012345678901234567890123456789. */
1476 = ("int test (int *p)\n" /* line 1. */
1478 " if (p)\n" /* line 3. */
1479 " return 0;\n" /* line 4. */
1480 " return *p;\n" /* line 5. */
1481 "}\n"); /* line 6. */
1483 control_flow_test
t (SELFTEST_LOCATION
, case_
, content
);
1485 const location_t conditional
= t
.get_line_and_column (3, 7);
1486 const location_t cfg_dest
= t
.get_line_and_column (5, 10);
1488 test_diagnostic_path
path (event_pp
);
1489 path
.add_event (conditional
, NULL_TREE
, 0,
1490 "following %qs branch (when %qs is NULL)...",
1492 path
.connect_to_next_event ();
1494 path
.add_event (cfg_dest
, NULL_TREE
, 0,
1496 path
.add_event (cfg_dest
, NULL_TREE
, 0,
1497 "dereference of NULL %qs",
1500 if (!path_events_have_column_data_p (path
))
1503 path_summary
summary (path
, true /*false*/);
1506 test_diagnostic_context dc
;
1507 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1508 dc
.m_source_printing
.show_event_links_p
= true;
1509 print_path_summary_as_text (&summary
, &dc
, false);
1516 " (1) following `false' branch (when `p' is NULL)... ->-+\n"
1520 "+------------------------------------------------------------+\n"
1524 "+-------->(2) ...to here\n"
1525 " (3) dereference of NULL `p'\n",
1526 pp_formatted_text (dc
.printer
));
1529 test_diagnostic_context dc
;
1530 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1531 dc
.m_source_printing
.show_event_links_p
= false;
1532 print_path_summary_as_text (&summary
, &dc
, false);
1539 " (1) following `false' branch (when `p' is NULL)...\n"
1545 " (3) dereference of NULL `p'\n",
1546 pp_formatted_text (dc
.printer
));
1549 test_diagnostic_context dc
;
1550 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1551 dc
.m_source_printing
.show_line_numbers_p
= true;
1552 dc
.m_source_printing
.show_event_links_p
= true;
1553 print_path_summary_as_text (&summary
, &dc
, false);
1560 " | (1) following `false' branch (when `p' is NULL)... ->-+\n"
1563 " |+------------------------------------------------------------+\n"
1565 " 5 || return *p;\n"
1568 " |+-------->(2) ...to here\n"
1569 " | (3) dereference of NULL `p'\n",
1570 pp_formatted_text (dc
.printer
));
1573 test_diagnostic_context dc
;
1574 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1575 dc
.m_source_printing
.show_line_numbers_p
= true;
1576 dc
.m_source_printing
.show_event_links_p
= false;
1577 print_path_summary_as_text (&summary
, &dc
, false);
1584 " | (1) following `false' branch (when `p' is NULL)...\n"
1589 " | (2) ...to here\n"
1590 " | (3) dereference of NULL `p'\n",
1591 pp_formatted_text (dc
.printer
));
1594 test_diagnostic_context dc
;
1595 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE
);
1596 dc
.m_source_printing
.show_event_links_p
= true;
1597 print_path_summary_as_text (&summary
, &dc
, false);
1604 " (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
1608 "┌────────────────────────────────────────────────────────────┘\n"
1612 "└────────>(2) ...to here\n"
1613 " (3) dereference of NULL `p'\n",
1614 pp_formatted_text (dc
.printer
));
1617 test_diagnostic_context dc
;
1618 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE
);
1619 dc
.m_source_printing
.show_event_links_p
= true;
1620 dc
.m_source_printing
.show_line_numbers_p
= true;
1621 print_path_summary_as_text (&summary
, &dc
, false);
1628 " | (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
1631 " |┌────────────────────────────────────────────────────────────┘\n"
1633 " 5 |│ return *p;\n"
1636 " |└────────>(2) ...to here\n"
1637 " | (3) dereference of NULL `p'\n",
1638 pp_formatted_text (dc
.printer
));
1642 /* Complex example involving a backedge. */
1645 test_control_flow_2 (const line_table_case
&case_
,
1646 pretty_printer
*event_pp
)
1648 /* Create a tempfile and write some text to it.
1649 ...000000000111111111122222222223333333333.
1650 ...123456789012345678901234567890123456789. */
1652 = ("int for_loop_noop_next (struct node *n)\n" /* <--------- line 1. */
1653 "{\n" /* <----------------------------------------------- line 2. */
1654 " int sum = 0;\n" /* <---------------------------------- line 3. */
1655 " for (struct node *iter = n; iter; iter->next)\n" /* <- line 4. */
1656 " sum += n->val;\n" /* <------------------------------ line 5. */
1657 " return sum;\n" /* <----------------------------------- line 6. */
1658 "}\n"); /* <-------------------------------------------- line 7. */
1659 /* Adapted from infinite-loop-linked-list.c where
1660 "iter->next" should be "iter = iter->next". */
1662 control_flow_test
t (SELFTEST_LOCATION
, case_
, content
);
1664 const location_t iter_test
= t
.get_line_and_columns (4, 31, 34);
1665 const location_t loop_body_start
= t
.get_line_and_columns (5, 12, 17);
1666 const location_t loop_body_end
= t
.get_line_and_columns (5, 5, 9, 17);
1668 test_diagnostic_path
path (event_pp
);
1669 path
.add_event (iter_test
, NULL_TREE
, 0, "infinite loop here");
1671 path
.add_event (iter_test
, NULL_TREE
, 0, "looping from here...");
1672 path
.connect_to_next_event ();
1674 path
.add_event (loop_body_start
, NULL_TREE
, 0, "...to here");
1676 path
.add_event (loop_body_end
, NULL_TREE
, 0, "looping back...");
1677 path
.connect_to_next_event ();
1679 path
.add_event (iter_test
, NULL_TREE
, 0, "...to here");
1681 if (!path_events_have_column_data_p (path
))
1684 path_summary
summary (path
, true);
1687 test_diagnostic_context dc
;
1688 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1689 dc
.m_source_printing
.show_event_links_p
= true;
1690 dc
.m_source_printing
.show_line_numbers_p
= true;
1691 print_path_summary_as_text (&summary
, &dc
, false);
1695 " 4 | for (struct node *iter = n; iter; iter->next)\n"
1698 " | (1) infinite loop here\n"
1699 " | (2) looping from here... ->-+\n"
1702 " |+----------------------------------------------------------+\n"
1703 " 5 || sum += n->val;\n"
1706 " |+---------->(3) ...to here\n"
1707 /* We need to start an new event_range here as event (4) is to the
1708 left of event (3), and thus (4) would mess up the in-edge to (3). */
1710 " 5 | sum += n->val;\n"
1711 " | ~~~~^~~~~~~~~\n"
1713 " | (4) looping back... ->-+\n"
1715 /* We need to start an new event_range here as event (4) with an
1716 out-edge is on a later line (line 5) than its destination event (5),
1720 " |+-------------------------------+\n"
1721 " 4 || for (struct node *iter = n; iter; iter->next)\n"
1724 " |+----------------------------->(5) ...to here\n",
1725 pp_formatted_text (dc
.printer
));
1729 /* Complex example involving a backedge and both an in-edge and out-edge
1730 on the same line. */
1733 test_control_flow_3 (const line_table_case
&case_
,
1734 pretty_printer
*event_pp
)
1736 /* Create a tempfile and write some text to it.
1737 ...000000000111111111122222222223333333333.
1738 ...123456789012345678901234567890123456789. */
1740 = ("void test_missing_comparison_in_for_condition_1 (int n)\n"
1741 "{\n" /* <------------------------- line 2. */
1742 " for (int i = 0; n; i++)\n" /* <- line 3. */
1743 " {\n" /* <--------------------- line 4. */
1744 " }\n" /* <--------------------- line 5. */
1745 "}\n"); /* <----------------------- line 6. */
1746 /* Adapted from infinite-loop-1.c where the condition should have been
1747 "i < n", rather than just "n". */
1749 control_flow_test
t (SELFTEST_LOCATION
, case_
, content
);
1751 const location_t iter_test
= t
.get_line_and_column (3, 19);
1752 const location_t iter_next
= t
.get_line_and_columns (3, 22, 24);
1754 test_diagnostic_path
path (event_pp
);
1755 path
.add_event (iter_test
, NULL_TREE
, 0, "infinite loop here");
1757 path
.add_event (iter_test
, NULL_TREE
, 0, "looping from here...");
1758 path
.connect_to_next_event ();
1760 path
.add_event (iter_next
, NULL_TREE
, 0, "...to here");
1762 path
.add_event (iter_next
, NULL_TREE
, 0, "looping back...");
1763 path
.connect_to_next_event ();
1765 path
.add_event (iter_test
, NULL_TREE
, 0, "...to here");
1767 if (!path_events_have_column_data_p (path
))
1770 path_summary
summary (path
, true);
1773 test_diagnostic_context dc
;
1774 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1775 dc
.m_source_printing
.show_event_links_p
= true;
1776 dc
.m_source_printing
.show_line_numbers_p
= true;
1777 print_path_summary_as_text (&summary
, &dc
, false);
1781 " 3 | for (int i = 0; n; i++)\n"
1784 " | (1) infinite loop here\n"
1785 " | (2) looping from here... ->-+\n"
1789 " |+----------------------------------------------+\n"
1790 " 3 || for (int i = 0; n; i++)\n"
1793 " |+-------------------->(3) ...to here\n"
1794 " | (4) looping back... ->-+\n"
1796 /* We need to start an new event_range here as event (4) with an
1797 out-edge is on the same line as its destination event (5), but
1798 to the right, which we can't handle as a single event_range. */
1801 " |+--------------------------------------------+\n"
1802 " 3 || for (int i = 0; n; i++)\n"
1805 " |+----------------->(5) ...to here\n",
1806 pp_formatted_text (dc
.printer
));
1810 /* Implementation of ASSERT_CFG_EDGE_PATH_STREQ. */
1813 assert_cfg_edge_path_streq (const location
&loc
,
1814 pretty_printer
*event_pp
,
1815 const location_t src_loc
,
1816 const location_t dst_loc
,
1817 const char *expected_str
)
1819 test_diagnostic_path
path (event_pp
);
1820 path
.add_event (src_loc
, NULL_TREE
, 0, "from here...");
1821 path
.connect_to_next_event ();
1823 path
.add_event (dst_loc
, NULL_TREE
, 0, "...to here");
1825 if (!path_events_have_column_data_p (path
))
1828 path_summary
summary (path
, true);
1830 test_diagnostic_context dc
;
1831 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
1832 dc
.m_source_printing
.show_event_links_p
= true;
1833 dc
.m_source_printing
.show_line_numbers_p
= true;
1834 print_path_summary_as_text (&summary
, &dc
, false);
1835 ASSERT_STREQ_AT (loc
, expected_str
,
1836 pp_formatted_text (dc
.printer
));
1839 /* Assert that if we make a path with an event with "from here..." at SRC_LOC
1840 leading to an event "...to here" at DST_LOC that we print the path
1843 #define ASSERT_CFG_EDGE_PATH_STREQ(SRC_LOC, DST_LOC, EXPECTED_STR) \
1844 assert_cfg_edge_path_streq ((SELFTEST_LOCATION), (event_pp), \
1845 (SRC_LOC), (DST_LOC), (EXPECTED_STR))
1847 /* Various examples of edge, trying to cover all combinations of:
1848 - relative x positive of src label and dst label
1849 - relative y position of labels:
1852 - on line after next
1853 - big gap, where src is before dst
1854 - big gap, where src is after dst
1855 and other awkward cases. */
1858 test_control_flow_4 (const line_table_case
&case_
,
1859 pretty_printer
*event_pp
)
1861 std::string many_lines
;
1862 for (int i
= 1; i
<= 100; i
++)
1863 /* ............000000000111
1864 ............123456789012. */
1865 many_lines
+= "LHS RHS\n";
1866 control_flow_test
t (SELFTEST_LOCATION
, case_
, many_lines
.c_str ());
1871 ASSERT_CFG_EDGE_PATH_STREQ
1872 (t
.get_line_and_columns (3, 1, 3),
1873 t
.get_line_and_columns (3, 10, 12),
1879 " | (1) from here... ->-+\n"
1883 " |+--------------------+\n"
1887 " |+-------->(2) ...to here\n"));
1890 ASSERT_CFG_EDGE_PATH_STREQ
1891 (t
.get_line_and_columns (3, 10, 12),
1892 t
.get_line_and_columns (3, 1, 3),
1898 " | (1) from here... ->-+\n"
1902 " |+-----------------------------+\n"
1906 " |+(2) ...to here\n"));
1912 ASSERT_CFG_EDGE_PATH_STREQ
1913 (t
.get_line_and_columns (3, 1, 3),
1914 t
.get_line_and_columns (4, 5, 7),
1920 " | (1) from here... ->-+\n"
1923 " |+--------------------+\n"
1927 " |+--->(2) ...to here\n"));
1930 ASSERT_CFG_EDGE_PATH_STREQ
1931 (t
.get_line_and_columns (3, 10, 12),
1932 t
.get_line_and_columns (4, 1, 3),
1938 " | (1) from here... ->-+\n"
1941 " |+-----------------------------+\n"
1945 " |+(2) ...to here\n"));
1948 /* Line after next. */
1951 ASSERT_CFG_EDGE_PATH_STREQ
1952 (t
.get_line_and_columns (3, 1, 3),
1953 t
.get_line_and_columns (5, 10, 12),
1959 " | (1) from here... ->-+\n"
1962 " |+--------------------+\n"
1967 " |+-------->(2) ...to here\n"));
1970 ASSERT_CFG_EDGE_PATH_STREQ
1971 (t
.get_line_and_columns (3, 10, 12),
1972 t
.get_line_and_columns (5, 1, 3),
1978 " | (1) from here... ->-+\n"
1981 " |+-----------------------------+\n"
1986 " |+(2) ...to here\n"));
1989 /* Big gap, increasing line number. */
1992 ASSERT_CFG_EDGE_PATH_STREQ
1993 (t
.get_line_and_columns (3, 1, 3),
1994 t
.get_line_and_columns (97, 10, 12),
2000 " | (1) from here... ->-+\n"
2004 " |+--------------------+\n"
2008 " |+-------->(2) ...to here\n"));
2011 ASSERT_CFG_EDGE_PATH_STREQ
2012 (t
.get_line_and_columns (3, 10, 12),
2013 t
.get_line_and_columns (97, 1, 3),
2019 " | (1) from here... ->-+\n"
2023 " |+-----------------------------+\n"
2027 " |+(2) ...to here\n"));
2030 /* Big gap, decreasing line number. */
2033 ASSERT_CFG_EDGE_PATH_STREQ
2034 (t
.get_line_and_columns (97, 1, 3),
2035 t
.get_line_and_columns (3, 10, 12),
2041 " | (1) from here... ->-+\n"
2045 " |+--------------------+\n"
2049 " |+-------->(2) ...to here\n"));
2052 ASSERT_CFG_EDGE_PATH_STREQ
2053 (t
.get_line_and_columns (97, 10, 12),
2054 t
.get_line_and_columns (3, 1, 3),
2060 " | (1) from here... ->-+\n"
2064 " |+-----------------------------+\n"
2068 " |+(2) ...to here\n"));
2073 ASSERT_CFG_EDGE_PATH_STREQ
2075 t
.get_line_and_columns (3, 10, 12),
2077 " (1): from here...\n"
2083 " |+-------->(2) ...to here\n"));
2088 ASSERT_CFG_EDGE_PATH_STREQ
2089 (t
.get_line_and_columns (3, 1, 3),
2096 " | (1) from here... ->-+\n"
2100 " (2): ...to here\n"));
2104 /* Another complex example, adapted from data-model-20.c. */
2107 test_control_flow_5 (const line_table_case
&case_
,
2108 pretty_printer
*event_pp
)
2110 /* Create a tempfile and write some text to it.
2111 ...000000000111111111122222222223333333333444444444455555555556666666666.
2112 ...123456789012345678901234567890123456789012345678901234567890123456789. */
2114 = (" if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
2115 " return NULL;\n" /* <------------------------- line 2. */
2116 "\n" /* <----------------------------------------- line 3. */
2117 " for (i = 0; i < n; i++) {\n" /* <-------------- line 4. */
2118 " if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n");
2120 control_flow_test
t (SELFTEST_LOCATION
, case_
, content
);
2122 test_diagnostic_path
path (event_pp
);
2124 path
.add_event (t
.get_line_and_column (1, 6), NULL_TREE
, 0,
2125 "following %qs branch (when %qs is non-NULL)...",
2127 path
.connect_to_next_event ();
2130 path
.add_event (t
.get_line_and_columns (4, 8, 10, 12), NULL_TREE
, 0,
2134 path
.add_event (t
.get_line_and_columns (4, 15, 17, 19), NULL_TREE
, 0,
2135 "following %qs branch (when %qs)...",
2137 path
.connect_to_next_event ();
2140 path
.add_event (t
.get_line_and_column (5, 13), NULL_TREE
, 0,
2144 path
.add_event (t
.get_line_and_columns (5, 33, 58), NULL_TREE
, 0,
2147 if (!path_events_have_column_data_p (path
))
2150 path_summary
summary (path
, true);
2153 test_diagnostic_context dc
;
2154 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
2155 dc
.m_source_printing
.show_event_links_p
= true;
2156 dc
.m_source_printing
.show_line_numbers_p
= true;
2157 print_path_summary_as_text (&summary
, &dc
, false);
2161 " 1 | if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
2164 " | (1) following `false' branch (when `arr' is non-NULL)... ->-+\n"
2168 " |+-----------------------------------------------------------------+\n"
2169 " 4 || for (i = 0; i < n; i++) {\n"
2172 " || | (3) following `true' branch (when `i < n')... ->-+\n"
2173 " |+-------->(2) ...to here |\n"
2176 " |+-----------------------------------------------------------------+\n"
2177 " 5 || if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n"
2178 " || ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
2180 " |+----------->(4) ...to here (5) allocated here\n",
2181 pp_formatted_text (dc
.printer
));
2185 /* Another complex example, adapted from loop-3.c. */
2188 test_control_flow_6 (const line_table_case
&case_
,
2189 pretty_printer
*event_pp
)
2191 /* Create a tempfile and write some text to it.
2192 ...000000000111111111122222222223333333.
2193 ...123456789012345678901234567890123456. */
2195 = ("#include <stdlib.h>\n" /* <------------------ line 1. */
2196 "\n" /* <------------------------------------- line 2. */
2197 "void test(int c)\n" /* <--------------------- line 3. */
2198 "{\n" /* <------------------------------------ line 4. */
2199 " int i;\n" /* <----------------------------- line 5. */
2200 " char *buffer = (char*)malloc(256);\n" /* <- line 6. */
2201 "\n" /* <------------------------------------- line 7. */
2202 " for (i=0; i<255; i++) {\n" /* <------------ line 8. */
2203 " buffer[i] = c;\n" /* <------------------- line 9. */
2204 "\n" /* <------------------------------------- line 10. */
2205 " free(buffer);\n" /* <-------------------- line 11. */
2206 " }\n"); /* <-------------------------------- line 12. */
2208 control_flow_test
t (SELFTEST_LOCATION
, case_
, content
);
2210 test_diagnostic_path
path (event_pp
);
2212 path
.add_event (t
.get_line_and_columns (6, 25, 35), NULL_TREE
, 0,
2216 path
.add_event (t
.get_line_and_columns (8, 13, 14, 17), NULL_TREE
, 0,
2217 "following %qs branch (when %qs)...",
2218 "true", "i <= 254");
2219 path
.connect_to_next_event ();
2222 path
.add_event (t
.get_line_and_columns (9, 5, 15, 17), NULL_TREE
, 0,
2226 path
.add_event (t
.get_line_and_columns (8, 13, 14, 17), NULL_TREE
, 0,
2227 "following %qs branch (when %qs)...",
2228 "true", "i <= 254");
2229 path
.connect_to_next_event ();
2232 path
.add_event (t
.get_line_and_columns (9, 5, 15, 17), NULL_TREE
, 0,
2235 if (!path_events_have_column_data_p (path
))
2238 path_summary
summary (path
, true);
2241 test_diagnostic_context dc
;
2242 dc
.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII
);
2243 dc
.m_source_printing
.show_event_links_p
= true;
2244 dc
.m_source_printing
.show_line_numbers_p
= true;
2245 print_path_summary_as_text (&summary
, &dc
, false);
2249 " 6 | char *buffer = (char*)malloc(256);\n"
2252 " | (1) allocated here\n"
2254 " 8 | for (i=0; i<255; i++) {\n"
2257 " | (2) following `true' branch (when `i <= 254')... ->-+\n"
2260 " |+-----------------------------------------------------------------+\n"
2261 " 9 || buffer[i] = c;\n"
2262 " || ~~~~~~~~~~~~~ \n"
2264 " |+------------->(3) ...to here\n"
2266 " 8 | for (i=0; i<255; i++) {\n"
2269 " | (4) following `true' branch (when `i <= 254')... ->-+\n"
2272 " |+-----------------------------------------------------------------+\n"
2273 " 9 || buffer[i] = c;\n"
2274 " || ~~~~~~~~~~~~~\n"
2276 " |+------------->(5) ...to here\n",
2277 pp_formatted_text (dc
.printer
));
2282 control_flow_tests (const line_table_case
&case_
)
2284 std::unique_ptr
<pretty_printer
> event_pp
2285 = std::unique_ptr
<pretty_printer
> (global_dc
->printer
->clone ());
2286 pp_show_color (event_pp
) = 0;
2288 test_control_flow_1 (case_
, event_pp
.get ());
2289 test_control_flow_2 (case_
, event_pp
.get ());
2290 test_control_flow_3 (case_
, event_pp
.get ());
2291 test_control_flow_4 (case_
, event_pp
.get ());
2292 test_control_flow_5 (case_
, event_pp
.get ());
2293 test_control_flow_6 (case_
, event_pp
.get ());
2296 /* Run all of the selftests within this file. */
2299 tree_diagnostic_path_cc_tests ()
2301 /* In a few places we use the global dc's printer to determine
2302 colorization so ensure this off during the tests. */
2303 bool saved_show_color
= pp_show_color (global_dc
->printer
);
2304 pp_show_color (global_dc
->printer
) = false;
2306 auto_fix_quotes fix_quotes
;
2307 std::unique_ptr
<pretty_printer
> event_pp
2308 = std::unique_ptr
<pretty_printer
> (global_dc
->printer
->clone ());
2309 test_empty_path (event_pp
.get ());
2310 test_intraprocedural_path (event_pp
.get ());
2311 test_interprocedural_path_1 (event_pp
.get ());
2312 test_interprocedural_path_2 (event_pp
.get ());
2313 test_recursion (event_pp
.get ());
2314 for_each_line_table_case (control_flow_tests
);
2316 pp_show_color (global_dc
->printer
) = saved_show_color
;
2319 } // namespace selftest
2322 # pragma GCC diagnostic pop
2325 #endif /* #if CHECKING_P */