1 /* Classes for analyzer diagnostics.
2 Copyright (C) 2019-2022 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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)
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.
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/>. */
21 #ifndef GCC_ANALYZER_PENDING_DIAGNOSTIC_H
22 #define GCC_ANALYZER_PENDING_DIAGNOSTIC_H
24 #include "diagnostic-path.h"
25 #include "analyzer/sm.h"
29 /* A bundle of information about things that are of interest to a
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
38 void add_region_creation (const region *reg);
40 void dump_to_pp (pretty_printer *pp, bool simple) const;
42 auto_vec<const region *> m_region_creation;
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. */
54 event_desc (bool colorize) : m_colorize (colorize) {}
56 label_text formatted_print (const char *fmt, ...) const
57 ATTRIBUTE_GCC_DIAG(2,3);
62 /* For use by pending_diagnostic::describe_state_change. */
64 struct state_change : public event_desc
66 state_change (bool colorize,
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)
79 bool is_global_p () const { return m_expr == NULL_TREE; }
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;
89 /* For use by pending_diagnostic::describe_call_with_state. */
91 struct call_with_state : public event_desc
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),
104 tree m_caller_fndecl;
105 tree m_callee_fndecl;
107 state_machine::state_t m_state;
110 /* For use by pending_diagnostic::describe_return_of_state. */
112 struct return_of_state : public event_desc
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),
124 tree m_caller_fndecl;
125 tree m_callee_fndecl;
126 state_machine::state_t m_state;
129 /* For use by pending_diagnostic::describe_final_event. */
131 struct final_event : public event_desc
133 final_event (bool colorize,
134 tree expr, state_machine::state_t state)
135 : event_desc (colorize),
136 m_expr (expr), m_state (state)
140 state_machine::state_t m_state;
143 } /* end of namespace evdesc */
145 /* An abstract base class for capturing information about a diagnostic in
146 a form that is ready to emit at a later point (or be rejected).
147 Each kind of diagnostic will have a concrete subclass of
150 Normally, gcc diagnostics are emitted using va_list, which can't be
151 portably stored for later use, so we have to use an "emit" virtual
154 This class also supports comparison, so that multiple pending_diagnostic
155 instances can be de-duplicated.
157 As well as emitting a diagnostic, the class has various "precision of
158 wording" virtual functions, for generating descriptions for events
159 within a diagnostic_path. These are optional, but implementing these
160 allows for more precise wordings than the more generic
163 class pending_diagnostic
166 virtual ~pending_diagnostic () {}
168 /* Vfunc to get the command-line option used when emitting the diagnostic,
169 or zero if there is none.
170 Used by diagnostic_manager for early rejection of diagnostics (to avoid
171 having to generate feasible execution paths for them). */
172 virtual int get_controlling_option () const = 0;
174 /* Vfunc for emitting the diagnostic. The rich_location will have been
175 populated with a diagnostic_path.
176 Return true if a diagnostic is actually emitted. */
177 virtual bool emit (rich_location *) = 0;
179 /* Hand-coded RTTI: get an ID for the subclass. */
180 virtual const char *get_kind () const = 0;
182 /* A vfunc for identifying "use of uninitialized value". */
183 virtual bool use_of_uninit_p () const { return false; }
185 /* Compare for equality with OTHER, which might be of a different
188 bool equal_p (const pending_diagnostic &other) const
190 /* Check for pointer equality on the IDs from get_kind. */
191 if (get_kind () != other.get_kind ())
193 /* Call vfunc now we know they have the same ID: */
194 return subclass_equal_p (other);
197 /* A vfunc for testing for equality, where we've already
198 checked they have the same ID. See pending_diagnostic_subclass
199 below for a convenience subclass for implementing this. */
200 virtual bool subclass_equal_p (const pending_diagnostic &other) const = 0;
202 /* Return true if T1 and T2 are "the same" for the purposes of
203 diagnostic deduplication. */
204 static bool same_tree_p (tree t1, tree t2);
206 /* Vfunc for fixing up locations, e.g. to avoid unwinding
207 inside specific macros. PRIMARY is true for the primary location
208 for the diagnostic, and FALSE for events in their paths. */
209 virtual location_t fixup_location (location_t loc, bool primary) const;
211 /* Precision-of-wording vfunc for describing a critical state change
212 within the diagnostic_path.
214 For example, a double-free diagnostic might use the descriptions:
215 - "first 'free' happens here"
216 - "second 'free' happens here"
217 for the pertinent events, whereas a use-after-free might use the
220 - "use after free here"
221 Note how in both cases the first event is a "free": the best
222 description to use depends on the diagnostic. */
224 virtual label_text describe_state_change (const evdesc::state_change &)
226 /* Default no-op implementation. */
227 return label_text ();
230 /* Vfunc for implementing diagnostic_event::get_meaning for
231 state_change_event. */
232 virtual diagnostic_event::meaning
233 get_meaning_for_state_change (const evdesc::state_change &) const
235 /* Default no-op implementation. */
236 return diagnostic_event::meaning ();
239 /* Precision-of-wording vfunc for describing an interprocedural call
240 carrying critial state for the diagnostic, from caller to callee.
242 For example a double-free diagnostic might use:
243 - "passing freed pointer 'ptr' in call to 'deallocator' from 'test'"
244 to make it clearer how the freed value moves from caller to
247 virtual label_text describe_call_with_state (const evdesc::call_with_state &)
249 /* Default no-op implementation. */
250 return label_text ();
253 /* Precision-of-wording vfunc for describing an interprocedural return
254 within the diagnostic_path that carries critial state for the
255 diagnostic, from callee back to caller.
257 For example, a deref-of-unchecked-malloc diagnostic might use:
258 - "returning possibly-NULL pointer to 'make_obj' from 'allocator'"
259 to make it clearer how the unchecked value moves from callee
262 virtual label_text describe_return_of_state (const evdesc::return_of_state &)
264 /* Default no-op implementation. */
265 return label_text ();
268 /* Precision-of-wording vfunc for describing the final event within a
271 For example a double-free diagnostic might use:
272 - "second 'free' here; first 'free' was at (3)"
273 and a use-after-free might use
274 - "use after 'free' here; memory was freed at (2)". */
276 virtual label_text describe_final_event (const evdesc::final_event &)
278 /* Default no-op implementation. */
279 return label_text ();
282 /* End of precision-of-wording vfuncs. */
284 /* Vfunc for adding a function_entry_event to a checker_path, so that e.g.
285 the infinite recursion diagnostic can add a custom event subclass
286 that annotates recursively entering a function. */
289 add_function_entry_event (const exploded_edge &eedge,
290 checker_path *emission_path);
292 /* Vfunc for extending/overriding creation of the events for an
293 exploded_edge that corresponds to a superedge, allowing for custom
294 events to be created that are pertinent to a particular
295 pending_diagnostic subclass.
297 For example, the -Wanalyzer-stale-setjmp-buffer diagnostic adds a
298 custom event showing when the pertinent stack frame is popped
299 (and thus the point at which the jmp_buf becomes invalid). */
301 virtual bool maybe_add_custom_events_for_superedge (const exploded_edge &,
307 /* Vfunc for adding a call_event to a checker_path, so that e.g.
308 the varargs diagnostics can add a custom event subclass that annotates
309 the variadic arguments. */
310 virtual void add_call_event (const exploded_edge &,
313 /* Vfunc for adding any events for the creation of regions identified
314 by the mark_interesting_stuff vfunc.
315 See the comment for class region_creation_event. */
316 virtual void add_region_creation_events (const region *reg,
318 const event_loc_info &loc_info,
319 checker_path &emission_path);
321 /* Vfunc for adding the final warning_event to a checker_path, so that e.g.
322 the infinite recursion diagnostic can have its diagnostic appear at
323 the callsite, but the final event in the path be at the entrypoint
324 of the called function. */
325 virtual void add_final_event (const state_machine *sm,
326 const exploded_node *enode,
328 tree var, state_machine::state_t state,
329 checker_path *emission_path);
331 /* Vfunc for determining that this pending_diagnostic supercedes OTHER,
332 and that OTHER should therefore not be emitted.
333 They have already been tested for being at the same stmt. */
336 supercedes_p (const pending_diagnostic &other ATTRIBUTE_UNUSED) const
341 /* Vfunc for registering additional information of interest to this
344 virtual void mark_interesting_stuff (interesting_t *)
346 /* Default no-op implementation. */
350 /* A template to make it easier to make subclasses of pending_diagnostic.
352 This uses the curiously-recurring template pattern, to implement
353 pending_diagnostic::subclass_equal_p by casting and calling
356 This assumes that BASE_OTHER has already been checked to have
357 been of the same subclass (which pending_diagnostic::equal_p does). */
359 template <class Subclass>
360 class pending_diagnostic_subclass : public pending_diagnostic
363 bool subclass_equal_p (const pending_diagnostic &base_other) const
366 const Subclass &other = (const Subclass &)base_other;
367 return *(const Subclass*)this == other;
371 /* An abstract base class for capturing additional notes that are to be
372 emitted with a diagnostic. */
377 virtual ~pending_note () {}
379 /* Hand-coded RTTI: get an ID for the subclass. */
380 virtual const char *get_kind () const = 0;
382 /* Vfunc for emitting the note. */
383 virtual void emit () const = 0;
385 bool equal_p (const pending_note &other) const
387 /* Check for pointer equality on the IDs from get_kind. */
388 if (get_kind () != other.get_kind ())
390 /* Call vfunc now we know they have the same ID: */
391 return subclass_equal_p (other);
394 /* A vfunc for testing for equality, where we've already
395 checked they have the same ID. See pending_note_subclass
396 below for a convenience subclass for implementing this. */
397 virtual bool subclass_equal_p (const pending_note &other) const = 0;
400 /* Analogous to pending_diagnostic_subclass, but for pending_note. */
402 template <class Subclass>
403 class pending_note_subclass : public pending_note
406 bool subclass_equal_p (const pending_note &base_other) const
409 const Subclass &other = (const Subclass &)base_other;
410 return *(const Subclass*)this == other;
416 #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */