]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/analyzer/checker-path.h
analyzer: refactor custom_event, introducing precanned_custom_event class
[thirdparty/gcc.git] / gcc / analyzer / checker-path.h
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:
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
92 location_t get_location () const FINAL OVERRIDE { return m_loc; }
93 tree get_fndecl () const FINAL OVERRIDE { return m_fndecl; }
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);
101 virtual bool is_call_p () const { return false; }
102 virtual bool is_function_entry_p () const { return false; }
103 virtual bool is_return_p () const { return false; }
104
105 /* For use with %@. */
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
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:
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 }
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:
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:
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 }
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:
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
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
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). */
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:
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:
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
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
343 /* A concrete event subclass for an interprocedural return. */
344
345 class return_event : public superedge_event
346 {
347 public:
348 return_event (const exploded_edge &eedge,
349 location_t loc, tree fndecl, int depth);
350
351 label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
352
353 bool is_return_p () const FINAL OVERRIDE;
354 };
355
356 /* A concrete event subclass for the start of a consolidated run of CFG
357 edges all either TRUE or FALSE e.g. "following 'false' branch...'. */
358
359 class start_consolidated_cfg_edges_event : public checker_event
360 {
361 public:
362 start_consolidated_cfg_edges_event (location_t loc, tree fndecl, int depth,
363 bool edge_sense)
364 : checker_event (EK_START_CONSOLIDATED_CFG_EDGES, loc, fndecl, depth),
365 m_edge_sense (edge_sense)
366 {
367 }
368
369 label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
370
371 private:
372 bool m_edge_sense;
373 };
374
375 /* A concrete event subclass for the end of a consolidated run of
376 CFG edges e.g. "...to here'. */
377
378 class end_consolidated_cfg_edges_event : public checker_event
379 {
380 public:
381 end_consolidated_cfg_edges_event (location_t loc, tree fndecl, int depth)
382 : checker_event (EK_END_CONSOLIDATED_CFG_EDGES, loc, fndecl, depth)
383 {
384 }
385
386 label_text get_desc (bool /*can_colorize*/) const FINAL OVERRIDE
387 {
388 return label_text::borrow ("...to here");
389 }
390 };
391
392 /* A concrete event subclass for a setjmp or sigsetjmp call. */
393
394 class setjmp_event : public checker_event
395 {
396 public:
397 setjmp_event (location_t loc, const exploded_node *enode,
398 tree fndecl, int depth, const gcall *setjmp_call)
399 : checker_event (EK_SETJMP, loc, fndecl, depth),
400 m_enode (enode), m_setjmp_call (setjmp_call)
401 {
402 }
403
404 label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
405
406 void prepare_for_emission (checker_path *path,
407 pending_diagnostic *pd,
408 diagnostic_event_id_t emission_id) FINAL OVERRIDE;
409
410 private:
411 const exploded_node *m_enode;
412 const gcall *m_setjmp_call;
413 };
414
415 /* An abstract event subclass for rewinding from a longjmp to a setjmp
416 (or siglongjmp to sigsetjmp).
417
418 Base class for two from/to subclasses, showing the two halves of the
419 rewind. */
420
421 class rewind_event : public checker_event
422 {
423 public:
424 tree get_longjmp_caller () const;
425 tree get_setjmp_caller () const;
426 const exploded_edge *get_eedge () const { return m_eedge; }
427
428 protected:
429 rewind_event (const exploded_edge *eedge,
430 enum event_kind kind,
431 location_t loc, tree fndecl, int depth,
432 const rewind_info_t *rewind_info);
433 const rewind_info_t *m_rewind_info;
434
435 private:
436 const exploded_edge *m_eedge;
437 };
438
439 /* A concrete event subclass for rewinding from a longjmp to a setjmp,
440 showing the longjmp (or siglongjmp). */
441
442 class rewind_from_longjmp_event : public rewind_event
443 {
444 public:
445 rewind_from_longjmp_event (const exploded_edge *eedge,
446 location_t loc, tree fndecl, int depth,
447 const rewind_info_t *rewind_info)
448 : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc, fndecl, depth,
449 rewind_info)
450 {
451 }
452
453 label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
454 };
455
456 /* A concrete event subclass for rewinding from a longjmp to a setjmp,
457 showing the setjmp (or sigsetjmp). */
458
459 class rewind_to_setjmp_event : public rewind_event
460 {
461 public:
462 rewind_to_setjmp_event (const exploded_edge *eedge,
463 location_t loc, tree fndecl, int depth,
464 const rewind_info_t *rewind_info)
465 : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth,
466 rewind_info)
467 {
468 }
469
470 label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
471
472 void prepare_for_emission (checker_path *path,
473 pending_diagnostic *pd,
474 diagnostic_event_id_t emission_id) FINAL OVERRIDE;
475
476 private:
477 diagnostic_event_id_t m_original_setjmp_event_id;
478 };
479
480 /* Concrete subclass of checker_event for use at the end of a path:
481 a repeat of the warning message at the end of the path (perhaps with
482 references to pertinent events that occurred on the way), at the point
483 where the problem occurs. */
484
485 class warning_event : public checker_event
486 {
487 public:
488 warning_event (location_t loc, tree fndecl, int depth,
489 const state_machine *sm,
490 tree var, state_machine::state_t state)
491 : checker_event (EK_WARNING, loc, fndecl, depth),
492 m_sm (sm), m_var (var), m_state (state)
493 {
494 }
495
496 label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
497
498 private:
499 const state_machine *m_sm;
500 tree m_var;
501 state_machine::state_t m_state;
502 };
503
504 /* Subclass of diagnostic_path for analyzer diagnostics. */
505
506 class checker_path : public diagnostic_path
507 {
508 public:
509 checker_path () : diagnostic_path () {}
510
511 /* Implementation of diagnostic_path vfuncs. */
512
513 unsigned num_events () const FINAL OVERRIDE
514 {
515 return m_events.length ();
516 }
517
518 const diagnostic_event & get_event (int idx) const FINAL OVERRIDE
519 {
520 return *m_events[idx];
521 }
522
523 checker_event *get_checker_event (int idx)
524 {
525 return m_events[idx];
526 }
527
528 void dump (pretty_printer *pp) const;
529 void debug () const;
530
531 void maybe_log (logger *logger, const char *desc) const;
532
533 void add_event (checker_event *event)
534 {
535 m_events.safe_push (event);
536 }
537
538 void delete_event (int idx)
539 {
540 checker_event *event = m_events[idx];
541 m_events.ordered_remove (idx);
542 delete event;
543 }
544
545 void delete_events (unsigned start_idx, unsigned len)
546 {
547 for (unsigned i = start_idx; i < start_idx + len; i++)
548 delete m_events[i];
549 m_events.block_remove (start_idx, len);
550 }
551
552 void replace_event (unsigned idx, checker_event *new_event)
553 {
554 delete m_events[idx];
555 m_events[idx] = new_event;
556 }
557
558 void add_final_event (const state_machine *sm,
559 const exploded_node *enode, const gimple *stmt,
560 tree var, state_machine::state_t state);
561
562 /* After all event-pruning, a hook for notifying each event what
563 its ID will be. The events are notified in order, allowing
564 for later events to refer to the IDs of earlier events in
565 their descriptions. */
566 void prepare_for_emission (pending_diagnostic *pd)
567 {
568 checker_event *e;
569 int i;
570 FOR_EACH_VEC_ELT (m_events, i, e)
571 e->prepare_for_emission (this, pd, diagnostic_event_id_t (i));
572 }
573
574 void fixup_locations (pending_diagnostic *pd);
575
576 void record_setjmp_event (const exploded_node *enode,
577 diagnostic_event_id_t setjmp_emission_id)
578 {
579 m_setjmp_event_ids.put (enode, setjmp_emission_id);
580 }
581
582 bool get_setjmp_event (const exploded_node *enode,
583 diagnostic_event_id_t *out_emission_id)
584 {
585 if (diagnostic_event_id_t *emission_id = m_setjmp_event_ids.get (enode))
586 {
587 *out_emission_id = *emission_id;
588 return true;
589 }
590 return false;
591 }
592
593 bool cfg_edge_pair_at_p (unsigned idx) const;
594
595 private:
596 DISABLE_COPY_AND_ASSIGN(checker_path);
597
598 /* The events that have occurred along this path. */
599 auto_delete_vec<checker_event> m_events;
600
601 /* During prepare_for_emission (and after), the setjmp_event for each
602 exploded_node *, so that rewind events can refer to them in their
603 descriptions. */
604 hash_map <const exploded_node *, diagnostic_event_id_t> m_setjmp_event_ids;
605 };
606
607 } // namespace ana
608
609 #endif /* GCC_ANALYZER_CHECKER_PATH_H */