1 /* Classes for analyzer diagnostics.
2 Copyright (C) 2019-2023 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_PENDING_DIAGNOSTIC_H
22 #define GCC_ANALYZER_PENDING_DIAGNOSTIC_H
23
24 #include "diagnostic-path.h"
25 #include "analyzer/sm.h"
26
27 namespace ana {
28
29 /* A bundle of information about things that are of interest to a
30 pending_diagnostic.
31
32 For now, merely the set of regions that are pertinent to the
33 diagnostic, so that we can notify the user about when they
34 were created. */
35
36 struct interesting_t
37 {
38 void add_region_creation (const region *reg);
39
40 void dump_to_pp (pretty_printer *pp, bool simple) const;
41
42 auto_vec<const region *> m_region_creation;
43 };
44
45 /* Various bundles of information used for generating more precise
46 messages for events within a diagnostic_path, for passing to the
47 various "describe_*" vfuncs of pending_diagnostic. See those
48 for more information. */
49
50 namespace evdesc {
51
52 struct event_desc
53 {
54 event_desc (bool colorize) : m_colorize (colorize) {}
55
56 label_text formatted_print (const char *fmt, ...) const
57 ATTRIBUTE_GCC_DIAG(2,3);
58
59 bool m_colorize;
60 };
61
62 /* For use by pending_diagnostic::describe_state_change. */
63
64 struct state_change : public event_desc
65 {
66 state_change (bool colorize,
67 tree expr,
68 tree origin,
69 state_machine::state_t old_state,
70 state_machine::state_t new_state,
71 diagnostic_event_id_t event_id,
72 const state_change_event &event)
73 : event_desc (colorize),
74 m_expr (expr), m_origin (origin),
75 m_old_state (old_state), m_new_state (new_state),
76 m_event_id (event_id), m_event (event)
77 {}
78
79 bool is_global_p () const { return m_expr == NULL_TREE; }
80
81 tree m_expr;
82 tree m_origin;
83 state_machine::state_t m_old_state;
84 state_machine::state_t m_new_state;
85 diagnostic_event_id_t m_event_id;
86 const state_change_event &m_event;
87 };
88
89 /* For use by pending_diagnostic::describe_call_with_state. */
90
91 struct call_with_state : public event_desc
92 {
93 call_with_state (bool colorize,
94 tree caller_fndecl, tree callee_fndecl,
95 tree expr, state_machine::state_t state)
96 : event_desc (colorize),
97 m_caller_fndecl (caller_fndecl),
98 m_callee_fndecl (callee_fndecl),
99 m_expr (expr),
100 m_state (state)
101 {
102 }
103
104 tree m_caller_fndecl;
105 tree m_callee_fndecl;
106 tree m_expr;
107 state_machine::state_t m_state;
108 };
109
110 /* For use by pending_diagnostic::describe_return_of_state. */
111
112 struct return_of_state : public event_desc
113 {
114 return_of_state (bool colorize,
115 tree caller_fndecl, tree callee_fndecl,
116 state_machine::state_t state)
117 : event_desc (colorize),
118 m_caller_fndecl (caller_fndecl),
119 m_callee_fndecl (callee_fndecl),
120 m_state (state)
121 {
122 }
123
124 tree m_caller_fndecl;
125 tree m_callee_fndecl;
126 state_machine::state_t m_state;
127 };
128
129 /* For use by pending_diagnostic::describe_final_event. */
130
131 struct final_event : public event_desc
132 {
133 final_event (bool colorize,
134 tree expr, state_machine::state_t state,
135 const warning_event &event)
136 : event_desc (colorize),
137 m_expr (expr), m_state (state), m_event (event)
138 {}
139
140 tree m_expr;
141 state_machine::state_t m_state;
142 const warning_event &m_event;
143 };
144
145 } /* end of namespace evdesc */
146
147 /* An abstract base class for capturing information about a diagnostic in
148 a form that is ready to emit at a later point (or be rejected).
149 Each kind of diagnostic will have a concrete subclass of
150 pending_diagnostic.
151
152 Normally, gcc diagnostics are emitted using va_list, which can't be
153 portably stored for later use, so we have to use an "emit" virtual
154 function.
155
156 This class also supports comparison, so that multiple pending_diagnostic
157 instances can be de-duplicated.
158
159 As well as emitting a diagnostic, the class has various "precision of
160 wording" virtual functions, for generating descriptions for events
161 within a diagnostic_path. These are optional, but implementing these
162 allows for more precise wordings than the more generic
163 implementation. */
164
165 class pending_diagnostic
166 {
167 public:
168 virtual ~pending_diagnostic () {}
169
170 /* Vfunc to get the command-line option used when emitting the diagnostic,
171 or zero if there is none.
172 Used by diagnostic_manager for early rejection of diagnostics (to avoid
173 having to generate feasible execution paths for them). */
174 virtual int get_controlling_option () const = 0;
175
176 /* Vfunc to give the diagnostic the chance to terminate the execution
177 path being explored. By default, don't terminate the path. */
178 virtual bool terminate_path_p () const { return false; }
179
180 /* Vfunc for emitting the diagnostic. The rich_location will have been
181 populated with a diagnostic_path.
182 Return true if a diagnostic is actually emitted. */
183 virtual bool emit (rich_location *) = 0;
184
185 /* Hand-coded RTTI: get an ID for the subclass. */
186 virtual const char *get_kind () const = 0;
187
188 /* A vfunc for identifying "use of uninitialized value". */
189 virtual bool use_of_uninit_p () const { return false; }
190
191 /* Compare for equality with OTHER, which might be of a different
192 subclass. */
193
194 bool equal_p (const pending_diagnostic &other) const
195 {
196 /* Check for pointer equality on the IDs from get_kind. */
197 if (get_kind () != other.get_kind ())
198 return false;
199 /* Call vfunc now we know they have the same ID: */
200 return subclass_equal_p (other);
201 }
202
203 /* A vfunc for testing for equality, where we've already
204 checked they have the same ID. See pending_diagnostic_subclass
205 below for a convenience subclass for implementing this. */
206 virtual bool subclass_equal_p (const pending_diagnostic &other) const = 0;
207
208 /* Return true if T1 and T2 are "the same" for the purposes of
209 diagnostic deduplication. */
210 static bool same_tree_p (tree t1, tree t2);
211
212 /* Vfunc for fixing up locations, e.g. to avoid unwinding
213 inside specific macros. PRIMARY is true for the primary location
214 for the diagnostic, and FALSE for events in their paths. */
215 virtual location_t fixup_location (location_t loc, bool primary) const;
216
217 /* Precision-of-wording vfunc for describing a critical state change
218 within the diagnostic_path.
219
220 For example, a double-free diagnostic might use the descriptions:
221 - "first 'free' happens here"
222 - "second 'free' happens here"
223 for the pertinent events, whereas a use-after-free might use the
224 descriptions:
225 - "freed here"
226 - "use after free here"
227 Note how in both cases the first event is a "free": the best
228 description to use depends on the diagnostic. */
229
230 virtual label_text describe_state_change (const evdesc::state_change &)
231 {
232 /* Default no-op implementation. */
233 return label_text ();
234 }
235
236 /* Vfunc for implementing diagnostic_event::get_meaning for
237 state_change_event. */
238 virtual diagnostic_event::meaning
239 get_meaning_for_state_change (const evdesc::state_change &) const
240 {
241 /* Default no-op implementation. */
242 return diagnostic_event::meaning ();
243 }
244
245 /* Precision-of-wording vfunc for describing an interprocedural call
246 carrying critial state for the diagnostic, from caller to callee.
247
248 For example a double-free diagnostic might use:
249 - "passing freed pointer 'ptr' in call to 'deallocator' from 'test'"
250 to make it clearer how the freed value moves from caller to
251 callee. */
252
253 virtual label_text describe_call_with_state (const evdesc::call_with_state &)
254 {
255 /* Default no-op implementation. */
256 return label_text ();
257 }
258
259 /* Precision-of-wording vfunc for describing an interprocedural return
260 within the diagnostic_path that carries critial state for the
261 diagnostic, from callee back to caller.
262
263 For example, a deref-of-unchecked-malloc diagnostic might use:
264 - "returning possibly-NULL pointer to 'make_obj' from 'allocator'"
265 to make it clearer how the unchecked value moves from callee
266 back to caller. */
267
268 virtual label_text describe_return_of_state (const evdesc::return_of_state &)
269 {
270 /* Default no-op implementation. */
271 return label_text ();
272 }
273
274 /* Precision-of-wording vfunc for describing the final event within a
275 diagnostic_path.
276
277 For example a double-free diagnostic might use:
278 - "second 'free' here; first 'free' was at (3)"
279 and a use-after-free might use
280 - "use after 'free' here; memory was freed at (2)". */
281
282 virtual label_text describe_final_event (const evdesc::final_event &)
283 {
284 /* Default no-op implementation. */
285 return label_text ();
286 }
287
288 /* End of precision-of-wording vfuncs. */
289
290 /* Vfunc for adding a function_entry_event to a checker_path, so that e.g.
291 the infinite recursion diagnostic can add a custom event subclass
292 that annotates recursively entering a function. */
293
294 virtual void
295 add_function_entry_event (const exploded_edge &eedge,
296 checker_path *emission_path);
297
298 /* Vfunc for extending/overriding creation of the events for an
299 exploded_edge that corresponds to a superedge, allowing for custom
300 events to be created that are pertinent to a particular
301 pending_diagnostic subclass.
302
303 For example, the -Wanalyzer-stale-setjmp-buffer diagnostic adds a
304 custom event showing when the pertinent stack frame is popped
305 (and thus the point at which the jmp_buf becomes invalid). */
306
307 virtual bool maybe_add_custom_events_for_superedge (const exploded_edge &,
308 checker_path *)
309 {
310 return false;
311 }
312
313 /* Vfunc for adding a call_event to a checker_path, so that e.g.
314 the varargs diagnostics can add a custom event subclass that annotates
315 the variadic arguments. */
316 virtual void add_call_event (const exploded_edge &,
317 checker_path *);
318
319 /* Vfunc for adding any events for the creation of regions identified
320 by the mark_interesting_stuff vfunc.
321 See the comment for class region_creation_event. */
322 virtual void add_region_creation_events (const region *reg,
323 tree capacity,
324 const event_loc_info &loc_info,
325 checker_path &emission_path);
326
327 /* Vfunc for adding the final warning_event to a checker_path, so that e.g.
328 the infinite recursion diagnostic can have its diagnostic appear at
329 the callsite, but the final event in the path be at the entrypoint
330 of the called function. */
331 virtual void add_final_event (const state_machine *sm,
332 const exploded_node *enode,
333 const gimple *stmt,
334 tree var, state_machine::state_t state,
335 checker_path *emission_path);
336
337 /* Vfunc for determining that this pending_diagnostic supercedes OTHER,
338 and that OTHER should therefore not be emitted.
339 They have already been tested for being at the same stmt. */
340
341 virtual bool
342 supercedes_p (const pending_diagnostic &other ATTRIBUTE_UNUSED) const
343 {
344 return false;
345 }
346
347 /* Vfunc for registering additional information of interest to this
348 diagnostic. */
349
350 virtual void mark_interesting_stuff (interesting_t *)
351 {
352 /* Default no-op implementation. */
353 }
354
355 /* Vfunc to give diagnostic subclasses the opportunity to reject diagnostics
356 by imposing their own additional feasibility checks on the path to a
357 given feasible_node. */
358 virtual bool check_valid_fpath_p (const feasible_node &,
359 const gimple *) const
360 {
361 /* Default implementation: accept this path. */
362 return true;
363 }
364 };
365
366 /* A template to make it easier to make subclasses of pending_diagnostic.
367
368 This uses the curiously-recurring template pattern, to implement
369 pending_diagnostic::subclass_equal_p by casting and calling
370 the operator==
371
372 This assumes that BASE_OTHER has already been checked to have
373 been of the same subclass (which pending_diagnostic::equal_p does). */
374
375 template <class Subclass>
376 class pending_diagnostic_subclass : public pending_diagnostic
377 {
378 public:
379 bool subclass_equal_p (const pending_diagnostic &base_other) const
380 final override
381 {
382 const Subclass &other = (const Subclass &)base_other;
383 return *(const Subclass*)this == other;
384 }
385 };
386
387 /* An abstract base class for capturing additional notes that are to be
388 emitted with a diagnostic. */
389
390 class pending_note
391 {
392 public:
393 virtual ~pending_note () {}
394
395 /* Hand-coded RTTI: get an ID for the subclass. */
396 virtual const char *get_kind () const = 0;
397
398 /* Vfunc for emitting the note. */
399 virtual void emit () const = 0;
400
401 bool equal_p (const pending_note &other) const
402 {
403 /* Check for pointer equality on the IDs from get_kind. */
404 if (get_kind () != other.get_kind ())
405 return false;
406 /* Call vfunc now we know they have the same ID: */
407 return subclass_equal_p (other);
408 }
409
410 /* A vfunc for testing for equality, where we've already
411 checked they have the same ID. See pending_note_subclass
412 below for a convenience subclass for implementing this. */
413 virtual bool subclass_equal_p (const pending_note &other) const = 0;
414 };
415
416 /* Analogous to pending_diagnostic_subclass, but for pending_note. */
417
418 template <class Subclass>
419 class pending_note_subclass : public pending_note
420 {
421 public:
422 bool subclass_equal_p (const pending_note &base_other) const
423 final override
424 {
425 const Subclass &other = (const Subclass &)base_other;
426 return *(const Subclass*)this == other;
427 }
428 };
429
430 } // namespace ana
431
432 #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */