]>
Commit | Line | Data |
---|---|---|
757bf1df | 1 | /* Classes for analyzer diagnostics. |
a945c346 | 2 | Copyright (C) 2019-2024 Free Software Foundation, Inc. |
757bf1df DM |
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 | ||
12b67d1e | 24 | #include "diagnostic-metadata.h" |
6cf276dd | 25 | #include "diagnostic-path.h" |
6341f14e | 26 | #include "analyzer/sm.h" |
6cf276dd | 27 | |
75038aa6 DM |
28 | namespace ana { |
29 | ||
00e7d024 DM |
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 | 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 | ||
757bf1df DM |
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 | 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 | 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 | : event_desc (colorize), | |
75 | m_expr (expr), m_origin (origin), | |
76 | m_old_state (old_state), m_new_state (new_state), | |
77 | m_event_id (event_id), m_event (event) | |
78 | {} | |
79 | ||
80 | 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 | call_with_state (bool colorize, | |
95 | tree caller_fndecl, tree callee_fndecl, | |
96 | tree expr, state_machine::state_t state) | |
97 | : event_desc (colorize), | |
98 | m_caller_fndecl (caller_fndecl), | |
99 | m_callee_fndecl (callee_fndecl), | |
100 | m_expr (expr), | |
101 | 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 | return_of_state (bool colorize, | |
116 | tree caller_fndecl, tree callee_fndecl, | |
117 | state_machine::state_t state) | |
118 | : event_desc (colorize), | |
119 | m_caller_fndecl (caller_fndecl), | |
120 | m_callee_fndecl (callee_fndecl), | |
121 | 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 | final_event (bool colorize, | |
0d6f7b1d DM |
135 | tree expr, state_machine::state_t state, |
136 | const warning_event &event) | |
757bf1df | 137 | : event_desc (colorize), |
0d6f7b1d | 138 | m_expr (expr), m_state (state), m_event (event) |
757bf1df DM |
139 | {} |
140 | ||
141 | tree m_expr; | |
142 | state_machine::state_t m_state; | |
0d6f7b1d | 143 | const warning_event &m_event; |
757bf1df DM |
144 | }; |
145 | ||
146 | } /* end of namespace evdesc */ | |
147 | ||
12b67d1e DM |
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 | diagnostic_emission_context (const saved_diagnostic &sd, | |
158 | rich_location &rich_loc, | |
159 | diagnostic_metadata &metadata, | |
160 | logger *logger) | |
161 | : m_sd (sd), | |
162 | m_rich_loc (rich_loc), | |
163 | m_metadata (metadata), | |
164 | 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 | location_t get_location () const { return m_rich_loc.get_loc (); } | |
174 | logger *get_logger () const { return m_logger; } | |
175 | ||
176 | void add_cwe (int cwe) { m_metadata.add_cwe (cwe); } | |
177 | void add_rule (const diagnostic_metadata::rule &r) | |
178 | { | |
179 | 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 | ||
757bf1df DM |
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 | class pending_diagnostic | |
208 | { | |
209 | public: | |
210 | virtual ~pending_diagnostic () {} | |
211 | ||
7fd6e36e DM |
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 | ||
8f636915 DM |
218 | /* Vfunc to give the diagnostic the chance to terminate the execution |
219 | path being explored. By default, don't terminate the path. */ | |
220 | virtual bool terminate_path_p () const { return false; } | |
221 | ||
12b67d1e | 222 | /* Vfunc for emitting the diagnostic. |
757bf1df | 223 | Return true if a diagnostic is actually emitted. */ |
12b67d1e | 224 | virtual bool emit (diagnostic_emission_context &) = 0; |
757bf1df DM |
225 | |
226 | /* Hand-coded RTTI: get an ID for the subclass. */ | |
227 | virtual const char *get_kind () const = 0; | |
228 | ||
33255ad3 DM |
229 | /* A vfunc for identifying "use of uninitialized value". */ |
230 | virtual bool use_of_uninit_p () const { return false; } | |
231 | ||
757bf1df DM |
232 | /* Compare for equality with OTHER, which might be of a different |
233 | subclass. */ | |
234 | ||
8a18261a | 235 | bool equal_p (const pending_diagnostic &other) const |
757bf1df DM |
236 | { |
237 | /* Check for pointer equality on the IDs from get_kind. */ | |
238 | if (get_kind () != other.get_kind ()) | |
239 | return false; | |
240 | /* Call vfunc now we know they have the same ID: */ | |
241 | 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 | ||
14f9d7b9 DM |
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 | ||
d777b38c DM |
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; | |
66dde7bc | 257 | |
757bf1df DM |
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 | virtual label_text describe_state_change (const evdesc::state_change &) | |
272 | { | |
273 | /* Default no-op implementation. */ | |
274 | return label_text (); | |
275 | } | |
276 | ||
6cf276dd DM |
277 | /* Vfunc for implementing diagnostic_event::get_meaning for |
278 | state_change_event. */ | |
279 | virtual diagnostic_event::meaning | |
280 | get_meaning_for_state_change (const evdesc::state_change &) const | |
281 | { | |
282 | /* Default no-op implementation. */ | |
283 | return diagnostic_event::meaning (); | |
284 | } | |
285 | ||
757bf1df DM |
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 | virtual label_text describe_call_with_state (const evdesc::call_with_state &) | |
295 | { | |
296 | /* Default no-op implementation. */ | |
297 | 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 | virtual label_text describe_return_of_state (const evdesc::return_of_state &) | |
310 | { | |
311 | /* Default no-op implementation. */ | |
312 | 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 | virtual label_text describe_final_event (const evdesc::final_event &) | |
324 | { | |
325 | /* Default no-op implementation. */ | |
326 | return label_text (); | |
327 | } | |
328 | ||
329 | /* End of precision-of-wording vfuncs. */ | |
8069928d | 330 | |
12c583a2 DM |
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 | ||
8069928d DM |
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 | virtual bool maybe_add_custom_events_for_superedge (const exploded_edge &, | |
349 | checker_path *) | |
350 | { | |
351 | return false; | |
352 | } | |
33255ad3 | 353 | |
2402dc6b DM |
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 | ||
f5758fe5 DM |
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, | |
e24fe128 | 365 | const event_loc_info &loc_info, |
f5758fe5 DM |
366 | checker_path &emission_path); |
367 | ||
12c583a2 DM |
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 | ||
33255ad3 DM |
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 | supercedes_p (const pending_diagnostic &other ATTRIBUTE_UNUSED) const | |
384 | { | |
385 | return false; | |
386 | } | |
00e7d024 DM |
387 | |
388 | /* Vfunc for registering additional information of interest to this | |
389 | diagnostic. */ | |
390 | ||
391 | virtual void mark_interesting_stuff (interesting_t *) | |
392 | { | |
393 | /* Default no-op implementation. */ | |
394 | } | |
7bffea89 DM |
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. */ | |
b03a10b0 DM |
399 | virtual bool check_valid_fpath_p (const feasible_node &, |
400 | const gimple *) const | |
7bffea89 DM |
401 | { |
402 | /* Default implementation: accept this path. */ | |
403 | return true; | |
404 | } | |
12b67d1e DM |
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 | virtual void maybe_add_sarif_properties (sarif_object &/*result_obj*/) const | |
411 | { | |
412 | /* Default no-op implementation. */ | |
413 | } | |
757bf1df DM |
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 | class pending_diagnostic_subclass : public pending_diagnostic | |
427 | { | |
428 | public: | |
429 | bool subclass_equal_p (const pending_diagnostic &base_other) const | |
ff171cb1 | 430 | final override |
757bf1df DM |
431 | { |
432 | const Subclass &other = (const Subclass &)base_other; | |
433 | return *(const Subclass*)this == other; | |
434 | } | |
435 | }; | |
436 | ||
c65d3c7f DM |
437 | /* An abstract base class for capturing additional notes that are to be |
438 | emitted with a diagnostic. */ | |
439 | ||
440 | class pending_note | |
441 | { | |
442 | public: | |
443 | 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 | bool equal_p (const pending_note &other) const | |
452 | { | |
453 | /* Check for pointer equality on the IDs from get_kind. */ | |
454 | if (get_kind () != other.get_kind ()) | |
455 | return false; | |
456 | /* Call vfunc now we know they have the same ID: */ | |
457 | 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 | class pending_note_subclass : public pending_note | |
470 | { | |
471 | public: | |
472 | bool subclass_equal_p (const pending_note &base_other) const | |
ff171cb1 | 473 | final override |
c65d3c7f DM |
474 | { |
475 | const Subclass &other = (const Subclass &)base_other; | |
476 | return *(const Subclass*)this == other; | |
477 | } | |
478 | }; | |
479 | ||
75038aa6 DM |
480 | } // namespace ana |
481 | ||
757bf1df | 482 | #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */ |