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