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