1 /* Classes for analyzer diagnostics. 2 Copyright (C) 2019-2022 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 namespace ana { 25 26 /* A bundle of information about things that are of interest to a 27 pending_diagnostic. 28 29 For now, merely the set of regions that are pertinent to the 30 diagnostic, so that we can notify the user about when they 31 were created. */ 32 33 struct interesting_t 34 { 35 void add_region_creation (const region *reg); 36 37 void dump_to_pp (pretty_printer *pp, bool simple) const; 38 39 auto_vec<const region *> m_region_creation; 40 }; 41 42 /* Various bundles of information used for generating more precise 43 messages for events within a diagnostic_path, for passing to the 44 various "describe_*" vfuncs of pending_diagnostic. See those 45 for more information. */ 46 47 namespace evdesc { 48 49 struct event_desc 50 { event_descevent_desc51 event_desc (bool colorize) : m_colorize (colorize) {} 52 53 label_text formatted_print (const char *fmt, ...) const 54 ATTRIBUTE_GCC_DIAG(2,3); 55 56 bool m_colorize; 57 }; 58 59 /* For use by pending_diagnostic::describe_state_change. */ 60 61 struct state_change : public event_desc 62 { state_changestate_change63 state_change (bool colorize, 64 tree expr, 65 tree origin, 66 state_machine::state_t old_state, 67 state_machine::state_t new_state, 68 diagnostic_event_id_t event_id, 69 const state_change_event &event) 70 : event_desc (colorize), 71 m_expr (expr), m_origin (origin), 72 m_old_state (old_state), m_new_state (new_state), 73 m_event_id (event_id), m_event (event) 74 {} 75 is_global_pstate_change76 bool is_global_p () const { return m_expr == NULL_TREE; } 77 78 tree m_expr; 79 tree m_origin; 80 state_machine::state_t m_old_state; 81 state_machine::state_t m_new_state; 82 diagnostic_event_id_t m_event_id; 83 const state_change_event &m_event; 84 }; 85 86 /* For use by pending_diagnostic::describe_call_with_state. */ 87 88 struct call_with_state : public event_desc 89 { call_with_statecall_with_state90 call_with_state (bool colorize, 91 tree caller_fndecl, tree callee_fndecl, 92 tree expr, state_machine::state_t state) 93 : event_desc (colorize), 94 m_caller_fndecl (caller_fndecl), 95 m_callee_fndecl (callee_fndecl), 96 m_expr (expr), 97 m_state (state) 98 { 99 } 100 101 tree m_caller_fndecl; 102 tree m_callee_fndecl; 103 tree m_expr; 104 state_machine::state_t m_state; 105 }; 106 107 /* For use by pending_diagnostic::describe_return_of_state. */ 108 109 struct return_of_state : public event_desc 110 { return_of_statereturn_of_state111 return_of_state (bool colorize, 112 tree caller_fndecl, tree callee_fndecl, 113 state_machine::state_t state) 114 : event_desc (colorize), 115 m_caller_fndecl (caller_fndecl), 116 m_callee_fndecl (callee_fndecl), 117 m_state (state) 118 { 119 } 120 121 tree m_caller_fndecl; 122 tree m_callee_fndecl; 123 state_machine::state_t m_state; 124 }; 125 126 /* For use by pending_diagnostic::describe_final_event. */ 127 128 struct final_event : public event_desc 129 { final_eventfinal_event130 final_event (bool colorize, 131 tree expr, state_machine::state_t state) 132 : event_desc (colorize), 133 m_expr (expr), m_state (state) 134 {} 135 136 tree m_expr; 137 state_machine::state_t m_state; 138 }; 139 140 } /* end of namespace evdesc */ 141 142 /* An abstract base class for capturing information about a diagnostic in 143 a form that is ready to emit at a later point (or be rejected). 144 Each kind of diagnostic will have a concrete subclass of 145 pending_diagnostic. 146 147 Normally, gcc diagnostics are emitted using va_list, which can't be 148 portably stored for later use, so we have to use an "emit" virtual 149 function. 150 151 This class also supports comparison, so that multiple pending_diagnostic 152 instances can be de-duplicated. 153 154 As well as emitting a diagnostic, the class has various "precision of 155 wording" virtual functions, for generating descriptions for events 156 within a diagnostic_path. These are optional, but implementing these 157 allows for more precise wordings than the more generic 158 implementation. */ 159 160 class pending_diagnostic 161 { 162 public: ~pending_diagnostic()163 virtual ~pending_diagnostic () {} 164 165 /* Vfunc to get the command-line option used when emitting the diagnostic, 166 or zero if there is none. 167 Used by diagnostic_manager for early rejection of diagnostics (to avoid 168 having to generate feasible execution paths for them). */ 169 virtual int get_controlling_option () const = 0; 170 171 /* Vfunc for emitting the diagnostic. The rich_location will have been 172 populated with a diagnostic_path. 173 Return true if a diagnostic is actually emitted. */ 174 virtual bool emit (rich_location *) = 0; 175 176 /* Hand-coded RTTI: get an ID for the subclass. */ 177 virtual const char *get_kind () const = 0; 178 179 /* A vfunc for identifying "use of uninitialized value". */ use_of_uninit_p()180 virtual bool use_of_uninit_p () const { return false; } 181 182 /* Compare for equality with OTHER, which might be of a different 183 subclass. */ 184 equal_p(const pending_diagnostic & other)185 bool equal_p (const pending_diagnostic &other) const 186 { 187 /* Check for pointer equality on the IDs from get_kind. */ 188 if (get_kind () != other.get_kind ()) 189 return false; 190 /* Call vfunc now we know they have the same ID: */ 191 return subclass_equal_p (other); 192 } 193 194 /* A vfunc for testing for equality, where we've already 195 checked they have the same ID. See pending_diagnostic_subclass 196 below for a convenience subclass for implementing this. */ 197 virtual bool subclass_equal_p (const pending_diagnostic &other) const = 0; 198 199 /* Return true if T1 and T2 are "the same" for the purposes of 200 diagnostic deduplication. */ 201 static bool same_tree_p (tree t1, tree t2); 202 203 /* A vfunc for fixing up locations (both the primary location for the 204 diagnostic, and for events in their paths), e.g. to avoid unwinding 205 inside specific macros. */ fixup_location(location_t loc)206 virtual location_t fixup_location (location_t loc) const 207 { 208 return loc; 209 } 210 211 /* For greatest precision-of-wording, the various following "describe_*" 212 virtual functions give the pending diagnostic a way to describe events 213 in a diagnostic_path in terms that make sense for that diagnostic. 214 215 In each case, return a non-NULL label_text to give the event a custom 216 description; NULL otherwise (falling back on a more generic 217 description). */ 218 219 /* Precision-of-wording vfunc for describing a critical state change 220 within the diagnostic_path. 221 222 For example, a double-free diagnostic might use the descriptions: 223 - "first 'free' happens here" 224 - "second 'free' happens here" 225 for the pertinent events, whereas a use-after-free might use the 226 descriptions: 227 - "freed here" 228 - "use after free here" 229 Note how in both cases the first event is a "free": the best 230 description to use depends on the diagnostic. */ 231 describe_state_change(const evdesc::state_change &)232 virtual label_text describe_state_change (const evdesc::state_change &) 233 { 234 /* Default no-op implementation. */ 235 return label_text (); 236 } 237 238 /* Precision-of-wording vfunc for describing an interprocedural call 239 carrying critial state for the diagnostic, from caller to callee. 240 241 For example a double-free diagnostic might use: 242 - "passing freed pointer 'ptr' in call to 'deallocator' from 'test'" 243 to make it clearer how the freed value moves from caller to 244 callee. */ 245 describe_call_with_state(const evdesc::call_with_state &)246 virtual label_text describe_call_with_state (const evdesc::call_with_state &) 247 { 248 /* Default no-op implementation. */ 249 return label_text (); 250 } 251 252 /* Precision-of-wording vfunc for describing an interprocedural return 253 within the diagnostic_path that carries critial state for the 254 diagnostic, from callee back to caller. 255 256 For example, a deref-of-unchecked-malloc diagnostic might use: 257 - "returning possibly-NULL pointer to 'make_obj' from 'allocator'" 258 to make it clearer how the unchecked value moves from callee 259 back to caller. */ 260 describe_return_of_state(const evdesc::return_of_state &)261 virtual label_text describe_return_of_state (const evdesc::return_of_state &) 262 { 263 /* Default no-op implementation. */ 264 return label_text (); 265 } 266 267 /* Precision-of-wording vfunc for describing the final event within a 268 diagnostic_path. 269 270 For example a double-free diagnostic might use: 271 - "second 'free' here; first 'free' was at (3)" 272 and a use-after-free might use 273 - "use after 'free' here; memory was freed at (2)". */ 274 describe_final_event(const evdesc::final_event &)275 virtual label_text describe_final_event (const evdesc::final_event &) 276 { 277 /* Default no-op implementation. */ 278 return label_text (); 279 } 280 281 /* End of precision-of-wording vfuncs. */ 282 283 /* Vfunc for extending/overriding creation of the events for an 284 exploded_edge that corresponds to a superedge, allowing for custom 285 events to be created that are pertinent to a particular 286 pending_diagnostic subclass. 287 288 For example, the -Wanalyzer-stale-setjmp-buffer diagnostic adds a 289 custom event showing when the pertinent stack frame is popped 290 (and thus the point at which the jmp_buf becomes invalid). */ 291 maybe_add_custom_events_for_superedge(const exploded_edge &,checker_path *)292 virtual bool maybe_add_custom_events_for_superedge (const exploded_edge &, 293 checker_path *) 294 { 295 return false; 296 } 297 298 /* Vfunc for determining that this pending_diagnostic supercedes OTHER, 299 and that OTHER should therefore not be emitted. 300 They have already been tested for being at the same stmt. */ 301 302 virtual bool supercedes_p(const pending_diagnostic & other ATTRIBUTE_UNUSED)303 supercedes_p (const pending_diagnostic &other ATTRIBUTE_UNUSED) const 304 { 305 return false; 306 } 307 308 /* Vfunc for registering additional information of interest to this 309 diagnostic. */ 310 mark_interesting_stuff(interesting_t *)311 virtual void mark_interesting_stuff (interesting_t *) 312 { 313 /* Default no-op implementation. */ 314 } 315 }; 316 317 /* A template to make it easier to make subclasses of pending_diagnostic. 318 319 This uses the curiously-recurring template pattern, to implement 320 pending_diagnostic::subclass_equal_p by casting and calling 321 the operator== 322 323 This assumes that BASE_OTHER has already been checked to have 324 been of the same subclass (which pending_diagnostic::equal_p does). */ 325 326 template <class Subclass> 327 class pending_diagnostic_subclass : public pending_diagnostic 328 { 329 public: subclass_equal_p(const pending_diagnostic & base_other)330 bool subclass_equal_p (const pending_diagnostic &base_other) const 331 FINAL OVERRIDE 332 { 333 const Subclass &other = (const Subclass &)base_other; 334 return *(const Subclass*)this == other; 335 } 336 }; 337 338 /* An abstract base class for capturing additional notes that are to be 339 emitted with a diagnostic. */ 340 341 class pending_note 342 { 343 public: ~pending_note()344 virtual ~pending_note () {} 345 346 /* Hand-coded RTTI: get an ID for the subclass. */ 347 virtual const char *get_kind () const = 0; 348 349 /* Vfunc for emitting the note. */ 350 virtual void emit () const = 0; 351 equal_p(const pending_note & other)352 bool equal_p (const pending_note &other) const 353 { 354 /* Check for pointer equality on the IDs from get_kind. */ 355 if (get_kind () != other.get_kind ()) 356 return false; 357 /* Call vfunc now we know they have the same ID: */ 358 return subclass_equal_p (other); 359 } 360 361 /* A vfunc for testing for equality, where we've already 362 checked they have the same ID. See pending_note_subclass 363 below for a convenience subclass for implementing this. */ 364 virtual bool subclass_equal_p (const pending_note &other) const = 0; 365 }; 366 367 /* Analogous to pending_diagnostic_subclass, but for pending_note. */ 368 369 template <class Subclass> 370 class pending_note_subclass : public pending_note 371 { 372 public: subclass_equal_p(const pending_note & base_other)373 bool subclass_equal_p (const pending_note &base_other) const 374 FINAL OVERRIDE 375 { 376 const Subclass &other = (const Subclass &)base_other; 377 return *(const Subclass*)this == other; 378 } 379 }; 380 381 } // namespace ana 382 383 #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */ 384