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