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 #ifndef GCC_ANALYZER_CHECKER_PATH_H
22 #define GCC_ANALYZER_CHECKER_PATH_H
23 
24 namespace ana {
25 
26 /* An enum for discriminating between the concrete subclasses of
27    checker_event.  */
28 
29 enum event_kind
30 {
31   EK_DEBUG,
32   EK_CUSTOM,
33   EK_STMT,
34   EK_FUNCTION_ENTRY,
35   EK_STATE_CHANGE,
36   EK_START_CFG_EDGE,
37   EK_END_CFG_EDGE,
38   EK_CALL_EDGE,
39   EK_RETURN_EDGE,
40   EK_START_CONSOLIDATED_CFG_EDGES,
41   EK_END_CONSOLIDATED_CFG_EDGES,
42   EK_SETJMP,
43   EK_REWIND_FROM_LONGJMP,
44   EK_REWIND_TO_SETJMP,
45   EK_WARNING
46 };
47 
48 extern const char *event_kind_to_string (enum event_kind ek);
49 
50 /* Event subclasses.
51 
52    The class hierarchy looks like this (using indentation to show
53    inheritance, and with event_kinds shown for the concrete subclasses):
54 
55    diagnostic_event
56      checker_event
57        debug_event (EK_DEBUG)
58        custom_event (EK_CUSTOM)
59 	 precanned_custom_event
60        statement_event (EK_STMT)
61        function_entry_event (EK_FUNCTION_ENTRY)
62        state_change_event (EK_STATE_CHANGE)
63        superedge_event
64          cfg_edge_event
65 	   start_cfg_edge_event (EK_START_CFG_EDGE)
66 	   end_cfg_edge_event (EK_END_CFG_EDGE)
67          call_event (EK_CALL_EDGE)
68          return_edge (EK_RETURN_EDGE)
69        start_consolidated_cfg_edges_event (EK_START_CONSOLIDATED_CFG_EDGES)
70        end_consolidated_cfg_edges_event (EK_END_CONSOLIDATED_CFG_EDGES)
71        setjmp_event (EK_SETJMP)
72        rewind_event
73          rewind_from_longjmp_event (EK_REWIND_FROM_LONGJMP)
74 	 rewind_to_setjmp_event (EK_REWIND_TO_SETJMP)
75        warning_event (EK_WARNING).  */
76 
77 /* Abstract subclass of diagnostic_event; the base class for use in
78    checker_path (the analyzer's diagnostic_path subclass).  */
79 
80 class checker_event : public diagnostic_event
81 {
82 public:
checker_event(enum event_kind kind,location_t loc,tree fndecl,int depth)83   checker_event (enum event_kind kind,
84 		 location_t loc, tree fndecl, int depth)
85     : m_kind (kind), m_loc (loc), m_fndecl (fndecl), m_depth (depth),
86       m_pending_diagnostic (NULL), m_emission_id ()
87   {
88   }
89 
90   /* Implementation of diagnostic_event.  */
91 
get_location()92   location_t get_location () const FINAL OVERRIDE { return m_loc; }
get_fndecl()93   tree get_fndecl () const FINAL OVERRIDE { return m_fndecl; }
get_stack_depth()94   int get_stack_depth () const FINAL OVERRIDE { return m_depth; }
95 
96   /* Additional functionality.  */
97 
98   virtual void prepare_for_emission (checker_path *,
99 				     pending_diagnostic *pd,
100 				     diagnostic_event_id_t emission_id);
is_call_p()101   virtual bool is_call_p () const { return false; }
is_function_entry_p()102   virtual bool is_function_entry_p () const  { return false; }
is_return_p()103   virtual bool is_return_p () const  { return false; }
104 
105   /* For use with %@.  */
get_id_ptr()106   const diagnostic_event_id_t *get_id_ptr () const
107   {
108     return &m_emission_id;
109   }
110 
111   void dump (pretty_printer *pp) const;
112 
set_location(location_t loc)113   void set_location (location_t loc) { m_loc = loc; }
114 
115  public:
116   const enum event_kind m_kind;
117  protected:
118   location_t m_loc;
119   tree m_fndecl;
120   int m_depth;
121   pending_diagnostic *m_pending_diagnostic;
122   diagnostic_event_id_t m_emission_id; // only set once all pruning has occurred
123 };
124 
125 /* A concrete event subclass for a purely textual event, for use in
126    debugging path creation and filtering.  */
127 
128 class debug_event : public checker_event
129 {
130 public:
debug_event(location_t loc,tree fndecl,int depth,const char * desc)131   debug_event (location_t loc, tree fndecl, int depth,
132 	      const char *desc)
133   : checker_event (EK_DEBUG, loc, fndecl, depth),
134     m_desc (xstrdup (desc))
135   {
136   }
~debug_event()137   ~debug_event ()
138   {
139     free (m_desc);
140   }
141 
142   label_text get_desc (bool) const FINAL OVERRIDE;
143 
144 private:
145   char *m_desc;
146 };
147 
148 /* An abstract event subclass for custom events.  These are not filtered,
149    as they are likely to be pertinent to the diagnostic.  */
150 
151 class custom_event : public checker_event
152 {
153 protected:
custom_event(location_t loc,tree fndecl,int depth)154   custom_event (location_t loc, tree fndecl, int depth)
155   : checker_event (EK_CUSTOM, loc, fndecl, depth)
156   {
157   }
158 };
159 
160 /* A concrete custom_event subclass with a precanned message.  */
161 
162 class precanned_custom_event : public custom_event
163 {
164 public:
precanned_custom_event(location_t loc,tree fndecl,int depth,const char * desc)165   precanned_custom_event (location_t loc, tree fndecl, int depth,
166 			  const char *desc)
167   : custom_event (loc, fndecl, depth),
168     m_desc (xstrdup (desc))
169   {
170   }
~precanned_custom_event()171   ~precanned_custom_event ()
172   {
173     free (m_desc);
174   }
175 
176   label_text get_desc (bool) const FINAL OVERRIDE;
177 
178 private:
179   char *m_desc;
180 };
181 
182 /* A concrete event subclass describing the execution of a gimple statement,
183    for use at high verbosity levels when debugging paths.  */
184 
185 class statement_event : public checker_event
186 {
187 public:
188   statement_event (const gimple *stmt, tree fndecl, int depth,
189 		   const program_state &dst_state);
190 
191   label_text get_desc (bool) const FINAL OVERRIDE;
192 
193   const gimple * const m_stmt;
194   const program_state m_dst_state;
195 };
196 
197 /* An event subclass describing the entry to a function.  */
198 
199 class function_entry_event : public checker_event
200 {
201 public:
function_entry_event(location_t loc,tree fndecl,int depth)202   function_entry_event (location_t loc, tree fndecl, int depth)
203   : checker_event (EK_FUNCTION_ENTRY, loc, fndecl, depth)
204   {
205   }
206 
207   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
208 
is_function_entry_p()209   bool is_function_entry_p () const FINAL OVERRIDE { return true; }
210 };
211 
212 /* Subclass of checker_event describing a state change.  */
213 
214 class state_change_event : public checker_event
215 {
216 public:
217   state_change_event (const supernode *node, const gimple *stmt,
218 		      int stack_depth,
219 		      const state_machine &sm,
220 		      const svalue *sval,
221 		      state_machine::state_t from,
222 		      state_machine::state_t to,
223 		      const svalue *origin,
224 		      const program_state &dst_state);
225 
226   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
227 
get_dest_function()228   function *get_dest_function () const
229   {
230     return m_dst_state.get_current_function ();
231   }
232 
233   const supernode *m_node;
234   const gimple *m_stmt;
235   const state_machine &m_sm;
236   const svalue *m_sval;
237   state_machine::state_t m_from;
238   state_machine::state_t m_to;
239   const svalue *m_origin;
240   program_state m_dst_state;
241 };
242 
243 /* Subclass of checker_event; parent class for subclasses that relate to
244    a superedge.  */
245 
246 class superedge_event : public checker_event
247 {
248 public:
249   /* Mark this edge event as being either an interprocedural call or
250      return in which VAR is in STATE, and that this is critical to the
251      diagnostic (so that get_desc can attempt to get a better description
252      from any pending_diagnostic).  */
record_critical_state(tree var,state_machine::state_t state)253   void record_critical_state (tree var, state_machine::state_t state)
254   {
255     m_var = var;
256     m_critical_state = state;
257   }
258 
259   const callgraph_superedge& get_callgraph_superedge () const;
260 
261   bool should_filter_p (int verbosity) const;
262 
263  protected:
264   superedge_event (enum event_kind kind, const exploded_edge &eedge,
265 		   location_t loc, tree fndecl, int depth);
266 
267  public:
268   const exploded_edge &m_eedge;
269   const superedge *m_sedge;
270   tree m_var;
271   state_machine::state_t m_critical_state;
272 };
273 
274 /* An abstract event subclass for when a CFG edge is followed; it has two
275    subclasses, representing the start of the edge and the end of the
276    edge, which come in pairs.  */
277 
278 class cfg_edge_event : public superedge_event
279 {
280 public:
281   const cfg_superedge& get_cfg_superedge () const;
282 
283  protected:
284   cfg_edge_event (enum event_kind kind, const exploded_edge &eedge,
285 		  location_t loc, tree fndecl, int depth);
286 };
287 
288 /* A concrete event subclass for the start of a CFG edge
289    e.g. "following 'false' branch...'.  */
290 
291 class start_cfg_edge_event : public cfg_edge_event
292 {
293 public:
start_cfg_edge_event(const exploded_edge & eedge,location_t loc,tree fndecl,int depth)294   start_cfg_edge_event (const exploded_edge &eedge,
295 			location_t loc, tree fndecl, int depth)
296   : cfg_edge_event (EK_START_CFG_EDGE, eedge, loc, fndecl, depth)
297   {
298   }
299 
300   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
301 
302  private:
303   label_text maybe_describe_condition (bool can_colorize) const;
304 
305   static label_text maybe_describe_condition (bool can_colorize,
306 					      tree lhs,
307 					      enum tree_code op,
308 					      tree rhs);
309   static bool should_print_expr_p (tree);
310 };
311 
312 /* A concrete event subclass for the end of a CFG edge
313    e.g. "...to here'.  */
314 
315 class end_cfg_edge_event : public cfg_edge_event
316 {
317 public:
end_cfg_edge_event(const exploded_edge & eedge,location_t loc,tree fndecl,int depth)318   end_cfg_edge_event (const exploded_edge &eedge,
319 		      location_t loc, tree fndecl, int depth)
320   : cfg_edge_event (EK_END_CFG_EDGE, eedge, loc, fndecl, depth)
321   {
322   }
323 
get_desc(bool)324   label_text get_desc (bool /*can_colorize*/) const FINAL OVERRIDE
325   {
326     return label_text::borrow ("...to here");
327   }
328 };
329 
330 /* A concrete event subclass for an interprocedural call.  */
331 
332 class call_event : public superedge_event
333 {
334 public:
335   call_event (const exploded_edge &eedge,
336 	      location_t loc, tree fndecl, int depth);
337 
338   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
339 
340   bool is_call_p () const FINAL OVERRIDE;
341 
342   const supernode *m_src_snode;
343   const supernode *m_dest_snode;
344 };
345 
346 /* A concrete event subclass for an interprocedural return.  */
347 
348 class return_event : public superedge_event
349 {
350 public:
351   return_event (const exploded_edge &eedge,
352 		location_t loc, tree fndecl, int depth);
353 
354   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
355 
356   bool is_return_p () const FINAL OVERRIDE;
357 
358   const supernode *m_src_snode;
359   const supernode *m_dest_snode;
360 };
361 
362 /* A concrete event subclass for the start of a consolidated run of CFG
363    edges all either TRUE or FALSE e.g. "following 'false' branch...'.  */
364 
365 class start_consolidated_cfg_edges_event : public checker_event
366 {
367 public:
start_consolidated_cfg_edges_event(location_t loc,tree fndecl,int depth,bool edge_sense)368   start_consolidated_cfg_edges_event (location_t loc, tree fndecl, int depth,
369 				      bool edge_sense)
370   : checker_event (EK_START_CONSOLIDATED_CFG_EDGES, loc, fndecl, depth),
371     m_edge_sense (edge_sense)
372   {
373   }
374 
375   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
376 
377  private:
378   bool m_edge_sense;
379 };
380 
381 /* A concrete event subclass for the end of a consolidated run of
382    CFG edges e.g. "...to here'.  */
383 
384 class end_consolidated_cfg_edges_event : public checker_event
385 {
386 public:
end_consolidated_cfg_edges_event(location_t loc,tree fndecl,int depth)387   end_consolidated_cfg_edges_event (location_t loc, tree fndecl, int depth)
388   : checker_event (EK_END_CONSOLIDATED_CFG_EDGES, loc, fndecl, depth)
389   {
390   }
391 
get_desc(bool)392   label_text get_desc (bool /*can_colorize*/) const FINAL OVERRIDE
393   {
394     return label_text::borrow ("...to here");
395   }
396 };
397 
398 /* A concrete event subclass for a setjmp or sigsetjmp call.  */
399 
400 class setjmp_event : public checker_event
401 {
402 public:
setjmp_event(location_t loc,const exploded_node * enode,tree fndecl,int depth,const gcall * setjmp_call)403   setjmp_event (location_t loc, const exploded_node *enode,
404 		tree fndecl, int depth, const gcall *setjmp_call)
405   : checker_event (EK_SETJMP, loc, fndecl, depth),
406     m_enode (enode), m_setjmp_call (setjmp_call)
407   {
408   }
409 
410   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
411 
412   void prepare_for_emission (checker_path *path,
413 			     pending_diagnostic *pd,
414 			     diagnostic_event_id_t emission_id) FINAL OVERRIDE;
415 
416 private:
417   const exploded_node *m_enode;
418   const gcall *m_setjmp_call;
419 };
420 
421 /* An abstract event subclass for rewinding from a longjmp to a setjmp
422    (or siglongjmp to sigsetjmp).
423 
424    Base class for two from/to subclasses, showing the two halves of the
425    rewind.  */
426 
427 class rewind_event : public checker_event
428 {
429 public:
430   tree get_longjmp_caller () const;
431   tree get_setjmp_caller () const;
get_eedge()432   const exploded_edge *get_eedge () const { return m_eedge; }
433 
434  protected:
435   rewind_event (const exploded_edge *eedge,
436 		enum event_kind kind,
437 		location_t loc, tree fndecl, int depth,
438 		const rewind_info_t *rewind_info);
439   const rewind_info_t *m_rewind_info;
440 
441  private:
442   const exploded_edge *m_eedge;
443 };
444 
445 /* A concrete event subclass for rewinding from a longjmp to a setjmp,
446    showing the longjmp (or siglongjmp).  */
447 
448 class rewind_from_longjmp_event : public rewind_event
449 {
450 public:
rewind_from_longjmp_event(const exploded_edge * eedge,location_t loc,tree fndecl,int depth,const rewind_info_t * rewind_info)451   rewind_from_longjmp_event (const exploded_edge *eedge,
452 			     location_t loc, tree fndecl, int depth,
453 			     const rewind_info_t *rewind_info)
454   : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc, fndecl, depth,
455 		  rewind_info)
456   {
457   }
458 
459   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
460 };
461 
462 /* A concrete event subclass for rewinding from a longjmp to a setjmp,
463    showing the setjmp (or sigsetjmp).  */
464 
465 class rewind_to_setjmp_event : public rewind_event
466 {
467 public:
rewind_to_setjmp_event(const exploded_edge * eedge,location_t loc,tree fndecl,int depth,const rewind_info_t * rewind_info)468   rewind_to_setjmp_event (const exploded_edge *eedge,
469 			  location_t loc, tree fndecl, int depth,
470 			  const rewind_info_t *rewind_info)
471   : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth,
472 		  rewind_info)
473   {
474   }
475 
476   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
477 
478   void prepare_for_emission (checker_path *path,
479 			     pending_diagnostic *pd,
480 			     diagnostic_event_id_t emission_id) FINAL OVERRIDE;
481 
482 private:
483   diagnostic_event_id_t m_original_setjmp_event_id;
484 };
485 
486 /* Concrete subclass of checker_event for use at the end of a path:
487    a repeat of the warning message at the end of the path (perhaps with
488    references to pertinent events that occurred on the way), at the point
489    where the problem occurs.  */
490 
491 class warning_event : public checker_event
492 {
493 public:
warning_event(location_t loc,tree fndecl,int depth,const state_machine * sm,tree var,state_machine::state_t state)494   warning_event (location_t loc, tree fndecl, int depth,
495 		 const state_machine *sm,
496 		 tree var, state_machine::state_t state)
497   : checker_event (EK_WARNING, loc, fndecl, depth),
498     m_sm (sm), m_var (var), m_state (state)
499   {
500   }
501 
502   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
503 
504 private:
505   const state_machine *m_sm;
506   tree m_var;
507   state_machine::state_t m_state;
508 };
509 
510 /* Subclass of diagnostic_path for analyzer diagnostics.  */
511 
512 class checker_path : public diagnostic_path
513 {
514 public:
checker_path()515   checker_path () : diagnostic_path () {}
516 
517   /* Implementation of diagnostic_path vfuncs.  */
518 
num_events()519   unsigned num_events () const FINAL OVERRIDE
520   {
521     return m_events.length ();
522   }
523 
get_event(int idx)524   const diagnostic_event & get_event (int idx) const FINAL OVERRIDE
525   {
526     return *m_events[idx];
527   }
528 
get_checker_event(int idx)529   checker_event *get_checker_event (int idx)
530   {
531     return m_events[idx];
532   }
533 
534   void dump (pretty_printer *pp) const;
535   void debug () const;
536 
537   void maybe_log (logger *logger, const char *desc) const;
538 
add_event(checker_event * event)539   void add_event (checker_event *event)
540   {
541     m_events.safe_push (event);
542   }
543 
delete_event(int idx)544   void delete_event (int idx)
545   {
546     checker_event *event = m_events[idx];
547     m_events.ordered_remove (idx);
548     delete event;
549   }
550 
delete_events(unsigned start_idx,unsigned len)551   void delete_events (unsigned start_idx, unsigned len)
552   {
553     for (unsigned i = start_idx; i < start_idx + len; i++)
554       delete m_events[i];
555     m_events.block_remove (start_idx, len);
556   }
557 
replace_event(unsigned idx,checker_event * new_event)558   void replace_event (unsigned idx, checker_event *new_event)
559   {
560     delete m_events[idx];
561     m_events[idx] = new_event;
562   }
563 
564   void add_final_event (const state_machine *sm,
565 			const exploded_node *enode, const gimple *stmt,
566 			tree var, state_machine::state_t state);
567 
568   /* After all event-pruning, a hook for notifying each event what
569      its ID will be.  The events are notified in order, allowing
570      for later events to refer to the IDs of earlier events in
571      their descriptions.  */
prepare_for_emission(pending_diagnostic * pd)572   void prepare_for_emission (pending_diagnostic *pd)
573   {
574     checker_event *e;
575     int i;
576     FOR_EACH_VEC_ELT (m_events, i, e)
577       e->prepare_for_emission (this, pd, diagnostic_event_id_t (i));
578   }
579 
580   void fixup_locations (pending_diagnostic *pd);
581 
record_setjmp_event(const exploded_node * enode,diagnostic_event_id_t setjmp_emission_id)582   void record_setjmp_event (const exploded_node *enode,
583 			    diagnostic_event_id_t setjmp_emission_id)
584   {
585     m_setjmp_event_ids.put (enode, setjmp_emission_id);
586   }
587 
get_setjmp_event(const exploded_node * enode,diagnostic_event_id_t * out_emission_id)588   bool get_setjmp_event (const exploded_node *enode,
589 			 diagnostic_event_id_t *out_emission_id)
590   {
591     if (diagnostic_event_id_t *emission_id = m_setjmp_event_ids.get (enode))
592       {
593 	*out_emission_id = *emission_id;
594 	return true;
595       }
596     return false;
597   }
598 
599   bool cfg_edge_pair_at_p (unsigned idx) const;
600 
601 private:
602   DISABLE_COPY_AND_ASSIGN(checker_path);
603 
604   /* The events that have occurred along this path.  */
605   auto_delete_vec<checker_event> m_events;
606 
607   /* During prepare_for_emission (and after), the setjmp_event for each
608      exploded_node *, so that rewind events can refer to them in their
609      descriptions.  */
610   hash_map <const exploded_node *, diagnostic_event_id_t> m_setjmp_event_ids;
611 };
612 
613 } // namespace ana
614 
615 #endif /* GCC_ANALYZER_CHECKER_PATH_H */
616