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