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