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