]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/tree-diagnostic-path.cc
Fix profiledbootstrap
[thirdparty/gcc.git] / gcc / tree-diagnostic-path.cc
1 /* Paths through the code associated with a diagnostic.
2 Copyright (C) 2019-2023 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>
4
5 This file is part of GCC.
6
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
10 version.
11
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
15 for more details.
16
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/>. */
20
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tree.h"
25 #include "diagnostic.h"
26 #include "tree-pretty-print.h"
27 #include "gimple-pretty-print.h"
28 #include "tree-diagnostic.h"
29 #include "langhooks.h"
30 #include "intl.h"
31 #include "diagnostic-path.h"
32 #include "json.h"
33 #include "gcc-rich-location.h"
34 #include "diagnostic-color.h"
35 #include "diagnostic-event-id.h"
36 #include "selftest.h"
37 #include "selftest-diagnostic.h"
38
39 /* Anonymous namespace for path-printing code. */
40
41 namespace {
42
43 /* Subclass of range_label for showing a particular event
44 when showing a consecutive run of events within a diagnostic_path as
45 labelled ranges within one gcc_rich_location. */
46
47 class path_label : public range_label
48 {
49 public:
50 path_label (const diagnostic_path *path, unsigned start_idx)
51 : m_path (path), m_start_idx (start_idx)
52 {}
53
54 label_text get_text (unsigned range_idx) const final override
55 {
56 unsigned event_idx = m_start_idx + range_idx;
57 const diagnostic_event &event = m_path->get_event (event_idx);
58
59 /* Get the description of the event, perhaps with colorization:
60 normally, we don't colorize within a range_label, but this
61 is special-cased for diagnostic paths. */
62 bool colorize = pp_show_color (global_dc->printer);
63 label_text event_text (event.get_desc (colorize));
64 gcc_assert (event_text.get ());
65 pretty_printer pp;
66 pp_show_color (&pp) = pp_show_color (global_dc->printer);
67 diagnostic_event_id_t event_id (event_idx);
68 pp_printf (&pp, "%@ %s", &event_id, event_text.get ());
69 label_text result = label_text::take (xstrdup (pp_formatted_text (&pp)));
70 return result;
71 }
72
73 private:
74 const diagnostic_path *m_path;
75 unsigned m_start_idx;
76 };
77
78 /* Return true if E1 and E2 can be consolidated into the same run of events
79 when printing a diagnostic_path. */
80
81 static bool
82 can_consolidate_events (const diagnostic_event &e1,
83 const diagnostic_event &e2,
84 bool check_locations)
85 {
86 if (e1.get_fndecl () != e2.get_fndecl ())
87 return false;
88
89 if (e1.get_stack_depth () != e2.get_stack_depth ())
90 return false;
91
92 if (check_locations)
93 {
94 location_t loc1 = e1.get_location ();
95 location_t loc2 = e2.get_location ();
96
97 if (loc1 < RESERVED_LOCATION_COUNT
98 || loc2 < RESERVED_LOCATION_COUNT)
99 return false;
100
101 /* Neither can be macro-based. */
102 if (linemap_location_from_macro_expansion_p (line_table, loc1))
103 return false;
104 if (linemap_location_from_macro_expansion_p (line_table, loc2))
105 return false;
106 }
107
108 /* Passed all the tests. */
109 return true;
110 }
111
112 /* A range of consecutive events within a diagnostic_path,
113 all with the same fndecl and stack_depth, and which are suitable
114 to print with a single call to diagnostic_show_locus. */
115 struct event_range
116 {
117 event_range (const diagnostic_path *path, unsigned start_idx,
118 const diagnostic_event &initial_event)
119 : m_path (path),
120 m_initial_event (initial_event),
121 m_fndecl (initial_event.get_fndecl ()),
122 m_stack_depth (initial_event.get_stack_depth ()),
123 m_start_idx (start_idx), m_end_idx (start_idx),
124 m_path_label (path, start_idx),
125 m_richloc (initial_event.get_location (), &m_path_label)
126 {}
127
128 bool maybe_add_event (const diagnostic_event &new_ev, unsigned idx,
129 bool check_rich_locations)
130 {
131 if (!can_consolidate_events (m_initial_event, new_ev,
132 check_rich_locations))
133 return false;
134 if (check_rich_locations)
135 if (!m_richloc.add_location_if_nearby (new_ev.get_location (),
136 false, &m_path_label))
137 return false;
138 m_end_idx = idx;
139 return true;
140 }
141
142 /* Print the events in this range to DC, typically as a single
143 call to the printer's diagnostic_show_locus. */
144
145 void print (diagnostic_context *dc)
146 {
147 location_t initial_loc = m_initial_event.get_location ();
148
149 /* Emit a span indicating the filename (and line/column) if the
150 line has changed relative to the last call to
151 diagnostic_show_locus. */
152 if (dc->show_caret)
153 {
154 expanded_location exploc
155 = linemap_client_expand_location_to_spelling_point
156 (initial_loc, LOCATION_ASPECT_CARET);
157 if (exploc.file != LOCATION_FILE (dc->last_location))
158 dc->start_span (dc, exploc);
159 }
160
161 /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
162 primary location for an event, diagnostic_show_locus won't print
163 anything.
164
165 In particular the label for the event won't get printed.
166 Fail more gracefully in this case by showing the event
167 index and text, at no particular location. */
168 if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
169 {
170 for (unsigned i = m_start_idx; i <= m_end_idx; i++)
171 {
172 const diagnostic_event &iter_event = m_path->get_event (i);
173 diagnostic_event_id_t event_id (i);
174 label_text event_text (iter_event.get_desc (true));
175 pretty_printer *pp = dc->printer;
176 pp_printf (pp, " %@: %s", &event_id, event_text.get ());
177 pp_newline (pp);
178 }
179 return;
180 }
181
182 /* Call diagnostic_show_locus to show the events using labels. */
183 diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH);
184
185 /* If we have a macro expansion, show the expansion to the user. */
186 if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
187 {
188 gcc_assert (m_start_idx == m_end_idx);
189 maybe_unwind_expanded_macro_loc (dc, initial_loc);
190 }
191 }
192
193 const diagnostic_path *m_path;
194 const diagnostic_event &m_initial_event;
195 tree m_fndecl;
196 int m_stack_depth;
197 unsigned m_start_idx;
198 unsigned m_end_idx;
199 path_label m_path_label;
200 gcc_rich_location m_richloc;
201 };
202
203 /* A struct for grouping together the events in a diagnostic_path into
204 ranges of events, partitioned by stack frame (i.e. by fndecl and
205 stack depth). */
206
207 struct path_summary
208 {
209 path_summary (const diagnostic_path &path, bool check_rich_locations);
210
211 unsigned get_num_ranges () const { return m_ranges.length (); }
212
213 auto_delete_vec <event_range> m_ranges;
214 };
215
216 /* path_summary's ctor. */
217
218 path_summary::path_summary (const diagnostic_path &path,
219 bool check_rich_locations)
220 {
221 const unsigned num_events = path.num_events ();
222
223 event_range *cur_event_range = NULL;
224 for (unsigned idx = 0; idx < num_events; idx++)
225 {
226 const diagnostic_event &event = path.get_event (idx);
227 if (cur_event_range)
228 if (cur_event_range->maybe_add_event (event, idx, check_rich_locations))
229 continue;
230
231 cur_event_range = new event_range (&path, idx, event);
232 m_ranges.safe_push (cur_event_range);
233 }
234 }
235
236 /* Write SPACES to PP. */
237
238 static void
239 write_indent (pretty_printer *pp, int spaces)
240 {
241 for (int i = 0; i < spaces; i++)
242 pp_space (pp);
243 }
244
245 /* Print FNDDECL to PP, quoting it if QUOTED is true.
246
247 We can't use "%qE" here since we can't guarantee the capabilities
248 of PP. */
249
250 static void
251 print_fndecl (pretty_printer *pp, tree fndecl, bool quoted)
252 {
253 const char *n = DECL_NAME (fndecl)
254 ? identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2))
255 : _("<anonymous>");
256 if (quoted)
257 pp_printf (pp, "%qs", n);
258 else
259 pp_string (pp, n);
260 }
261
262 /* Print path_summary PS to DC, giving an overview of the interprocedural
263 calls and returns.
264
265 Print the event descriptions in a nested form, printing the event
266 descriptions within calls to diagnostic_show_locus, using labels to
267 show the events:
268
269 'foo' (events 1-2)
270 | NN |
271 | |
272 +--> 'bar' (events 3-4)
273 | NN |
274 | |
275 +--> 'baz' (events 5-6)
276 | NN |
277 | |
278 <------------ +
279 |
280 'foo' (events 7-8)
281 | NN |
282 | |
283 +--> 'bar' (events 9-10)
284 | NN |
285 | |
286 +--> 'baz' (events 11-12)
287 | NN |
288 | |
289
290 If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
291 of events.
292
293 For events with UNKNOWN_LOCATION, print a summary of each the event. */
294
295 void
296 print_path_summary_as_text (const path_summary *ps, diagnostic_context *dc,
297 bool show_depths)
298 {
299 pretty_printer *pp = dc->printer;
300
301 const int per_frame_indent = 2;
302
303 const char *const line_color = "path";
304 const char *start_line_color
305 = colorize_start (pp_show_color (pp), line_color);
306 const char *end_line_color = colorize_stop (pp_show_color (pp));
307
308 /* Keep track of column numbers of existing '|' characters for
309 stack depths we've already printed. */
310 const int EMPTY = -1;
311 const int DELETED = -2;
312 typedef int_hash <int, EMPTY, DELETED> vbar_hash;
313 hash_map <vbar_hash, int> vbar_column_for_depth;
314
315 /* Print the ranges. */
316 const int base_indent = 2;
317 int cur_indent = base_indent;
318 unsigned i;
319 event_range *range;
320 FOR_EACH_VEC_ELT (ps->m_ranges, i, range)
321 {
322 write_indent (pp, cur_indent);
323 if (i > 0)
324 {
325 const event_range *prev_range = ps->m_ranges[i - 1];
326 if (range->m_stack_depth > prev_range->m_stack_depth)
327 {
328 /* Show pushed stack frame(s). */
329 const char *push_prefix = "+--> ";
330 pp_string (pp, start_line_color);
331 pp_string (pp, push_prefix);
332 pp_string (pp, end_line_color);
333 cur_indent += strlen (push_prefix);
334 }
335 }
336 if (range->m_fndecl)
337 {
338 print_fndecl (pp, range->m_fndecl, true);
339 pp_string (pp, ": ");
340 }
341 if (range->m_start_idx == range->m_end_idx)
342 pp_printf (pp, "event %i",
343 range->m_start_idx + 1);
344 else
345 pp_printf (pp, "events %i-%i",
346 range->m_start_idx + 1, range->m_end_idx + 1);
347 if (show_depths)
348 pp_printf (pp, " (depth %i)", range->m_stack_depth);
349 pp_newline (pp);
350
351 /* Print a run of events. */
352 {
353 write_indent (pp, cur_indent + per_frame_indent);
354 pp_string (pp, start_line_color);
355 pp_string (pp, "|");
356 pp_string (pp, end_line_color);
357 pp_newline (pp);
358
359 char *saved_prefix = pp_take_prefix (pp);
360 char *prefix;
361 {
362 pretty_printer tmp_pp;
363 write_indent (&tmp_pp, cur_indent + per_frame_indent);
364 pp_string (&tmp_pp, start_line_color);
365 pp_string (&tmp_pp, "|");
366 pp_string (&tmp_pp, end_line_color);
367 prefix = xstrdup (pp_formatted_text (&tmp_pp));
368 }
369 pp_set_prefix (pp, prefix);
370 pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
371 range->print (dc);
372 pp_set_prefix (pp, saved_prefix);
373
374 write_indent (pp, cur_indent + per_frame_indent);
375 pp_string (pp, start_line_color);
376 pp_string (pp, "|");
377 pp_string (pp, end_line_color);
378 pp_newline (pp);
379 }
380
381 if (i < ps->m_ranges.length () - 1)
382 {
383 const event_range *next_range = ps->m_ranges[i + 1];
384
385 if (range->m_stack_depth > next_range->m_stack_depth)
386 {
387 if (vbar_column_for_depth.get (next_range->m_stack_depth))
388 {
389 /* Show returning from stack frame(s), by printing
390 something like:
391 " |\n"
392 " <------------ +\n"
393 " |\n". */
394 int vbar_for_next_frame
395 = *vbar_column_for_depth.get (next_range->m_stack_depth);
396
397 int indent_for_next_frame
398 = vbar_for_next_frame - per_frame_indent;
399 write_indent (pp, vbar_for_next_frame);
400 pp_string (pp, start_line_color);
401 pp_character (pp, '<');
402 for (int i = indent_for_next_frame + per_frame_indent;
403 i < cur_indent + per_frame_indent - 1; i++)
404 pp_character (pp, '-');
405 pp_character (pp, '+');
406 pp_string (pp, end_line_color);
407 pp_newline (pp);
408 cur_indent = indent_for_next_frame;
409
410 write_indent (pp, vbar_for_next_frame);
411 pp_string (pp, start_line_color);
412 pp_character (pp, '|');
413 pp_string (pp, end_line_color);
414 pp_newline (pp);
415 }
416 else
417 {
418 /* Handle disjoint paths (e.g. a callback at some later
419 time). */
420 cur_indent = base_indent;
421 }
422 }
423 else if (range->m_stack_depth < next_range->m_stack_depth)
424 {
425 /* Prepare to show pushed stack frame. */
426 gcc_assert (range->m_stack_depth != EMPTY);
427 gcc_assert (range->m_stack_depth != DELETED);
428 vbar_column_for_depth.put (range->m_stack_depth,
429 cur_indent + per_frame_indent);
430 cur_indent += per_frame_indent;
431 }
432
433 }
434 }
435 }
436
437 } /* end of anonymous namespace for path-printing code. */
438
439 /* Print PATH to CONTEXT, according to CONTEXT's path_format. */
440
441 void
442 default_tree_diagnostic_path_printer (diagnostic_context *context,
443 const diagnostic_path *path)
444 {
445 gcc_assert (path);
446
447 const unsigned num_events = path->num_events ();
448
449 switch (context->path_format)
450 {
451 case DPF_NONE:
452 /* Do nothing. */
453 return;
454
455 case DPF_SEPARATE_EVENTS:
456 {
457 /* A note per event. */
458 for (unsigned i = 0; i < num_events; i++)
459 {
460 const diagnostic_event &event = path->get_event (i);
461 label_text event_text (event.get_desc (false));
462 gcc_assert (event_text.get ());
463 diagnostic_event_id_t event_id (i);
464 if (context->show_path_depths)
465 {
466 int stack_depth = event.get_stack_depth ();
467 tree fndecl = event.get_fndecl ();
468 /* -fdiagnostics-path-format=separate-events doesn't print
469 fndecl information, so with -fdiagnostics-show-path-depths
470 print the fndecls too, if any. */
471 if (fndecl)
472 inform (event.get_location (),
473 "%@ %s (fndecl %qD, depth %i)",
474 &event_id, event_text.get (),
475 fndecl, stack_depth);
476 else
477 inform (event.get_location (),
478 "%@ %s (depth %i)",
479 &event_id, event_text.get (),
480 stack_depth);
481 }
482 else
483 inform (event.get_location (),
484 "%@ %s", &event_id, event_text.get ());
485 }
486 }
487 break;
488
489 case DPF_INLINE_EVENTS:
490 {
491 /* Consolidate related events. */
492 path_summary summary (*path, true);
493 char *saved_prefix = pp_take_prefix (context->printer);
494 pp_set_prefix (context->printer, NULL);
495 print_path_summary_as_text (&summary, context,
496 context->show_path_depths);
497 pp_flush (context->printer);
498 pp_set_prefix (context->printer, saved_prefix);
499 }
500 }
501 }
502
503 /* This has to be here, rather than diagnostic-format-json.cc,
504 since diagnostic-format-json.o is within OBJS-libcommon and thus
505 doesn't have access to trees (for m_fndecl). */
506
507 json::value *
508 default_tree_make_json_for_path (diagnostic_context *context,
509 const diagnostic_path *path)
510 {
511 json::array *path_array = new json::array ();
512 for (unsigned i = 0; i < path->num_events (); i++)
513 {
514 const diagnostic_event &event = path->get_event (i);
515
516 json::object *event_obj = new json::object ();
517 if (event.get_location ())
518 event_obj->set ("location",
519 json_from_expanded_location (context,
520 event.get_location ()));
521 label_text event_text (event.get_desc (false));
522 event_obj->set ("description", new json::string (event_text.get ()));
523 if (tree fndecl = event.get_fndecl ())
524 {
525 const char *function
526 = identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2));
527 event_obj->set ("function", new json::string (function));
528 }
529 event_obj->set ("depth",
530 new json::integer_number (event.get_stack_depth ()));
531 path_array->append (event_obj);
532 }
533 return path_array;
534 }
535
536 #if CHECKING_P
537
538 /* Disable warnings about missing quoting in GCC diagnostics for the print
539 calls in the tests below. */
540 #if __GNUC__ >= 10
541 # pragma GCC diagnostic push
542 # pragma GCC diagnostic ignored "-Wformat-diag"
543 #endif
544
545 namespace selftest {
546
547 /* A subclass of simple_diagnostic_path that adds member functions
548 for adding test events. */
549
550 class test_diagnostic_path : public simple_diagnostic_path
551 {
552 public:
553 test_diagnostic_path (pretty_printer *event_pp)
554 : simple_diagnostic_path (event_pp)
555 {
556 }
557
558 void add_entry (tree fndecl, int stack_depth)
559 {
560 add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
561 "entering %qE", fndecl);
562 }
563
564 void add_return (tree fndecl, int stack_depth)
565 {
566 add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
567 "returning to %qE", fndecl);
568 }
569
570 void add_call (tree caller, int caller_stack_depth, tree callee)
571 {
572 add_event (UNKNOWN_LOCATION, caller, caller_stack_depth,
573 "calling %qE", callee);
574 add_entry (callee, caller_stack_depth + 1);
575 }
576 };
577
578 /* Verify that empty paths are handled gracefully. */
579
580 static void
581 test_empty_path (pretty_printer *event_pp)
582 {
583 test_diagnostic_path path (event_pp);
584 ASSERT_FALSE (path.interprocedural_p ());
585
586 path_summary summary (path, false);
587 ASSERT_EQ (summary.get_num_ranges (), 0);
588
589 test_diagnostic_context dc;
590 print_path_summary_as_text (&summary, &dc, true);
591 ASSERT_STREQ ("",
592 pp_formatted_text (dc.printer));
593 }
594
595 /* Verify that print_path_summary works on a purely intraprocedural path. */
596
597 static void
598 test_intraprocedural_path (pretty_printer *event_pp)
599 {
600 tree fntype_void_void
601 = build_function_type_array (void_type_node, 0, NULL);
602 tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
603
604 test_diagnostic_path path (event_pp);
605 path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free");
606 path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free");
607
608 ASSERT_FALSE (path.interprocedural_p ());
609
610 path_summary summary (path, false);
611 ASSERT_EQ (summary.get_num_ranges (), 1);
612
613 test_diagnostic_context dc;
614 print_path_summary_as_text (&summary, &dc, true);
615 ASSERT_STREQ (" `foo': events 1-2 (depth 0)\n"
616 " |\n"
617 " | (1): first `free'\n"
618 " | (2): double `free'\n"
619 " |\n",
620 pp_formatted_text (dc.printer));
621 }
622
623 /* Verify that print_path_summary works on an interprocedural path. */
624
625 static void
626 test_interprocedural_path_1 (pretty_printer *event_pp)
627 {
628 /* Build fndecls. The types aren't quite right, but that
629 doesn't matter for the purposes of this test. */
630 tree fntype_void_void
631 = build_function_type_array (void_type_node, 0, NULL);
632 tree fndecl_test = build_fn_decl ("test", fntype_void_void);
633 tree fndecl_make_boxed_int
634 = build_fn_decl ("make_boxed_int", fntype_void_void);
635 tree fndecl_wrapped_malloc
636 = build_fn_decl ("wrapped_malloc", fntype_void_void);
637 tree fndecl_free_boxed_int
638 = build_fn_decl ("free_boxed_int", fntype_void_void);
639 tree fndecl_wrapped_free
640 = build_fn_decl ("wrapped_free", fntype_void_void);
641
642 test_diagnostic_path path (event_pp);
643 path.add_entry (fndecl_test, 0);
644 path.add_call (fndecl_test, 0, fndecl_make_boxed_int);
645 path.add_call (fndecl_make_boxed_int, 1, fndecl_wrapped_malloc);
646 path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_malloc, 2, "calling malloc");
647 path.add_return (fndecl_test, 0);
648 path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
649 path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
650 path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
651 path.add_return (fndecl_test, 0);
652 path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
653 path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
654 path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
655 ASSERT_EQ (path.num_events (), 18);
656
657 ASSERT_TRUE (path.interprocedural_p ());
658
659 path_summary summary (path, false);
660 ASSERT_EQ (summary.get_num_ranges (), 9);
661
662 test_diagnostic_context dc;
663 print_path_summary_as_text (&summary, &dc, true);
664 ASSERT_STREQ
665 (" `test': events 1-2 (depth 0)\n"
666 " |\n"
667 " | (1): entering `test'\n"
668 " | (2): calling `make_boxed_int'\n"
669 " |\n"
670 " +--> `make_boxed_int': events 3-4 (depth 1)\n"
671 " |\n"
672 " | (3): entering `make_boxed_int'\n"
673 " | (4): calling `wrapped_malloc'\n"
674 " |\n"
675 " +--> `wrapped_malloc': events 5-6 (depth 2)\n"
676 " |\n"
677 " | (5): entering `wrapped_malloc'\n"
678 " | (6): calling malloc\n"
679 " |\n"
680 " <-------------+\n"
681 " |\n"
682 " `test': events 7-8 (depth 0)\n"
683 " |\n"
684 " | (7): returning to `test'\n"
685 " | (8): calling `free_boxed_int'\n"
686 " |\n"
687 " +--> `free_boxed_int': events 9-10 (depth 1)\n"
688 " |\n"
689 " | (9): entering `free_boxed_int'\n"
690 " | (10): calling `wrapped_free'\n"
691 " |\n"
692 " +--> `wrapped_free': events 11-12 (depth 2)\n"
693 " |\n"
694 " | (11): entering `wrapped_free'\n"
695 " | (12): calling free\n"
696 " |\n"
697 " <-------------+\n"
698 " |\n"
699 " `test': events 13-14 (depth 0)\n"
700 " |\n"
701 " | (13): returning to `test'\n"
702 " | (14): calling `free_boxed_int'\n"
703 " |\n"
704 " +--> `free_boxed_int': events 15-16 (depth 1)\n"
705 " |\n"
706 " | (15): entering `free_boxed_int'\n"
707 " | (16): calling `wrapped_free'\n"
708 " |\n"
709 " +--> `wrapped_free': events 17-18 (depth 2)\n"
710 " |\n"
711 " | (17): entering `wrapped_free'\n"
712 " | (18): calling free\n"
713 " |\n",
714 pp_formatted_text (dc.printer));
715 }
716
717 /* Example where we pop the stack to an intermediate frame, rather than the
718 initial one. */
719
720 static void
721 test_interprocedural_path_2 (pretty_printer *event_pp)
722 {
723 /* Build fndecls. The types aren't quite right, but that
724 doesn't matter for the purposes of this test. */
725 tree fntype_void_void
726 = build_function_type_array (void_type_node, 0, NULL);
727 tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
728 tree fndecl_bar = build_fn_decl ("bar", fntype_void_void);
729 tree fndecl_baz = build_fn_decl ("baz", fntype_void_void);
730
731 test_diagnostic_path path (event_pp);
732 path.add_entry (fndecl_foo, 0);
733 path.add_call (fndecl_foo, 0, fndecl_bar);
734 path.add_call (fndecl_bar, 1, fndecl_baz);
735 path.add_return (fndecl_bar, 1);
736 path.add_call (fndecl_bar, 1, fndecl_baz);
737 ASSERT_EQ (path.num_events (), 8);
738
739 ASSERT_TRUE (path.interprocedural_p ());
740
741 path_summary summary (path, false);
742 ASSERT_EQ (summary.get_num_ranges (), 5);
743
744 test_diagnostic_context dc;
745 print_path_summary_as_text (&summary, &dc, true);
746 ASSERT_STREQ
747 (" `foo': events 1-2 (depth 0)\n"
748 " |\n"
749 " | (1): entering `foo'\n"
750 " | (2): calling `bar'\n"
751 " |\n"
752 " +--> `bar': events 3-4 (depth 1)\n"
753 " |\n"
754 " | (3): entering `bar'\n"
755 " | (4): calling `baz'\n"
756 " |\n"
757 " +--> `baz': event 5 (depth 2)\n"
758 " |\n"
759 " | (5): entering `baz'\n"
760 " |\n"
761 " <------+\n"
762 " |\n"
763 " `bar': events 6-7 (depth 1)\n"
764 " |\n"
765 " | (6): returning to `bar'\n"
766 " | (7): calling `baz'\n"
767 " |\n"
768 " +--> `baz': event 8 (depth 2)\n"
769 " |\n"
770 " | (8): entering `baz'\n"
771 " |\n",
772 pp_formatted_text (dc.printer));
773 }
774
775 /* Verify that print_path_summary is sane in the face of a recursive
776 diagnostic_path. */
777
778 static void
779 test_recursion (pretty_printer *event_pp)
780 {
781 tree fntype_void_void
782 = build_function_type_array (void_type_node, 0, NULL);
783 tree fndecl_factorial = build_fn_decl ("factorial", fntype_void_void);
784
785 test_diagnostic_path path (event_pp);
786 path.add_entry (fndecl_factorial, 0);
787 for (int depth = 0; depth < 3; depth++)
788 path.add_call (fndecl_factorial, depth, fndecl_factorial);
789 ASSERT_EQ (path.num_events (), 7);
790
791 ASSERT_TRUE (path.interprocedural_p ());
792
793 path_summary summary (path, false);
794 ASSERT_EQ (summary.get_num_ranges (), 4);
795
796 test_diagnostic_context dc;
797 print_path_summary_as_text (&summary, &dc, true);
798 ASSERT_STREQ
799 (" `factorial': events 1-2 (depth 0)\n"
800 " |\n"
801 " | (1): entering `factorial'\n"
802 " | (2): calling `factorial'\n"
803 " |\n"
804 " +--> `factorial': events 3-4 (depth 1)\n"
805 " |\n"
806 " | (3): entering `factorial'\n"
807 " | (4): calling `factorial'\n"
808 " |\n"
809 " +--> `factorial': events 5-6 (depth 2)\n"
810 " |\n"
811 " | (5): entering `factorial'\n"
812 " | (6): calling `factorial'\n"
813 " |\n"
814 " +--> `factorial': event 7 (depth 3)\n"
815 " |\n"
816 " | (7): entering `factorial'\n"
817 " |\n",
818 pp_formatted_text (dc.printer));
819 }
820
821 /* Run all of the selftests within this file. */
822
823 void
824 tree_diagnostic_path_cc_tests ()
825 {
826 auto_fix_quotes fix_quotes;
827 pretty_printer *event_pp = global_dc->printer->clone ();
828 pp_show_color (event_pp) = 0;
829 test_empty_path (event_pp);
830 test_intraprocedural_path (event_pp);
831 test_interprocedural_path_1 (event_pp);
832 test_interprocedural_path_2 (event_pp);
833 test_recursion (event_pp);
834 delete event_pp;
835 }
836
837 } // namespace selftest
838
839 #if __GNUC__ >= 10
840 # pragma GCC diagnostic pop
841 #endif
842
843 #endif /* #if CHECKING_P */