Branch data Line data Source code
1 : : /* Classes for analyzer diagnostics.
2 : : Copyright (C) 2019-2025 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 : 7966 : 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 : 4868 : 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 : 4868 : : m_expr (expr), m_origin (origin),
63 : 4868 : m_old_state (old_state), m_new_state (new_state),
64 : 4868 : 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 : 351 : call_with_state (tree caller_fndecl, tree callee_fndecl,
82 : : tree expr, state_machine::state_t state)
83 : 351 : : m_caller_fndecl (caller_fndecl),
84 : 351 : m_callee_fndecl (callee_fndecl),
85 : 351 : m_expr (expr),
86 : 351 : 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 : 104 : return_of_state (tree caller_fndecl, tree callee_fndecl,
101 : : state_machine::state_t state)
102 : 104 : : m_caller_fndecl (caller_fndecl),
103 : 104 : m_callee_fndecl (callee_fndecl),
104 : 104 : 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 : 7900 : final_event (tree expr, state_machine::state_t state,
118 : : const warning_event &event)
119 : 7900 : : 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 : 3983 : diagnostic_emission_context (const saved_diagnostic &sd,
139 : : rich_location &rich_loc,
140 : : diagnostics::metadata &metadata,
141 : : logger *logger)
142 : 3983 : : m_sd (sd),
143 : 3983 : m_rich_loc (rich_loc),
144 : 3983 : m_metadata (metadata),
145 : 3983 : 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 : 946 : location_t get_location () const { return m_rich_loc.get_loc (); }
155 : 555 : logger *get_logger () const { return m_logger; }
156 : :
157 : 3311 : 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 : 3934 : 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 : 7390 : 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 : 209 : 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 : 6386 : bool equal_p (const pending_diagnostic &other) const
217 : : {
218 : : /* Check for pointer equality on the IDs from get_kind. */
219 : 6386 : if (get_kind () != other.get_kind ())
220 : : return false;
221 : : /* Call vfunc now we know they have the same ID: */
222 : 6329 : 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 : 49 : virtual bool describe_call_with_state (pretty_printer &,
280 : : const evdesc::call_with_state &)
281 : : {
282 : : /* Default no-op implementation. */
283 : 49 : 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 : 54 : virtual bool describe_return_of_state (pretty_printer &,
296 : : const evdesc::return_of_state &)
297 : : {
298 : : /* Default no-op implementation. */
299 : 54 : 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 : 576 : virtual bool describe_final_event (pretty_printer &,
311 : : const evdesc::final_event &)
312 : : {
313 : : /* Default no-op implementation. */
314 : 576 : 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 that corresponds to a superedge, allowing for custom
329 : : events to be created that are pertinent to a particular
330 : : pending_diagnostic subclass.
331 : :
332 : : For example, the -Wanalyzer-stale-setjmp-buffer diagnostic adds a
333 : : custom event showing when the pertinent stack frame is popped
334 : : (and thus the point at which the jmp_buf becomes invalid). */
335 : :
336 : 13967 : virtual bool maybe_add_custom_events_for_superedge (const exploded_edge &,
337 : : checker_path *)
338 : : {
339 : 13967 : return false;
340 : : }
341 : :
342 : : /* Vfunc for adding a call_event to a checker_path, so that e.g.
343 : : the varargs diagnostics can add a custom event subclass that annotates
344 : : the variadic arguments. */
345 : : virtual void add_call_event (const exploded_edge &,
346 : : checker_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 : 2917 : get_final_state () const
368 : : {
369 : 2917 : 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 : 5048 : supercedes_p (const pending_diagnostic &other ATTRIBUTE_UNUSED) const
378 : : {
379 : 5048 : return false;
380 : : }
381 : :
382 : : /* Vfunc for registering additional information of interest to this
383 : : diagnostic. */
384 : :
385 : 2657 : virtual void mark_interesting_stuff (interesting_t *)
386 : : {
387 : : /* Default no-op implementation. */
388 : 2657 : }
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 : 4898 : virtual bool check_valid_fpath_p (const feasible_node &,
394 : : const gimple *) const
395 : : {
396 : : /* Default implementation: accept this path. */
397 : 4898 : return true;
398 : : }
399 : :
400 : : /* Vfunc for use in SARIF output to give pending_diagnostic subclasses
401 : : the opportunity to add diagnostic-specific properties to the SARIF
402 : : "result" object for the diagnostic.
403 : : This is intended for use when debugging a diagnostic. */
404 : : virtual void
405 : 11 : maybe_add_sarif_properties (diagnostics::sarif_object &/*result_obj*/) const
406 : : {
407 : : /* Default no-op implementation. */
408 : 11 : }
409 : : };
410 : :
411 : : /* A template to make it easier to make subclasses of pending_diagnostic.
412 : :
413 : : This uses the curiously-recurring template pattern, to implement
414 : : pending_diagnostic::subclass_equal_p by casting and calling
415 : : the operator==
416 : :
417 : : This assumes that BASE_OTHER has already been checked to have
418 : : been of the same subclass (which pending_diagnostic::equal_p does). */
419 : :
420 : : template <class Subclass>
421 : 1196 : class pending_diagnostic_subclass : public pending_diagnostic
422 : : {
423 : : public:
424 : 2198 : bool subclass_equal_p (const pending_diagnostic &base_other) const
425 : : final override
426 : : {
427 : 2198 : const Subclass &other = (const Subclass &)base_other;
428 : 2198 : return *(const Subclass*)this == other;
429 : : }
430 : : };
431 : :
432 : : /* An abstract base class for capturing additional notes that are to be
433 : : emitted with a diagnostic. */
434 : :
435 : 224 : class pending_note
436 : : {
437 : : public:
438 : 0 : virtual ~pending_note () {}
439 : :
440 : : /* Hand-coded RTTI: get an ID for the subclass. */
441 : : virtual const char *get_kind () const = 0;
442 : :
443 : : /* Vfunc for emitting the note. */
444 : : virtual void emit () const = 0;
445 : :
446 : 802 : bool equal_p (const pending_note &other) const
447 : : {
448 : : /* Check for pointer equality on the IDs from get_kind. */
449 : 802 : if (get_kind () != other.get_kind ())
450 : : return false;
451 : : /* Call vfunc now we know they have the same ID: */
452 : 802 : return subclass_equal_p (other);
453 : : }
454 : :
455 : : /* A vfunc for testing for equality, where we've already
456 : : checked they have the same ID. See pending_note_subclass
457 : : below for a convenience subclass for implementing this. */
458 : : virtual bool subclass_equal_p (const pending_note &other) const = 0;
459 : : };
460 : :
461 : : /* Analogous to pending_diagnostic_subclass, but for pending_note. */
462 : :
463 : : template <class Subclass>
464 : 22 : class pending_note_subclass : public pending_note
465 : : {
466 : : public:
467 : 802 : bool subclass_equal_p (const pending_note &base_other) const
468 : : final override
469 : : {
470 : 802 : const Subclass &other = (const Subclass &)base_other;
471 : 802 : return *(const Subclass*)this == other;
472 : : }
473 : : };
474 : :
475 : : } // namespace ana
476 : :
477 : : #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */
|