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