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:
path_label(const diagnostic_path * path,unsigned start_idx)50   path_label (const diagnostic_path *path, unsigned start_idx)
51   : m_path (path), m_start_idx (start_idx)
52   {}
53 
get_text(unsigned range_idx) const54   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
can_consolidate_events(const diagnostic_event & e1,const diagnostic_event & e2,bool check_locations)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 {
event_range__anon63cb56060111::event_range118   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 
maybe_add_event__anon63cb56060111::event_range129   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 
print__anon63cb56060111::event_range146   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 
get_num_ranges__anon63cb56060111::path_summary213   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 
path_summary(const diagnostic_path & path,bool check_rich_locations)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
write_indent(pretty_printer * pp,int spaces)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
print_fndecl(pretty_printer * pp,tree fndecl,bool quoted)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
print_path_summary_as_text(const path_summary * ps,diagnostic_context * dc,bool show_depths)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
default_tree_diagnostic_path_printer(diagnostic_context * context,const diagnostic_path * path)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 *
default_tree_make_json_for_path(diagnostic_context * context,const diagnostic_path * path)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:
test_diagnostic_path(pretty_printer * event_pp)538   test_diagnostic_path (pretty_printer *event_pp)
539   : simple_diagnostic_path (event_pp)
540   {
541   }
542 
add_entry(tree fndecl,int stack_depth)543   void add_entry (tree fndecl, int stack_depth)
544   {
545     add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
546 	       "entering %qE", fndecl);
547   }
548 
add_return(tree fndecl,int stack_depth)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 
add_call(tree caller,int caller_stack_depth,tree callee)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
test_empty_path(pretty_printer * event_pp)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
test_intraprocedural_path(pretty_printer * event_pp)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
test_interprocedural_path_1(pretty_printer * event_pp)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
test_interprocedural_path_2(pretty_printer * event_pp)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
test_recursion(pretty_printer * event_pp)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
tree_diagnostic_path_cc_tests()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 */
829