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