xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/analyzer/checker-path.cc (revision 4c3eb207d36f67d31994830c0a694161fc1ca39b)
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 *
event_kind_to_string(enum event_kind ek)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
dump(pretty_printer * pp) const110 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
prepare_for_emission(checker_path *,pending_diagnostic * pd,diagnostic_event_id_t emission_id)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
get_desc(bool) const154 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
get_desc(bool) const166 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 
statement_event(const gimple * stmt,tree fndecl,int depth,const program_state & dst_state)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
get_desc(bool) const188 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
get_desc(bool can_colorize) const204 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 
state_change_event(const supernode * node,const gimple * stmt,int stack_depth,const state_machine & sm,tree var,state_machine::state_t from,state_machine::state_t to,tree origin,const program_state & dst_state)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
get_desc(bool can_colorize) const244 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&
get_callgraph_superedge() const319 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
should_filter_p(int verbosity) const329 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 
superedge_event(enum event_kind kind,const exploded_edge & eedge,location_t loc,tree fndecl,int depth)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 &
get_cfg_superedge() const373 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 
cfg_edge_event(enum event_kind kind,const exploded_edge & eedge,location_t loc,tree fndecl,int depth)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
get_desc(bool can_colorize) const411 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
maybe_describe_condition(bool can_colorize) const495 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
maybe_describe_condition(bool can_colorize,tree lhs,enum tree_code op,tree rhs)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
should_print_expr_p(tree expr)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 
call_event(const exploded_edge & eedge,location_t loc,tree fndecl,int depth)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
get_desc(bool can_colorize) const619 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
is_call_p() const644 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 
return_event(const exploded_edge & eedge,location_t loc,tree fndecl,int depth)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
get_desc(bool can_colorize) const672 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
is_return_p() const698 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
get_desc(bool can_colorize) const709 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
prepare_for_emission(checker_path * path,pending_diagnostic * pd,diagnostic_event_id_t emission_id)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
get_longjmp_caller() const735 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
get_setjmp_caller() const743 rewind_event::get_setjmp_caller () const
744 {
745   return m_eedge->m_dest->get_function ()->decl;
746 }
747 
748 /* rewind_event's ctor.  */
749 
rewind_event(const exploded_edge * eedge,enum event_kind kind,location_t loc,tree fndecl,int depth,const rewind_info_t * rewind_info)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
get_desc(bool can_colorize) const767 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
get_desc(bool can_colorize) const791 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
prepare_for_emission(checker_path * path,pending_diagnostic * pd,diagnostic_event_id_t emission_id)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
get_desc(bool can_colorize) const856 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
dump(pretty_printer * pp) const892 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
maybe_log(logger * logger,const char * desc) const912 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
debug() const933 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
add_final_event(const state_machine * sm,const exploded_node * enode,const gimple * stmt,tree var,state_machine::state_t state)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