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