1/* Classes for analyzer diagnostics.
2 Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along 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
28namespace 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
37struct 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
51namespace evdesc {
52
53struct 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
65struct 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
92struct 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
113struct 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
132struct final_event : public event_desc
133{
134 final_event (bool colorize,
135 tree expr, state_machine::state_t state,
136 const warning_event &event)
137 : event_desc (colorize),
138 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
154class diagnostic_emission_context
155{
156public:
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
182private:
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
207class pending_diagnostic
208{
209 public:
210 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 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 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 bool equal_p (const pending_diagnostic &other) const
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
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 virtual label_text describe_state_change (const evdesc::state_change &)
272 {
273 /* Default no-op implementation. */
274 return label_text ();
275 }
276
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
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. */
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 virtual bool maybe_add_custom_events_for_superedge (const exploded_edge &,
349 checker_path *)
350 {
351 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 supercedes_p (const pending_diagnostic &other ATTRIBUTE_UNUSED) const
384 {
385 return false;
386 }
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 }
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 virtual bool check_valid_fpath_p (const feasible_node &,
400 const gimple *) const
401 {
402 /* Default implementation: accept this path. */
403 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 virtual void maybe_add_sarif_properties (sarif_object &/*result_obj*/) const
411 {
412 /* Default no-op implementation. */
413 }
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
425template <class Subclass>
426class pending_diagnostic_subclass : public pending_diagnostic
427{
428 public:
429 bool subclass_equal_p (const pending_diagnostic &base_other) const
430 final override
431 {
432 const Subclass &other = (const Subclass &)base_other;
433 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
440class pending_note
441{
442public:
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
468template <class Subclass>
469class pending_note_subclass : public pending_note
470{
471 public:
472 bool subclass_equal_p (const pending_note &base_other) const
473 final override
474 {
475 const Subclass &other = (const Subclass &)base_other;
476 return *(const Subclass*)this == other;
477 }
478};
479
480} // namespace ana
481
482#endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */
483

source code of gcc/analyzer/pending-diagnostic.h