| 1 | /* Classes for analyzer diagnostics. |
| 2 | Copyright (C) 2019-2025 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 | 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 | 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 | : m_expr (expr), m_origin (origin), |
| 64 | m_old_state (old_state), m_new_state (new_state), |
| 65 | m_event_id (event_id), m_event (event) |
| 66 | {} |
| 67 | |
| 68 | 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 | call_with_state (tree caller_fndecl, tree callee_fndecl, |
| 83 | tree expr, state_machine::state_t state) |
| 84 | : m_caller_fndecl (caller_fndecl), |
| 85 | m_callee_fndecl (callee_fndecl), |
| 86 | m_expr (expr), |
| 87 | 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 | return_of_state (tree caller_fndecl, tree callee_fndecl, |
| 102 | state_machine::state_t state) |
| 103 | : m_caller_fndecl (caller_fndecl), |
| 104 | m_callee_fndecl (callee_fndecl), |
| 105 | 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 | final_event (tree expr, state_machine::state_t state, |
| 119 | const warning_event &event) |
| 120 | : 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 | diagnostic_emission_context (const saved_diagnostic &sd, |
| 140 | rich_location &rich_loc, |
| 141 | diagnostic_metadata &metadata, |
| 142 | logger *logger) |
| 143 | : m_sd (sd), |
| 144 | m_rich_loc (rich_loc), |
| 145 | m_metadata (metadata), |
| 146 | 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 | location_t get_location () const { return m_rich_loc.get_loc (); } |
| 156 | logger *get_logger () const { return m_logger; } |
| 157 | |
| 158 | void add_cwe (int cwe) { m_metadata.add_cwe (cwe); } |
| 159 | void add_rule (const diagnostic_metadata::rule &r) |
| 160 | { |
| 161 | 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 | class pending_diagnostic |
| 190 | { |
| 191 | public: |
| 192 | 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 | 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 | 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 | bool equal_p (const pending_diagnostic &other) const |
| 218 | { |
| 219 | /* Check for pointer equality on the IDs from get_kind. */ |
| 220 | if (get_kind () != other.get_kind ()) |
| 221 | return false; |
| 222 | /* Call vfunc now we know they have the same ID: */ |
| 223 | 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 | virtual bool describe_state_change (pretty_printer &, |
| 257 | const evdesc::state_change &) |
| 258 | { |
| 259 | /* Default no-op implementation. */ |
| 260 | return false; |
| 261 | } |
| 262 | |
| 263 | /* Vfunc for implementing diagnostic_event::get_meaning for |
| 264 | state_change_event. */ |
| 265 | virtual diagnostic_event::meaning |
| 266 | get_meaning_for_state_change (const evdesc::state_change &) const |
| 267 | { |
| 268 | /* Default no-op implementation. */ |
| 269 | 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 | virtual bool describe_call_with_state (pretty_printer &, |
| 281 | const evdesc::call_with_state &) |
| 282 | { |
| 283 | /* Default no-op implementation. */ |
| 284 | 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 | virtual bool describe_return_of_state (pretty_printer &, |
| 297 | const evdesc::return_of_state &) |
| 298 | { |
| 299 | /* Default no-op implementation. */ |
| 300 | 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 | virtual bool describe_final_event (pretty_printer &, |
| 312 | const evdesc::final_event &) |
| 313 | { |
| 314 | /* Default no-op implementation. */ |
| 315 | 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 | virtual bool maybe_add_custom_events_for_superedge (const exploded_edge &, |
| 338 | checker_path *) |
| 339 | { |
| 340 | 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 | supercedes_p (const pending_diagnostic &other ATTRIBUTE_UNUSED) const |
| 373 | { |
| 374 | return false; |
| 375 | } |
| 376 | |
| 377 | /* Vfunc for registering additional information of interest to this |
| 378 | diagnostic. */ |
| 379 | |
| 380 | virtual void mark_interesting_stuff (interesting_t *) |
| 381 | { |
| 382 | /* Default no-op implementation. */ |
| 383 | } |
| 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 | virtual bool check_valid_fpath_p (const feasible_node &, |
| 389 | const gimple *) const |
| 390 | { |
| 391 | /* Default implementation: accept this path. */ |
| 392 | 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 | virtual void maybe_add_sarif_properties (sarif_object &/*result_obj*/) const |
| 400 | { |
| 401 | /* Default no-op implementation. */ |
| 402 | } |
| 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 | class pending_diagnostic_subclass : public pending_diagnostic |
| 416 | { |
| 417 | public: |
| 418 | bool subclass_equal_p (const pending_diagnostic &base_other) const |
| 419 | final override |
| 420 | { |
| 421 | const Subclass &other = (const Subclass &)base_other; |
| 422 | 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 | class pending_note |
| 430 | { |
| 431 | public: |
| 432 | 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 | bool equal_p (const pending_note &other) const |
| 441 | { |
| 442 | /* Check for pointer equality on the IDs from get_kind. */ |
| 443 | if (get_kind () != other.get_kind ()) |
| 444 | return false; |
| 445 | /* Call vfunc now we know they have the same ID: */ |
| 446 | 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 | class pending_note_subclass : public pending_note |
| 459 | { |
| 460 | public: |
| 461 | bool subclass_equal_p (const pending_note &base_other) const |
| 462 | final override |
| 463 | { |
| 464 | const Subclass &other = (const Subclass &)base_other; |
| 465 | return *(const Subclass*)this == other; |
| 466 | } |
| 467 | }; |
| 468 | |
| 469 | } // namespace ana |
| 470 | |
| 471 | #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */ |
| 472 | |