1 /* Subclasses of diagnostic_path and diagnostic_event for analyzer diagnostics. 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 8 under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3, or (at your option) 10 any later version. 11 12 GCC is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License 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 "function.h" 26 #include "basic-block.h" 27 #include "gimple.h" 28 #include "gimple-pretty-print.h" 29 #include "fold-const.h" 30 #include "function.h" 31 #include "diagnostic-path.h" 32 #include "options.h" 33 #include "cgraph.h" 34 #include "function.h" 35 #include "cfg.h" 36 #include "digraph.h" 37 #include "alloc-pool.h" 38 #include "fibonacci_heap.h" 39 #include "diagnostic-event-id.h" 40 #include "shortest-paths.h" 41 #include "analyzer/analyzer.h" 42 #include "analyzer/analyzer-logging.h" 43 #include "analyzer/sm.h" 44 #include "sbitmap.h" 45 #include "bitmap.h" 46 #include "tristate.h" 47 #include "ordered-hash-map.h" 48 #include "selftest.h" 49 #include "analyzer/region-model.h" 50 #include "analyzer/program-state.h" 51 #include "analyzer/checker-path.h" 52 #include "gimple-iterator.h" 53 #include "analyzer/supergraph.h" 54 #include "analyzer/pending-diagnostic.h" 55 #include "analyzer/diagnostic-manager.h" 56 #include "analyzer/constraint-manager.h" 57 #include "analyzer/diagnostic-manager.h" 58 #include "analyzer/checker-path.h" 59 #include "analyzer/call-string.h" 60 #include "analyzer/program-point.h" 61 #include "analyzer/exploded-graph.h" 62 63 #if ENABLE_ANALYZER 64 65 namespace ana { 66 67 /* Get a string for EK. */ 68 69 const char * 70 event_kind_to_string (enum event_kind ek) 71 { 72 switch (ek) 73 { 74 default: 75 gcc_unreachable (); 76 case EK_DEBUG: 77 return "EK_DEBUG"; 78 case EK_CUSTOM: 79 return "EK_CUSTOM"; 80 case EK_STMT: 81 return "EK_STMT"; 82 case EK_FUNCTION_ENTRY: 83 return "EK_FUNCTION_ENTRY"; 84 case EK_STATE_CHANGE: 85 return "EK_STATE_CHANGE"; 86 case EK_START_CFG_EDGE: 87 return "EK_START_CFG_EDGE"; 88 case EK_END_CFG_EDGE: 89 return "EK_END_CFG_EDGE"; 90 case EK_CALL_EDGE: 91 return "EK_CALL_EDGE"; 92 case EK_RETURN_EDGE: 93 return "EK_RETURN_EDGE"; 94 case EK_SETJMP: 95 return "EK_SETJMP"; 96 case EK_REWIND_FROM_LONGJMP: 97 return "EK_REWIND_FROM_LONGJMP"; 98 case EK_REWIND_TO_SETJMP: 99 return "EK_REWIND_TO_SETJMP"; 100 case EK_WARNING: 101 return "EK_WARNING"; 102 } 103 } 104 105 /* class checker_event : public diagnostic_event. */ 106 107 /* Dump this event to PP (for debugging/logging purposes). */ 108 109 void 110 checker_event::dump (pretty_printer *pp) const 111 { 112 label_text event_desc (get_desc (false)); 113 pp_printf (pp, "\"%s\" (depth %i, m_loc=%x)", 114 event_desc.m_buffer, 115 get_stack_depth (), 116 get_location ()); 117 event_desc.maybe_free (); 118 } 119 120 /* Hook for being notified when this event has its final id EMISSION_ID 121 and is about to emitted for PD. 122 123 Base implementation of checker_event::prepare_for_emission vfunc; 124 subclasses that override this should chain up to it. 125 126 Record PD and EMISSION_ID, and call the get_desc vfunc, so that any 127 side-effects of the call to get_desc take place before 128 pending_diagnostic::emit is called. 129 130 For example, state_change_event::get_desc can call 131 pending_diagnostic::describe_state_change; free_of_non_heap can use this 132 to tweak the message (TODO: would be neater to simply capture the 133 pertinent data within the sm-state). */ 134 135 void 136 checker_event::prepare_for_emission (checker_path *, 137 pending_diagnostic *pd, 138 diagnostic_event_id_t emission_id) 139 { 140 m_pending_diagnostic = pd; 141 m_emission_id = emission_id; 142 143 label_text desc = get_desc (false); 144 desc.maybe_free (); 145 } 146 147 /* class debug_event : public checker_event. */ 148 149 /* Implementation of diagnostic_event::get_desc vfunc for 150 debug_event. 151 Use the saved string as the event's description. */ 152 153 label_text 154 debug_event::get_desc (bool) const 155 { 156 return label_text::borrow (m_desc); 157 } 158 159 /* class custom_event : public checker_event. */ 160 161 /* Implementation of diagnostic_event::get_desc vfunc for 162 custom_event. 163 Use the saved string as the event's description. */ 164 165 label_text 166 custom_event::get_desc (bool) const 167 { 168 return label_text::borrow (m_desc); 169 } 170 171 /* class statement_event : public checker_event. */ 172 173 /* statement_event's ctor. */ 174 175 statement_event::statement_event (const gimple *stmt, tree fndecl, int depth, 176 const program_state &dst_state) 177 : checker_event (EK_STMT, gimple_location (stmt), fndecl, depth), 178 m_stmt (stmt), 179 m_dst_state (dst_state) 180 { 181 } 182 183 /* Implementation of diagnostic_event::get_desc vfunc for 184 statement_event. 185 Use the statement's dump form as the event's description. */ 186 187 label_text 188 statement_event::get_desc (bool) const 189 { 190 pretty_printer pp; 191 pp_string (&pp, "stmt: "); 192 pp_gimple_stmt_1 (&pp, m_stmt, 0, (dump_flags_t)0); 193 return label_text::take (xstrdup (pp_formatted_text (&pp))); 194 } 195 196 /* class function_entry_event : public checker_event. */ 197 198 /* Implementation of diagnostic_event::get_desc vfunc for 199 function_entry_event. 200 201 Use a string such as "entry to 'foo'" as the event's description. */ 202 203 label_text 204 function_entry_event::get_desc (bool can_colorize) const 205 { 206 return make_label_text (can_colorize, "entry to %qE", m_fndecl); 207 } 208 209 /* class state_change_event : public checker_event. */ 210 211 /* state_change_event's ctor. */ 212 213 state_change_event::state_change_event (const supernode *node, 214 const gimple *stmt, 215 int stack_depth, 216 const state_machine &sm, 217 tree var, 218 state_machine::state_t from, 219 state_machine::state_t to, 220 tree origin, 221 const program_state &dst_state) 222 : checker_event (EK_STATE_CHANGE, 223 stmt->location, node->m_fun->decl, 224 stack_depth), 225 m_node (node), m_stmt (stmt), m_sm (sm), 226 m_var (var), m_from (from), m_to (to), 227 m_origin (origin), 228 m_dst_state (dst_state) 229 { 230 } 231 232 /* Implementation of diagnostic_event::get_desc vfunc for 233 state_change_event. 234 235 Attempt to generate a nicer human-readable description. 236 For greatest precision-of-wording, give the pending diagnostic 237 a chance to describe this state change (in terms of the 238 diagnostic). 239 Note that we only have a pending_diagnostic set on the event once 240 the diagnostic is about to being emitted, so the description for 241 an event can change. */ 242 243 label_text 244 state_change_event::get_desc (bool can_colorize) const 245 { 246 if (m_pending_diagnostic) 247 { 248 label_text custom_desc 249 = m_pending_diagnostic->describe_state_change 250 (evdesc::state_change (can_colorize, m_var, m_origin, 251 m_from, m_to, m_emission_id, *this)); 252 if (custom_desc.m_buffer) 253 { 254 if (flag_analyzer_verbose_state_changes) 255 { 256 /* Append debug version. */ 257 label_text result; 258 if (m_origin) 259 result = make_label_text 260 (can_colorize, 261 "%s (state of %qE: %qs -> %qs, origin: %qE)", 262 custom_desc.m_buffer, 263 m_var, 264 m_sm.get_state_name (m_from), 265 m_sm.get_state_name (m_to), 266 m_origin); 267 else 268 result = make_label_text 269 (can_colorize, 270 "%s (state of %qE: %qs -> %qs, origin: NULL)", 271 custom_desc.m_buffer, 272 m_var, 273 m_sm.get_state_name (m_from), 274 m_sm.get_state_name (m_to)); 275 custom_desc.maybe_free (); 276 return result; 277 } 278 else 279 return custom_desc; 280 } 281 } 282 283 /* Fallback description. */ 284 if (m_var) 285 { 286 if (m_origin) 287 return make_label_text 288 (can_colorize, 289 "state of %qE: %qs -> %qs (origin: %qE)", 290 m_var, 291 m_sm.get_state_name (m_from), 292 m_sm.get_state_name (m_to), 293 m_origin); 294 else 295 return make_label_text 296 (can_colorize, 297 "state of %qE: %qs -> %qs (origin: NULL)", 298 m_var, 299 m_sm.get_state_name (m_from), 300 m_sm.get_state_name (m_to)); 301 } 302 else 303 { 304 gcc_assert (m_origin == NULL_TREE); 305 return make_label_text 306 (can_colorize, 307 "global state: %qs -> %qs", 308 m_sm.get_state_name (m_from), 309 m_sm.get_state_name (m_to)); 310 } 311 } 312 313 /* class superedge_event : public checker_event. */ 314 315 /* Get the callgraph_superedge for this superedge_event, which must be 316 for an interprocedural edge, rather than a CFG edge. */ 317 318 const callgraph_superedge& 319 superedge_event::get_callgraph_superedge () const 320 { 321 gcc_assert (m_sedge->m_kind != SUPEREDGE_CFG_EDGE); 322 return *m_sedge->dyn_cast_callgraph_superedge (); 323 } 324 325 /* Determine if this event should be filtered at the given verbosity 326 level. */ 327 328 bool 329 superedge_event::should_filter_p (int verbosity) const 330 { 331 switch (m_sedge->m_kind) 332 { 333 case SUPEREDGE_CFG_EDGE: 334 { 335 if (verbosity < 2) 336 return true; 337 338 if (verbosity < 4) 339 { 340 /* Filter events with empty descriptions. This ought to filter 341 FALLTHRU, but retain true/false/switch edges. */ 342 label_text desc = get_desc (false); 343 gcc_assert (desc.m_buffer); 344 if (desc.m_buffer[0] == '\0') 345 return true; 346 desc.maybe_free (); 347 } 348 } 349 break; 350 351 default: 352 break; 353 } 354 return false; 355 } 356 357 /* superedge_event's ctor. */ 358 359 superedge_event::superedge_event (enum event_kind kind, 360 const exploded_edge &eedge, 361 location_t loc, tree fndecl, int depth) 362 : checker_event (kind, loc, fndecl, depth), 363 m_eedge (eedge), m_sedge (eedge.m_sedge), 364 m_var (NULL_TREE), m_critical_state (0) 365 { 366 } 367 368 /* class cfg_edge_event : public superedge_event. */ 369 370 /* Get the cfg_superedge for this cfg_edge_event. */ 371 372 const cfg_superedge & 373 cfg_edge_event::get_cfg_superedge () const 374 { 375 return *m_sedge->dyn_cast_cfg_superedge (); 376 } 377 378 /* cfg_edge_event's ctor. */ 379 380 cfg_edge_event::cfg_edge_event (enum event_kind kind, 381 const exploded_edge &eedge, 382 location_t loc, tree fndecl, int depth) 383 : superedge_event (kind, eedge, loc, fndecl, depth) 384 { 385 gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CFG_EDGE); 386 } 387 388 /* class start_cfg_edge_event : public cfg_edge_event. */ 389 390 /* Implementation of diagnostic_event::get_desc vfunc for 391 start_cfg_edge_event. 392 393 If -fanalyzer-verbose-edges, then generate low-level descriptions, such 394 as 395 "taking 'true' edge SN:7 -> SN:8". 396 397 Otherwise, generate strings using the label of the underlying CFG if 398 any, such as: 399 "following 'true' branch..." or 400 "following 'case 3' branch..." 401 "following 'default' branch..." 402 403 For conditionals, attempt to supply a description of the condition that 404 holds, such as: 405 "following 'false' branch (when 'ptr' is non-NULL)..." 406 407 Failing that, return an empty description (which will lead to this event 408 being filtered). */ 409 410 label_text 411 start_cfg_edge_event::get_desc (bool can_colorize) const 412 { 413 bool user_facing = !flag_analyzer_verbose_edges; 414 char *edge_desc = m_sedge->get_description (user_facing); 415 if (user_facing) 416 { 417 if (edge_desc && strlen (edge_desc) > 0) 418 { 419 label_text cond_desc = maybe_describe_condition (can_colorize); 420 label_text result; 421 if (cond_desc.m_buffer) 422 { 423 result = make_label_text (can_colorize, 424 "following %qs branch (%s)...", 425 edge_desc, cond_desc.m_buffer); 426 cond_desc.maybe_free (); 427 } 428 else 429 { 430 result = make_label_text (can_colorize, 431 "following %qs branch...", 432 edge_desc); 433 } 434 free (edge_desc); 435 return result; 436 } 437 else 438 { 439 free (edge_desc); 440 return label_text::borrow (""); 441 } 442 } 443 else 444 { 445 if (strlen (edge_desc) > 0) 446 { 447 label_text result 448 = make_label_text (can_colorize, 449 "taking %qs edge SN:%i -> SN:%i", 450 edge_desc, 451 m_sedge->m_src->m_index, 452 m_sedge->m_dest->m_index); 453 free (edge_desc); 454 return result; 455 } 456 else 457 { 458 free (edge_desc); 459 return make_label_text (can_colorize, 460 "taking edge SN:%i -> SN:%i", 461 m_sedge->m_src->m_index, 462 m_sedge->m_dest->m_index); 463 } 464 } 465 } 466 467 /* Attempt to generate a description of any condition that holds at this edge. 468 469 The intent is to make the user-facing messages more clear, especially for 470 cases where there's a single or double-negative, such as 471 when describing the false branch of an inverted condition. 472 473 For example, rather than printing just: 474 475 | if (!ptr) 476 | ~ 477 | | 478 | (1) following 'false' branch... 479 480 it's clearer to spell out the condition that holds: 481 482 | if (!ptr) 483 | ~ 484 | | 485 | (1) following 'false' branch (when 'ptr' is non-NULL)... 486 ^^^^^^^^^^^^^^^^^^^^^^ 487 488 In the above example, this function would generate the highlighted 489 string: "when 'ptr' is non-NULL". 490 491 If the edge is not a condition, or it's not clear that a description of 492 the condition would be helpful to the user, return NULL. */ 493 494 label_text 495 start_cfg_edge_event::maybe_describe_condition (bool can_colorize) const 496 { 497 const cfg_superedge& cfg_sedge = get_cfg_superedge (); 498 499 if (cfg_sedge.true_value_p () || cfg_sedge.false_value_p ()) 500 { 501 const gimple *last_stmt = m_sedge->m_src->get_last_stmt (); 502 if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt)) 503 { 504 enum tree_code op = gimple_cond_code (cond_stmt); 505 tree lhs = gimple_cond_lhs (cond_stmt); 506 tree rhs = gimple_cond_rhs (cond_stmt); 507 if (cfg_sedge.false_value_p ()) 508 op = invert_tree_comparison (op, false /* honor_nans */); 509 return maybe_describe_condition (can_colorize, 510 lhs, op, rhs); 511 } 512 } 513 return label_text::borrow (NULL); 514 } 515 516 /* Subroutine of maybe_describe_condition above. 517 518 Attempt to generate a user-facing description of the condition 519 LHS OP RHS, but only if it is likely to make it easier for the 520 user to understand a condition. */ 521 522 label_text 523 start_cfg_edge_event::maybe_describe_condition (bool can_colorize, 524 tree lhs, 525 enum tree_code op, 526 tree rhs) 527 { 528 /* In theory we could just build a tree via 529 fold_build2 (op, boolean_type_node, lhs, rhs) 530 and print it with %qE on it, but this leads to warts such as 531 parenthesizing vars, such as '(i) <= 9', and uses of '<unknown>'. */ 532 533 /* Special-case: describe testing the result of strcmp, as figuring 534 out what the "true" or "false" path is can be confusing to the user. */ 535 if (TREE_CODE (lhs) == SSA_NAME 536 && zerop (rhs)) 537 { 538 if (gcall *call = dyn_cast <gcall *> (SSA_NAME_DEF_STMT (lhs))) 539 if (is_special_named_call_p (call, "strcmp", 2)) 540 { 541 if (op == EQ_EXPR) 542 return label_text::borrow ("when the strings are equal"); 543 if (op == NE_EXPR) 544 return label_text::borrow ("when the strings are non-equal"); 545 } 546 } 547 548 /* Only attempt to generate text for sufficiently simple expressions. */ 549 if (!should_print_expr_p (lhs)) 550 return label_text::borrow (NULL); 551 if (!should_print_expr_p (rhs)) 552 return label_text::borrow (NULL); 553 554 /* Special cases for pointer comparisons against NULL. */ 555 if (POINTER_TYPE_P (TREE_TYPE (lhs)) 556 && POINTER_TYPE_P (TREE_TYPE (rhs)) 557 && zerop (rhs)) 558 { 559 if (op == EQ_EXPR) 560 return make_label_text (can_colorize, "when %qE is NULL", 561 lhs); 562 if (op == NE_EXPR) 563 return make_label_text (can_colorize, "when %qE is non-NULL", 564 lhs); 565 } 566 567 return make_label_text (can_colorize, "when %<%E %s %E%>", 568 lhs, op_symbol_code (op), rhs); 569 } 570 571 /* Subroutine of maybe_describe_condition. 572 573 Return true if EXPR is we will get suitable user-facing output 574 from %E on it. */ 575 576 bool 577 start_cfg_edge_event::should_print_expr_p (tree expr) 578 { 579 if (TREE_CODE (expr) == SSA_NAME) 580 { 581 if (SSA_NAME_VAR (expr)) 582 return should_print_expr_p (SSA_NAME_VAR (expr)); 583 else 584 return false; 585 } 586 587 if (DECL_P (expr)) 588 return true; 589 590 if (CONSTANT_CLASS_P (expr)) 591 return true; 592 593 return false; 594 } 595 596 /* class call_event : public superedge_event. */ 597 598 /* call_event's ctor. */ 599 600 call_event::call_event (const exploded_edge &eedge, 601 location_t loc, tree fndecl, int depth) 602 : superedge_event (EK_CALL_EDGE, eedge, loc, fndecl, depth) 603 { 604 gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CALL); 605 } 606 607 /* Implementation of diagnostic_event::get_desc vfunc for 608 call_event. 609 610 If this call event passes critical state for an sm-based warning, 611 allow the diagnostic to generate a precise description, such as: 612 613 "passing freed pointer 'ptr' in call to 'foo' from 'bar'" 614 615 Otherwise, generate a description of the form 616 "calling 'foo' from 'bar'". */ 617 618 label_text 619 call_event::get_desc (bool can_colorize) const 620 { 621 if (m_critical_state && m_pending_diagnostic) 622 { 623 gcc_assert (m_var); 624 label_text custom_desc 625 = m_pending_diagnostic->describe_call_with_state 626 (evdesc::call_with_state (can_colorize, 627 m_sedge->m_src->m_fun->decl, 628 m_sedge->m_dest->m_fun->decl, 629 m_var, 630 m_critical_state)); 631 if (custom_desc.m_buffer) 632 return custom_desc; 633 } 634 635 return make_label_text (can_colorize, 636 "calling %qE from %qE", 637 m_sedge->m_dest->m_fun->decl, 638 m_sedge->m_src->m_fun->decl); 639 } 640 641 /* Override of checker_event::is_call_p for calls. */ 642 643 bool 644 call_event::is_call_p () const 645 { 646 return true; 647 } 648 649 /* class return_event : public superedge_event. */ 650 651 /* return_event's ctor. */ 652 653 return_event::return_event (const exploded_edge &eedge, 654 location_t loc, tree fndecl, int depth) 655 : superedge_event (EK_RETURN_EDGE, eedge, loc, fndecl, depth) 656 { 657 gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_RETURN); 658 } 659 660 /* Implementation of diagnostic_event::get_desc vfunc for 661 return_event. 662 663 If this return event returns critical state for an sm-based warning, 664 allow the diagnostic to generate a precise description, such as: 665 666 "possible of NULL to 'foo' from 'bar'" 667 668 Otherwise, generate a description of the form 669 "returning to 'foo' from 'bar'. */ 670 671 label_text 672 return_event::get_desc (bool can_colorize) const 673 { 674 /* For greatest precision-of-wording, if this is returning the 675 state involved in the pending diagnostic, give the pending 676 diagnostic a chance to describe this return (in terms of 677 itself). */ 678 if (m_critical_state && m_pending_diagnostic) 679 { 680 label_text custom_desc 681 = m_pending_diagnostic->describe_return_of_state 682 (evdesc::return_of_state (can_colorize, 683 m_sedge->m_dest->m_fun->decl, 684 m_sedge->m_src->m_fun->decl, 685 m_critical_state)); 686 if (custom_desc.m_buffer) 687 return custom_desc; 688 } 689 return make_label_text (can_colorize, 690 "returning to %qE from %qE", 691 m_sedge->m_dest->m_fun->decl, 692 m_sedge->m_src->m_fun->decl); 693 } 694 695 /* Override of checker_event::is_return_p for returns. */ 696 697 bool 698 return_event::is_return_p () const 699 { 700 return true; 701 } 702 703 /* class setjmp_event : public checker_event. */ 704 705 /* Implementation of diagnostic_event::get_desc vfunc for 706 setjmp_event. */ 707 708 label_text 709 setjmp_event::get_desc (bool can_colorize) const 710 { 711 return make_label_text (can_colorize, 712 "%qs called here", 713 get_user_facing_name (m_setjmp_call)); 714 } 715 716 /* Implementation of checker_event::prepare_for_emission vfunc for setjmp_event. 717 718 Record this setjmp's event ID into the path, so that rewind events can 719 use it. */ 720 721 void 722 setjmp_event::prepare_for_emission (checker_path *path, 723 pending_diagnostic *pd, 724 diagnostic_event_id_t emission_id) 725 { 726 checker_event::prepare_for_emission (path, pd, emission_id); 727 path->record_setjmp_event (m_enode, emission_id); 728 } 729 730 /* class rewind_event : public checker_event. */ 731 732 /* Get the fndecl containing the site of the longjmp call. */ 733 734 tree 735 rewind_event::get_longjmp_caller () const 736 { 737 return m_eedge->m_src->get_function ()->decl; 738 } 739 740 /* Get the fndecl containing the site of the setjmp call. */ 741 742 tree 743 rewind_event::get_setjmp_caller () const 744 { 745 return m_eedge->m_dest->get_function ()->decl; 746 } 747 748 /* rewind_event's ctor. */ 749 750 rewind_event::rewind_event (const exploded_edge *eedge, 751 enum event_kind kind, 752 location_t loc, tree fndecl, int depth, 753 const rewind_info_t *rewind_info) 754 : checker_event (kind, loc, fndecl, depth), 755 m_rewind_info (rewind_info), 756 m_eedge (eedge) 757 { 758 gcc_assert (m_eedge->m_custom_info == m_rewind_info); 759 } 760 761 /* class rewind_from_longjmp_event : public rewind_event. */ 762 763 /* Implementation of diagnostic_event::get_desc vfunc for 764 rewind_from_longjmp_event. */ 765 766 label_text 767 rewind_from_longjmp_event::get_desc (bool can_colorize) const 768 { 769 const char *src_name 770 = get_user_facing_name (m_rewind_info->get_longjmp_call ()); 771 772 if (get_longjmp_caller () == get_setjmp_caller ()) 773 /* Special-case: purely intraprocedural rewind. */ 774 return make_label_text (can_colorize, 775 "rewinding within %qE from %qs...", 776 get_longjmp_caller (), 777 src_name); 778 else 779 return make_label_text (can_colorize, 780 "rewinding from %qs in %qE...", 781 src_name, 782 get_longjmp_caller ()); 783 } 784 785 /* class rewind_to_setjmp_event : public rewind_event. */ 786 787 /* Implementation of diagnostic_event::get_desc vfunc for 788 rewind_to_setjmp_event. */ 789 790 label_text 791 rewind_to_setjmp_event::get_desc (bool can_colorize) const 792 { 793 const char *dst_name 794 = get_user_facing_name (m_rewind_info->get_setjmp_call ()); 795 796 /* If we can, identify the ID of the setjmp_event. */ 797 if (m_original_setjmp_event_id.known_p ()) 798 { 799 if (get_longjmp_caller () == get_setjmp_caller ()) 800 /* Special-case: purely intraprocedural rewind. */ 801 return make_label_text (can_colorize, 802 "...to %qs (saved at %@)", 803 dst_name, 804 &m_original_setjmp_event_id); 805 else 806 return make_label_text (can_colorize, 807 "...to %qs in %qE (saved at %@)", 808 dst_name, 809 get_setjmp_caller (), 810 &m_original_setjmp_event_id); 811 } 812 else 813 { 814 if (get_longjmp_caller () == get_setjmp_caller ()) 815 /* Special-case: purely intraprocedural rewind. */ 816 return make_label_text (can_colorize, 817 "...to %qs", 818 dst_name, 819 get_setjmp_caller ()); 820 else 821 return make_label_text (can_colorize, 822 "...to %qs in %qE", 823 dst_name, 824 get_setjmp_caller ()); 825 } 826 } 827 828 /* Implementation of checker_event::prepare_for_emission vfunc for 829 rewind_to_setjmp_event. 830 831 Attempt to look up the setjmp event ID that recorded the jmp_buf 832 for this rewind. */ 833 834 void 835 rewind_to_setjmp_event::prepare_for_emission (checker_path *path, 836 pending_diagnostic *pd, 837 diagnostic_event_id_t emission_id) 838 { 839 checker_event::prepare_for_emission (path, pd, emission_id); 840 path->get_setjmp_event (m_rewind_info->get_enode_origin (), 841 &m_original_setjmp_event_id); 842 } 843 844 /* class warning_event : public checker_event. */ 845 846 /* Implementation of diagnostic_event::get_desc vfunc for 847 warning_event. 848 849 If the pending diagnostic implements describe_final_event, use it, 850 generating a precise description e.g. 851 "second 'free' here; first 'free' was at (7)" 852 853 Otherwise generate a generic description. */ 854 855 label_text 856 warning_event::get_desc (bool can_colorize) const 857 { 858 if (m_pending_diagnostic) 859 { 860 label_text ev_desc 861 = m_pending_diagnostic->describe_final_event 862 (evdesc::final_event (can_colorize, m_var, m_state)); 863 if (ev_desc.m_buffer) 864 { 865 if (m_sm && flag_analyzer_verbose_state_changes) 866 { 867 label_text result 868 = make_label_text (can_colorize, 869 "%s (%qE is in state %qs)", 870 ev_desc.m_buffer, 871 m_var,m_sm->get_state_name (m_state)); 872 ev_desc.maybe_free (); 873 return result; 874 } 875 else 876 return ev_desc; 877 } 878 } 879 880 if (m_sm) 881 return make_label_text (can_colorize, 882 "here (%qE is in state %qs)", 883 m_var, 884 m_sm->get_state_name (m_state)); 885 else 886 return label_text::borrow ("here"); 887 } 888 889 /* Print a single-line representation of this path to PP. */ 890 891 void 892 checker_path::dump (pretty_printer *pp) const 893 { 894 pp_character (pp, '['); 895 896 checker_event *e; 897 int i; 898 FOR_EACH_VEC_ELT (m_events, i, e) 899 { 900 if (i > 0) 901 pp_string (pp, ", "); 902 label_text event_desc (e->get_desc (false)); 903 pp_printf (pp, "\"%s\"", event_desc.m_buffer); 904 event_desc.maybe_free (); 905 } 906 pp_character (pp, ']'); 907 } 908 909 /* Print a multiline form of this path to LOGGER, prefixing it with DESC. */ 910 911 void 912 checker_path::maybe_log (logger *logger, const char *desc) const 913 { 914 if (!logger) 915 return; 916 logger->start_log_line (); 917 logger->log_partial ("%s: ", desc); 918 dump (logger->get_printer ()); 919 logger->end_log_line (); 920 for (unsigned i = 0; i < m_events.length (); i++) 921 { 922 logger->start_log_line (); 923 logger->log_partial ("%s[%i]: %s ", desc, i, 924 event_kind_to_string (m_events[i]->m_kind)); 925 m_events[i]->dump (logger->get_printer ()); 926 logger->end_log_line (); 927 } 928 } 929 930 /* Print a multiline form of this path to STDERR. */ 931 932 DEBUG_FUNCTION void 933 checker_path::debug () const 934 { 935 checker_event *e; 936 int i; 937 FOR_EACH_VEC_ELT (m_events, i, e) 938 { 939 label_text event_desc (e->get_desc (false)); 940 fprintf (stderr, 941 "[%i]: %s \"%s\"\n", 942 i, 943 event_kind_to_string (m_events[i]->m_kind), 944 event_desc.m_buffer); 945 event_desc.maybe_free (); 946 } 947 } 948 949 /* Add a warning_event to the end of this path. */ 950 951 void 952 checker_path::add_final_event (const state_machine *sm, 953 const exploded_node *enode, const gimple *stmt, 954 tree var, state_machine::state_t state) 955 { 956 checker_event *end_of_path 957 = new warning_event (stmt->location, 958 enode->get_function ()->decl, 959 enode->get_stack_depth (), 960 sm, var, state); 961 add_event (end_of_path); 962 } 963 964 } // namespace ana 965 966 #endif /* #if ENABLE_ANALYZER */ 967