1 | /* Subclasses of diagnostic_event for analyzer diagnostics. |
2 | Copyright (C) 2019-2024 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 | #include "config.h" |
22 | #define INCLUDE_MEMORY |
23 | #include "system.h" |
24 | #include "coretypes.h" |
25 | #include "tree.h" |
26 | #include "function.h" |
27 | #include "basic-block.h" |
28 | #include "gimple.h" |
29 | #include "diagnostic-core.h" |
30 | #include "gimple-pretty-print.h" |
31 | #include "fold-const.h" |
32 | #include "diagnostic-path.h" |
33 | #include "options.h" |
34 | #include "cgraph.h" |
35 | #include "cfg.h" |
36 | #include "digraph.h" |
37 | #include "diagnostic-event-id.h" |
38 | #include "analyzer/analyzer.h" |
39 | #include "analyzer/analyzer-logging.h" |
40 | #include "analyzer/sm.h" |
41 | #include "sbitmap.h" |
42 | #include "bitmap.h" |
43 | #include "ordered-hash-map.h" |
44 | #include "analyzer/call-string.h" |
45 | #include "analyzer/program-point.h" |
46 | #include "analyzer/store.h" |
47 | #include "analyzer/region-model.h" |
48 | #include "analyzer/program-state.h" |
49 | #include "analyzer/checker-path.h" |
50 | #include "gimple-iterator.h" |
51 | #include "inlining-iterator.h" |
52 | #include "analyzer/supergraph.h" |
53 | #include "analyzer/pending-diagnostic.h" |
54 | #include "analyzer/diagnostic-manager.h" |
55 | #include "analyzer/constraint-manager.h" |
56 | #include "analyzer/checker-event.h" |
57 | #include "analyzer/exploded-graph.h" |
58 | #include "diagnostic-format-sarif.h" |
59 | #include "tree-logical-location.h" |
60 | |
61 | #if ENABLE_ANALYZER |
62 | |
63 | namespace ana { |
64 | |
65 | /* Get a string for EK. */ |
66 | |
67 | const char * |
68 | event_kind_to_string (enum event_kind ek) |
69 | { |
70 | switch (ek) |
71 | { |
72 | default: |
73 | gcc_unreachable (); |
74 | case EK_DEBUG: |
75 | return "EK_DEBUG" ; |
76 | case EK_CUSTOM: |
77 | return "EK_CUSTOM" ; |
78 | case EK_STMT: |
79 | return "EK_STMT" ; |
80 | case EK_REGION_CREATION: |
81 | return "EK_REGION_CREATION" ; |
82 | case EK_FUNCTION_ENTRY: |
83 | return "EK_FUNCTION_ENTRY" ; |
84 | case EK_STATE_CHANGE: |
85 | return "EK_STATE_CHANGE" ; |
86 | case EK_START_CFG_EDGE: |
87 | return "EK_START_CFG_EDGE" ; |
88 | case EK_END_CFG_EDGE: |
89 | return "EK_END_CFG_EDGE" ; |
90 | case EK_CALL_EDGE: |
91 | return "EK_CALL_EDGE" ; |
92 | case EK_RETURN_EDGE: |
93 | return "EK_RETURN_EDGE" ; |
94 | case EK_START_CONSOLIDATED_CFG_EDGES: |
95 | return "EK_START_CONSOLIDATED_CFG_EDGES" ; |
96 | case EK_END_CONSOLIDATED_CFG_EDGES: |
97 | return "EK_END_CONSOLIDATED_CFG_EDGES" ; |
98 | case EK_INLINED_CALL: |
99 | return "EK_INLINED_CALL" ; |
100 | case EK_SETJMP: |
101 | return "EK_SETJMP" ; |
102 | case EK_REWIND_FROM_LONGJMP: |
103 | return "EK_REWIND_FROM_LONGJMP" ; |
104 | case EK_REWIND_TO_SETJMP: |
105 | return "EK_REWIND_TO_SETJMP" ; |
106 | case EK_WARNING: |
107 | return "EK_WARNING" ; |
108 | } |
109 | } |
110 | |
111 | /* class checker_event : public diagnostic_event. */ |
112 | |
113 | /* checker_event's ctor. */ |
114 | |
115 | checker_event::checker_event (enum event_kind kind, |
116 | const event_loc_info &loc_info) |
117 | : m_kind (kind), m_loc (loc_info.m_loc), |
118 | m_original_fndecl (loc_info.m_fndecl), |
119 | m_effective_fndecl (loc_info.m_fndecl), |
120 | m_original_depth (loc_info.m_depth), |
121 | m_effective_depth (loc_info.m_depth), |
122 | m_pending_diagnostic (NULL), m_emission_id (), |
123 | m_logical_loc (loc_info.m_fndecl) |
124 | { |
125 | /* Update effective fndecl and depth if inlining has been recorded. */ |
126 | if (flag_analyzer_undo_inlining) |
127 | { |
128 | inlining_info info (m_loc); |
129 | if (info.get_inner_fndecl ()) |
130 | { |
131 | m_effective_fndecl = info.get_inner_fndecl (); |
132 | m_effective_depth += info.get_extra_frames (); |
133 | m_logical_loc = tree_logical_location (m_effective_fndecl); |
134 | } |
135 | } |
136 | } |
137 | |
138 | /* No-op implementation of diagnostic_event::get_meaning vfunc for |
139 | checker_event: checker events have no meaning by default. */ |
140 | |
141 | diagnostic_event::meaning |
142 | checker_event::get_meaning () const |
143 | { |
144 | return meaning (); |
145 | } |
146 | |
147 | /* Implementation of diagnostic_event::maybe_add_sarif_properties |
148 | for checker_event. */ |
149 | |
150 | void |
151 | checker_event:: |
152 | maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) const |
153 | { |
154 | sarif_property_bag &props = thread_flow_loc_obj.get_or_create_properties (); |
155 | #define PROPERTY_PREFIX "gcc/analyzer/checker_event/" |
156 | props.set (PROPERTY_PREFIX "emission_id" , |
157 | v: diagnostic_event_id_to_json (m_emission_id)); |
158 | props.set_string (PROPERTY_PREFIX "kind" , utf8_value: event_kind_to_string (ek: m_kind)); |
159 | |
160 | if (m_original_fndecl != m_effective_fndecl) |
161 | { |
162 | tree_logical_location logical_loc (m_original_fndecl); |
163 | props.set (PROPERTY_PREFIX "original_fndecl" , |
164 | v: make_sarif_logical_location_object (logical_loc)); |
165 | } |
166 | if (m_original_depth != m_effective_depth) |
167 | props.set_integer (PROPERTY_PREFIX "original_depth" , v: m_original_depth); |
168 | #undef PROPERTY_PREFIX |
169 | } |
170 | |
171 | /* Dump this event to PP (for debugging/logging purposes). */ |
172 | |
173 | void |
174 | checker_event::dump (pretty_printer *pp) const |
175 | { |
176 | label_text event_desc (get_desc (can_colorize: false)); |
177 | pp_printf (pp, "\"%s\" (depth %i" , |
178 | event_desc.get (), m_effective_depth); |
179 | |
180 | if (m_effective_depth != m_original_depth) |
181 | pp_printf (pp, " corrected from %i" , |
182 | m_original_depth); |
183 | if (m_effective_fndecl) |
184 | { |
185 | pp_printf (pp, ", fndecl %qE" , m_effective_fndecl); |
186 | if (m_effective_fndecl != m_original_fndecl) |
187 | pp_printf (pp, " corrected from %qE" , m_original_fndecl); |
188 | } |
189 | pp_printf (pp, ", m_loc=%x)" , |
190 | get_location ()); |
191 | } |
192 | |
193 | /* Dump this event to stderr (for debugging/logging purposes). */ |
194 | |
195 | DEBUG_FUNCTION void |
196 | checker_event::debug () const |
197 | { |
198 | pretty_printer pp; |
199 | pp_format_decoder (&pp) = default_tree_printer; |
200 | pp_show_color (&pp) = pp_show_color (global_dc->printer); |
201 | pp.buffer->stream = stderr; |
202 | dump (pp: &pp); |
203 | pp_newline (&pp); |
204 | pp_flush (&pp); |
205 | } |
206 | |
207 | /* Hook for being notified when this event has its final id EMISSION_ID |
208 | and is about to emitted for PD. |
209 | |
210 | Base implementation of checker_event::prepare_for_emission vfunc; |
211 | subclasses that override this should chain up to it. |
212 | |
213 | Record PD and EMISSION_ID, and call the get_desc vfunc, so that any |
214 | side-effects of the call to get_desc take place before |
215 | pending_diagnostic::emit is called. |
216 | |
217 | For example, state_change_event::get_desc can call |
218 | pending_diagnostic::describe_state_change; free_of_non_heap can use this |
219 | to tweak the message (TODO: would be neater to simply capture the |
220 | pertinent data within the sm-state). */ |
221 | |
222 | void |
223 | checker_event::prepare_for_emission (checker_path *, |
224 | pending_diagnostic *pd, |
225 | diagnostic_event_id_t emission_id) |
226 | { |
227 | m_pending_diagnostic = pd; |
228 | m_emission_id = emission_id; |
229 | |
230 | label_text desc = get_desc (can_colorize: false); |
231 | } |
232 | |
233 | /* class debug_event : public checker_event. */ |
234 | |
235 | /* Implementation of diagnostic_event::get_desc vfunc for |
236 | debug_event. |
237 | Use the saved string as the event's description. */ |
238 | |
239 | label_text |
240 | debug_event::get_desc (bool) const |
241 | { |
242 | return label_text::borrow (buffer: m_desc); |
243 | } |
244 | |
245 | /* class precanned_custom_event : public custom_event. */ |
246 | |
247 | /* Implementation of diagnostic_event::get_desc vfunc for |
248 | precanned_custom_event. |
249 | Use the saved string as the event's description. */ |
250 | |
251 | label_text |
252 | precanned_custom_event::get_desc (bool) const |
253 | { |
254 | return label_text::borrow (buffer: m_desc); |
255 | } |
256 | |
257 | /* class statement_event : public checker_event. */ |
258 | |
259 | /* statement_event's ctor. */ |
260 | |
261 | statement_event::statement_event (const gimple *stmt, tree fndecl, int depth, |
262 | const program_state &dst_state) |
263 | : checker_event (EK_STMT, |
264 | event_loc_info (gimple_location (g: stmt), fndecl, depth)), |
265 | m_stmt (stmt), |
266 | m_dst_state (dst_state) |
267 | { |
268 | } |
269 | |
270 | /* Implementation of diagnostic_event::get_desc vfunc for |
271 | statement_event. |
272 | Use the statement's dump form as the event's description. */ |
273 | |
274 | label_text |
275 | statement_event::get_desc (bool) const |
276 | { |
277 | pretty_printer pp; |
278 | pp_string (&pp, "stmt: " ); |
279 | pp_gimple_stmt_1 (&pp, m_stmt, 0, (dump_flags_t)0); |
280 | return label_text::take (buffer: xstrdup (pp_formatted_text (&pp))); |
281 | } |
282 | |
283 | /* class region_creation_event : public checker_event. */ |
284 | |
285 | region_creation_event::region_creation_event (const event_loc_info &loc_info) |
286 | : checker_event (EK_REGION_CREATION, loc_info) |
287 | { |
288 | } |
289 | |
290 | /* The various region_creation_event subclasses' get_desc |
291 | implementations. */ |
292 | |
293 | label_text |
294 | region_creation_event_memory_space::get_desc (bool) const |
295 | { |
296 | switch (m_mem_space) |
297 | { |
298 | default: |
299 | return label_text::borrow (buffer: "region created here" ); |
300 | case MEMSPACE_STACK: |
301 | return label_text::borrow (buffer: "region created on stack here" ); |
302 | case MEMSPACE_HEAP: |
303 | return label_text::borrow (buffer: "region created on heap here" ); |
304 | } |
305 | } |
306 | |
307 | label_text |
308 | region_creation_event_capacity::get_desc (bool can_colorize) const |
309 | { |
310 | gcc_assert (m_capacity); |
311 | if (TREE_CODE (m_capacity) == INTEGER_CST) |
312 | { |
313 | unsigned HOST_WIDE_INT hwi = tree_to_uhwi (m_capacity); |
314 | return make_label_text_n (can_colorize, |
315 | n: hwi, |
316 | singular_fmt: "capacity: %wu byte" , |
317 | plural_fmt: "capacity: %wu bytes" , |
318 | hwi); |
319 | } |
320 | else |
321 | return make_label_text (can_colorize, |
322 | fmt: "capacity: %qE bytes" , m_capacity); |
323 | } |
324 | |
325 | label_text |
326 | region_creation_event_allocation_size::get_desc (bool can_colorize) const |
327 | { |
328 | if (m_capacity) |
329 | { |
330 | if (TREE_CODE (m_capacity) == INTEGER_CST) |
331 | return make_label_text_n (can_colorize, |
332 | n: tree_to_uhwi (m_capacity), |
333 | singular_fmt: "allocated %E byte here" , |
334 | plural_fmt: "allocated %E bytes here" , |
335 | m_capacity); |
336 | else |
337 | return make_label_text (can_colorize, |
338 | fmt: "allocated %qE bytes here" , |
339 | m_capacity); |
340 | } |
341 | return make_label_text (can_colorize, fmt: "allocated here" ); |
342 | } |
343 | |
344 | label_text |
345 | region_creation_event_debug::get_desc (bool) const |
346 | { |
347 | pretty_printer pp; |
348 | pp_format_decoder (&pp) = default_tree_printer; |
349 | pp_string (&pp, "region creation: " ); |
350 | m_reg->dump_to_pp (pp: &pp, simple: true); |
351 | if (m_capacity) |
352 | pp_printf (&pp, " capacity: %qE" , m_capacity); |
353 | return label_text::take (buffer: xstrdup (pp_formatted_text (&pp))); |
354 | } |
355 | |
356 | /* class function_entry_event : public checker_event. */ |
357 | |
358 | function_entry_event::function_entry_event (const program_point &dst_point) |
359 | : checker_event (EK_FUNCTION_ENTRY, |
360 | event_loc_info (dst_point.get_supernode |
361 | ()->get_start_location (), |
362 | dst_point.get_fndecl (), |
363 | dst_point.get_stack_depth ())) |
364 | { |
365 | } |
366 | |
367 | /* Implementation of diagnostic_event::get_desc vfunc for |
368 | function_entry_event. |
369 | |
370 | Use a string such as "entry to 'foo'" as the event's description. */ |
371 | |
372 | label_text |
373 | function_entry_event::get_desc (bool can_colorize) const |
374 | { |
375 | return make_label_text (can_colorize, fmt: "entry to %qE" , m_effective_fndecl); |
376 | } |
377 | |
378 | /* Implementation of diagnostic_event::get_meaning vfunc for |
379 | function entry. */ |
380 | |
381 | diagnostic_event::meaning |
382 | function_entry_event::get_meaning () const |
383 | { |
384 | return meaning (VERB_enter, NOUN_function); |
385 | } |
386 | |
387 | /* class state_change_event : public checker_event. */ |
388 | |
389 | /* state_change_event's ctor. */ |
390 | |
391 | state_change_event::state_change_event (const supernode *node, |
392 | const gimple *stmt, |
393 | int stack_depth, |
394 | const state_machine &sm, |
395 | const svalue *sval, |
396 | state_machine::state_t from, |
397 | state_machine::state_t to, |
398 | const svalue *origin, |
399 | const program_state &dst_state, |
400 | const exploded_node *enode) |
401 | : checker_event (EK_STATE_CHANGE, |
402 | event_loc_info (stmt->location, |
403 | node->m_fun->decl, |
404 | stack_depth)), |
405 | m_node (node), m_stmt (stmt), m_sm (sm), |
406 | m_sval (sval), m_from (from), m_to (to), |
407 | m_origin (origin), |
408 | m_dst_state (dst_state), |
409 | m_enode (enode) |
410 | { |
411 | } |
412 | |
413 | /* Implementation of diagnostic_event::get_desc vfunc for |
414 | state_change_event. |
415 | |
416 | Attempt to generate a nicer human-readable description. |
417 | For greatest precision-of-wording, give the pending diagnostic |
418 | a chance to describe this state change (in terms of the |
419 | diagnostic). |
420 | Note that we only have a pending_diagnostic set on the event once |
421 | the diagnostic is about to being emitted, so the description for |
422 | an event can change. */ |
423 | |
424 | label_text |
425 | state_change_event::get_desc (bool can_colorize) const |
426 | { |
427 | if (m_pending_diagnostic) |
428 | { |
429 | region_model *model = m_dst_state.m_region_model; |
430 | tree var = model->get_representative_tree (sval: m_sval); |
431 | tree origin = model->get_representative_tree (sval: m_origin); |
432 | label_text custom_desc |
433 | = m_pending_diagnostic->describe_state_change |
434 | (evdesc::state_change (can_colorize, var, origin, |
435 | m_from, m_to, m_emission_id, *this)); |
436 | if (custom_desc.get ()) |
437 | { |
438 | if (flag_analyzer_verbose_state_changes) |
439 | { |
440 | /* Get any "meaning" of event. */ |
441 | diagnostic_event::meaning meaning = get_meaning (); |
442 | pretty_printer meaning_pp; |
443 | meaning.dump_to_pp (pp: &meaning_pp); |
444 | |
445 | /* Append debug version. */ |
446 | if (var) |
447 | { |
448 | if (m_origin) |
449 | return make_label_text |
450 | (can_colorize, |
451 | fmt: "%s (state of %qE: %qs -> %qs, origin: %qE, meaning: %s)" , |
452 | custom_desc.get (), |
453 | var, |
454 | m_from->get_name (), |
455 | m_to->get_name (), |
456 | origin, |
457 | pp_formatted_text (&meaning_pp)); |
458 | else |
459 | return make_label_text |
460 | (can_colorize, |
461 | fmt: "%s (state of %qE: %qs -> %qs, NULL origin, meaning: %s)" , |
462 | custom_desc.get (), |
463 | var, |
464 | m_from->get_name (), |
465 | m_to->get_name (), |
466 | pp_formatted_text (&meaning_pp)); |
467 | } |
468 | else |
469 | { |
470 | if (m_origin) |
471 | return make_label_text |
472 | (can_colorize, |
473 | fmt: "%s (state: %qs -> %qs, origin: %qE, meaning: %s)" , |
474 | custom_desc.get (), |
475 | m_from->get_name (), |
476 | m_to->get_name (), |
477 | origin, |
478 | pp_formatted_text (&meaning_pp)); |
479 | else |
480 | return make_label_text |
481 | (can_colorize, |
482 | fmt: "%s (state: %qs -> %qs, NULL origin, meaning: %s)" , |
483 | custom_desc.get (), |
484 | m_from->get_name (), |
485 | m_to->get_name (), |
486 | pp_formatted_text (&meaning_pp)); |
487 | } |
488 | } |
489 | else |
490 | return custom_desc; |
491 | } |
492 | } |
493 | |
494 | /* Fallback description. */ |
495 | if (m_sval) |
496 | { |
497 | label_text sval_desc = m_sval->get_desc (); |
498 | if (m_origin) |
499 | { |
500 | label_text origin_desc = m_origin->get_desc (); |
501 | return make_label_text |
502 | (can_colorize, |
503 | fmt: "state of %qs: %qs -> %qs (origin: %qs)" , |
504 | sval_desc.get (), |
505 | m_from->get_name (), |
506 | m_to->get_name (), |
507 | origin_desc.get ()); |
508 | } |
509 | else |
510 | return make_label_text |
511 | (can_colorize, |
512 | fmt: "state of %qs: %qs -> %qs (NULL origin)" , |
513 | sval_desc.get (), |
514 | m_from->get_name (), |
515 | m_to->get_name ()); |
516 | } |
517 | else |
518 | { |
519 | gcc_assert (m_origin == NULL); |
520 | return make_label_text |
521 | (can_colorize, |
522 | fmt: "global state: %qs -> %qs" , |
523 | m_from->get_name (), |
524 | m_to->get_name ()); |
525 | } |
526 | } |
527 | |
528 | /* Implementation of diagnostic_event::get_meaning vfunc for |
529 | state change events: delegate to the pending_diagnostic to |
530 | get any meaning. */ |
531 | |
532 | diagnostic_event::meaning |
533 | state_change_event::get_meaning () const |
534 | { |
535 | if (m_pending_diagnostic) |
536 | { |
537 | region_model *model = m_dst_state.m_region_model; |
538 | tree var = model->get_representative_tree (sval: m_sval); |
539 | tree origin = model->get_representative_tree (sval: m_origin); |
540 | return m_pending_diagnostic->get_meaning_for_state_change |
541 | (evdesc::state_change (false, var, origin, |
542 | m_from, m_to, m_emission_id, *this)); |
543 | } |
544 | else |
545 | return meaning (); |
546 | } |
547 | |
548 | /* class superedge_event : public checker_event. */ |
549 | |
550 | /* Implementation of diagnostic_event::maybe_add_sarif_properties |
551 | for superedge_event. */ |
552 | |
553 | void |
554 | superedge_event::maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) |
555 | const |
556 | { |
557 | checker_event::maybe_add_sarif_properties (thread_flow_loc_obj); |
558 | sarif_property_bag &props = thread_flow_loc_obj.get_or_create_properties (); |
559 | #define PROPERTY_PREFIX "gcc/analyzer/superedge_event/" |
560 | if (m_sedge) |
561 | props.set (PROPERTY_PREFIX "superedge" , v: m_sedge->to_json ()); |
562 | #undef PROPERTY_PREFIX |
563 | } |
564 | |
565 | /* Get the callgraph_superedge for this superedge_event, which must be |
566 | for an interprocedural edge, rather than a CFG edge. */ |
567 | |
568 | const callgraph_superedge& |
569 | superedge_event::get_callgraph_superedge () const |
570 | { |
571 | gcc_assert (m_sedge->m_kind != SUPEREDGE_CFG_EDGE); |
572 | return *m_sedge->dyn_cast_callgraph_superedge (); |
573 | } |
574 | |
575 | /* Determine if this event should be filtered at the given verbosity |
576 | level. */ |
577 | |
578 | bool |
579 | superedge_event::should_filter_p (int verbosity) const |
580 | { |
581 | switch (m_sedge->m_kind) |
582 | { |
583 | case SUPEREDGE_CFG_EDGE: |
584 | { |
585 | if (verbosity < 2) |
586 | return true; |
587 | |
588 | if (verbosity < 4) |
589 | { |
590 | /* Filter events with empty descriptions. This ought to filter |
591 | FALLTHRU, but retain true/false/switch edges. */ |
592 | label_text desc = get_desc (can_colorize: false); |
593 | gcc_assert (desc.get ()); |
594 | if (desc.get ()[0] == '\0') |
595 | return true; |
596 | } |
597 | } |
598 | break; |
599 | |
600 | default: |
601 | break; |
602 | } |
603 | return false; |
604 | } |
605 | |
606 | /* superedge_event's ctor. */ |
607 | |
608 | superedge_event::superedge_event (enum event_kind kind, |
609 | const exploded_edge &eedge, |
610 | const event_loc_info &loc_info) |
611 | : checker_event (kind, loc_info), |
612 | m_eedge (eedge), m_sedge (eedge.m_sedge), |
613 | m_var (NULL_TREE), m_critical_state (0) |
614 | { |
615 | /* Note that m_sedge can be nullptr for e.g. jumps through |
616 | function pointers. */ |
617 | } |
618 | |
619 | /* class cfg_edge_event : public superedge_event. */ |
620 | |
621 | /* Get the cfg_superedge for this cfg_edge_event. */ |
622 | |
623 | const cfg_superedge & |
624 | cfg_edge_event::get_cfg_superedge () const |
625 | { |
626 | return *m_sedge->dyn_cast_cfg_superedge (); |
627 | } |
628 | |
629 | /* cfg_edge_event's ctor. */ |
630 | |
631 | cfg_edge_event::cfg_edge_event (enum event_kind kind, |
632 | const exploded_edge &eedge, |
633 | const event_loc_info &loc_info) |
634 | : superedge_event (kind, eedge, loc_info) |
635 | { |
636 | gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CFG_EDGE); |
637 | } |
638 | |
639 | /* Implementation of diagnostic_event::get_meaning vfunc for |
640 | CFG edge events. */ |
641 | |
642 | diagnostic_event::meaning |
643 | cfg_edge_event::get_meaning () const |
644 | { |
645 | const cfg_superedge& cfg_sedge = get_cfg_superedge (); |
646 | if (cfg_sedge.true_value_p ()) |
647 | return meaning (VERB_branch, PROPERTY_true); |
648 | else if (cfg_sedge.false_value_p ()) |
649 | return meaning (VERB_branch, PROPERTY_false); |
650 | else |
651 | return meaning (); |
652 | } |
653 | |
654 | /* class start_cfg_edge_event : public cfg_edge_event. */ |
655 | |
656 | /* Implementation of diagnostic_event::get_desc vfunc for |
657 | start_cfg_edge_event. |
658 | |
659 | If -fanalyzer-verbose-edges, then generate low-level descriptions, such |
660 | as |
661 | "taking 'true' edge SN:7 -> SN:8". |
662 | |
663 | Otherwise, generate strings using the label of the underlying CFG if |
664 | any, such as: |
665 | "following 'true' branch..." or |
666 | "following 'case 3' branch..." |
667 | "following 'default' branch..." |
668 | |
669 | For conditionals, attempt to supply a description of the condition that |
670 | holds, such as: |
671 | "following 'false' branch (when 'ptr' is non-NULL)..." |
672 | |
673 | Failing that, return an empty description (which will lead to this event |
674 | being filtered). */ |
675 | |
676 | label_text |
677 | start_cfg_edge_event::get_desc (bool can_colorize) const |
678 | { |
679 | bool user_facing = !flag_analyzer_verbose_edges; |
680 | label_text edge_desc (m_sedge->get_description (user_facing)); |
681 | if (user_facing) |
682 | { |
683 | if (edge_desc.get () && strlen (s: edge_desc.get ()) > 0) |
684 | { |
685 | label_text cond_desc = maybe_describe_condition (can_colorize); |
686 | label_text result; |
687 | if (cond_desc.get ()) |
688 | return make_label_text (can_colorize, |
689 | fmt: "following %qs branch (%s)..." , |
690 | edge_desc.get (), cond_desc.get ()); |
691 | else |
692 | return make_label_text (can_colorize, |
693 | fmt: "following %qs branch..." , |
694 | edge_desc.get ()); |
695 | } |
696 | else |
697 | return label_text::borrow (buffer: "" ); |
698 | } |
699 | else |
700 | { |
701 | if (strlen (s: edge_desc.get ()) > 0) |
702 | return make_label_text (can_colorize, |
703 | fmt: "taking %qs edge SN:%i -> SN:%i" , |
704 | edge_desc.get (), |
705 | m_sedge->m_src->m_index, |
706 | m_sedge->m_dest->m_index); |
707 | else |
708 | return make_label_text (can_colorize, |
709 | fmt: "taking edge SN:%i -> SN:%i" , |
710 | m_sedge->m_src->m_index, |
711 | m_sedge->m_dest->m_index); |
712 | } |
713 | } |
714 | |
715 | /* Attempt to generate a description of any condition that holds at this edge. |
716 | |
717 | The intent is to make the user-facing messages more clear, especially for |
718 | cases where there's a single or double-negative, such as |
719 | when describing the false branch of an inverted condition. |
720 | |
721 | For example, rather than printing just: |
722 | |
723 | | if (!ptr) |
724 | | ~ |
725 | | | |
726 | | (1) following 'false' branch... |
727 | |
728 | it's clearer to spell out the condition that holds: |
729 | |
730 | | if (!ptr) |
731 | | ~ |
732 | | | |
733 | | (1) following 'false' branch (when 'ptr' is non-NULL)... |
734 | ^^^^^^^^^^^^^^^^^^^^^^ |
735 | |
736 | In the above example, this function would generate the highlighted |
737 | string: "when 'ptr' is non-NULL". |
738 | |
739 | If the edge is not a condition, or it's not clear that a description of |
740 | the condition would be helpful to the user, return NULL. */ |
741 | |
742 | label_text |
743 | start_cfg_edge_event::maybe_describe_condition (bool can_colorize) const |
744 | { |
745 | const cfg_superedge& cfg_sedge = get_cfg_superedge (); |
746 | |
747 | if (cfg_sedge.true_value_p () || cfg_sedge.false_value_p ()) |
748 | { |
749 | const gimple *last_stmt = m_sedge->m_src->get_last_stmt (); |
750 | if (const gcond *cond_stmt = dyn_cast <const gcond *> (p: last_stmt)) |
751 | { |
752 | enum tree_code op = gimple_cond_code (gs: cond_stmt); |
753 | tree lhs = gimple_cond_lhs (gs: cond_stmt); |
754 | tree rhs = gimple_cond_rhs (gs: cond_stmt); |
755 | if (cfg_sedge.false_value_p ()) |
756 | op = invert_tree_comparison (op, false /* honor_nans */); |
757 | return maybe_describe_condition (can_colorize, |
758 | lhs, op, rhs); |
759 | } |
760 | } |
761 | return label_text::borrow (NULL); |
762 | } |
763 | |
764 | /* Subroutine of maybe_describe_condition above. |
765 | |
766 | Attempt to generate a user-facing description of the condition |
767 | LHS OP RHS, but only if it is likely to make it easier for the |
768 | user to understand a condition. */ |
769 | |
770 | label_text |
771 | start_cfg_edge_event::maybe_describe_condition (bool can_colorize, |
772 | tree lhs, |
773 | enum tree_code op, |
774 | tree rhs) |
775 | { |
776 | /* In theory we could just build a tree via |
777 | fold_build2 (op, boolean_type_node, lhs, rhs) |
778 | and print it with %qE on it, but this leads to warts such as |
779 | parenthesizing vars, such as '(i) <= 9', and uses of '<unknown>'. */ |
780 | |
781 | /* Special-case: describe testing the result of strcmp, as figuring |
782 | out what the "true" or "false" path is can be confusing to the user. */ |
783 | if (TREE_CODE (lhs) == SSA_NAME |
784 | && zerop (rhs)) |
785 | { |
786 | if (gcall *call = dyn_cast <gcall *> (SSA_NAME_DEF_STMT (lhs))) |
787 | if (is_special_named_call_p (call, funcname: "strcmp" , num_args: 2)) |
788 | { |
789 | if (op == EQ_EXPR) |
790 | return label_text::borrow (buffer: "when the strings are equal" ); |
791 | if (op == NE_EXPR) |
792 | return label_text::borrow (buffer: "when the strings are non-equal" ); |
793 | } |
794 | } |
795 | |
796 | /* Only attempt to generate text for sufficiently simple expressions. */ |
797 | if (!should_print_expr_p (lhs)) |
798 | return label_text::borrow (NULL); |
799 | if (!should_print_expr_p (rhs)) |
800 | return label_text::borrow (NULL); |
801 | |
802 | /* Special cases for pointer comparisons against NULL. */ |
803 | if (POINTER_TYPE_P (TREE_TYPE (lhs)) |
804 | && POINTER_TYPE_P (TREE_TYPE (rhs)) |
805 | && zerop (rhs)) |
806 | { |
807 | if (op == EQ_EXPR) |
808 | return make_label_text (can_colorize, fmt: "when %qE is NULL" , |
809 | lhs); |
810 | if (op == NE_EXPR) |
811 | return make_label_text (can_colorize, fmt: "when %qE is non-NULL" , |
812 | lhs); |
813 | } |
814 | |
815 | return make_label_text (can_colorize, fmt: "when %<%E %s %E%>" , |
816 | lhs, op_symbol_code (op), rhs); |
817 | } |
818 | |
819 | /* Subroutine of maybe_describe_condition. |
820 | |
821 | Return true if EXPR is we will get suitable user-facing output |
822 | from %E on it. */ |
823 | |
824 | bool |
825 | start_cfg_edge_event::should_print_expr_p (tree expr) |
826 | { |
827 | if (TREE_CODE (expr) == SSA_NAME) |
828 | { |
829 | if (SSA_NAME_VAR (expr)) |
830 | return should_print_expr_p (SSA_NAME_VAR (expr)); |
831 | else |
832 | return false; |
833 | } |
834 | |
835 | if (DECL_P (expr)) |
836 | return true; |
837 | |
838 | if (CONSTANT_CLASS_P (expr)) |
839 | return true; |
840 | |
841 | return false; |
842 | } |
843 | |
844 | /* class call_event : public superedge_event. */ |
845 | |
846 | /* call_event's ctor. */ |
847 | |
848 | call_event::call_event (const exploded_edge &eedge, |
849 | const event_loc_info &loc_info) |
850 | : superedge_event (EK_CALL_EDGE, eedge, loc_info) |
851 | { |
852 | if (eedge.m_sedge) |
853 | gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CALL); |
854 | |
855 | m_src_snode = eedge.m_src->get_supernode (); |
856 | m_dest_snode = eedge.m_dest->get_supernode (); |
857 | } |
858 | |
859 | /* Implementation of diagnostic_event::get_desc vfunc for |
860 | call_event. |
861 | |
862 | If this call event passes critical state for an sm-based warning, |
863 | allow the diagnostic to generate a precise description, such as: |
864 | |
865 | "passing freed pointer 'ptr' in call to 'foo' from 'bar'" |
866 | |
867 | Otherwise, generate a description of the form |
868 | "calling 'foo' from 'bar'". */ |
869 | |
870 | label_text |
871 | call_event::get_desc (bool can_colorize) const |
872 | { |
873 | if (m_critical_state && m_pending_diagnostic) |
874 | { |
875 | gcc_assert (m_var); |
876 | tree var = fixup_tree_for_diagnostic (m_var); |
877 | label_text custom_desc |
878 | = m_pending_diagnostic->describe_call_with_state |
879 | (evdesc::call_with_state (can_colorize, |
880 | m_src_snode->m_fun->decl, |
881 | m_dest_snode->m_fun->decl, |
882 | var, |
883 | m_critical_state)); |
884 | if (custom_desc.get ()) |
885 | return custom_desc; |
886 | } |
887 | |
888 | return make_label_text (can_colorize, |
889 | fmt: "calling %qE from %qE" , |
890 | get_callee_fndecl (), |
891 | get_caller_fndecl ()); |
892 | } |
893 | |
894 | /* Implementation of diagnostic_event::get_meaning vfunc for |
895 | function call events. */ |
896 | |
897 | diagnostic_event::meaning |
898 | call_event::get_meaning () const |
899 | { |
900 | return meaning (VERB_call, NOUN_function); |
901 | } |
902 | |
903 | /* Override of checker_event::is_call_p for calls. */ |
904 | |
905 | bool |
906 | call_event::is_call_p () const |
907 | { |
908 | return true; |
909 | } |
910 | |
911 | tree |
912 | call_event::get_caller_fndecl () const |
913 | { |
914 | return m_src_snode->m_fun->decl; |
915 | } |
916 | |
917 | tree |
918 | call_event::get_callee_fndecl () const |
919 | { |
920 | return m_dest_snode->m_fun->decl; |
921 | } |
922 | |
923 | /* class return_event : public superedge_event. */ |
924 | |
925 | /* return_event's ctor. */ |
926 | |
927 | return_event::return_event (const exploded_edge &eedge, |
928 | const event_loc_info &loc_info) |
929 | : superedge_event (EK_RETURN_EDGE, eedge, loc_info) |
930 | { |
931 | if (eedge.m_sedge) |
932 | gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_RETURN); |
933 | |
934 | m_src_snode = eedge.m_src->get_supernode (); |
935 | m_dest_snode = eedge.m_dest->get_supernode (); |
936 | } |
937 | |
938 | /* Implementation of diagnostic_event::get_desc vfunc for |
939 | return_event. |
940 | |
941 | If this return event returns critical state for an sm-based warning, |
942 | allow the diagnostic to generate a precise description, such as: |
943 | |
944 | "possible of NULL to 'foo' from 'bar'" |
945 | |
946 | Otherwise, generate a description of the form |
947 | "returning to 'foo' from 'bar'. */ |
948 | |
949 | label_text |
950 | return_event::get_desc (bool can_colorize) const |
951 | { |
952 | /* For greatest precision-of-wording, if this is returning the |
953 | state involved in the pending diagnostic, give the pending |
954 | diagnostic a chance to describe this return (in terms of |
955 | itself). */ |
956 | if (m_critical_state && m_pending_diagnostic) |
957 | { |
958 | label_text custom_desc |
959 | = m_pending_diagnostic->describe_return_of_state |
960 | (evdesc::return_of_state (can_colorize, |
961 | m_dest_snode->m_fun->decl, |
962 | m_src_snode->m_fun->decl, |
963 | m_critical_state)); |
964 | if (custom_desc.get ()) |
965 | return custom_desc; |
966 | } |
967 | return make_label_text (can_colorize, |
968 | fmt: "returning to %qE from %qE" , |
969 | m_dest_snode->m_fun->decl, |
970 | m_src_snode->m_fun->decl); |
971 | } |
972 | |
973 | /* Implementation of diagnostic_event::get_meaning vfunc for |
974 | function return events. */ |
975 | |
976 | diagnostic_event::meaning |
977 | return_event::get_meaning () const |
978 | { |
979 | return meaning (VERB_return, NOUN_function); |
980 | } |
981 | |
982 | /* Override of checker_event::is_return_p for returns. */ |
983 | |
984 | bool |
985 | return_event::is_return_p () const |
986 | { |
987 | return true; |
988 | } |
989 | |
990 | /* class start_consolidated_cfg_edges_event : public checker_event. */ |
991 | |
992 | label_text |
993 | start_consolidated_cfg_edges_event::get_desc (bool can_colorize) const |
994 | { |
995 | return make_label_text (can_colorize, |
996 | fmt: "following %qs branch..." , |
997 | m_edge_sense ? "true" : "false" ); |
998 | } |
999 | |
1000 | /* Implementation of diagnostic_event::get_meaning vfunc for |
1001 | start_consolidated_cfg_edges_event. */ |
1002 | |
1003 | diagnostic_event::meaning |
1004 | start_consolidated_cfg_edges_event::get_meaning () const |
1005 | { |
1006 | return meaning (VERB_branch, |
1007 | (m_edge_sense ? PROPERTY_true : PROPERTY_false)); |
1008 | } |
1009 | |
1010 | /* class inlined_call_event : public checker_event. */ |
1011 | |
1012 | label_text |
1013 | inlined_call_event::get_desc (bool can_colorize) const |
1014 | { |
1015 | return make_label_text (can_colorize, |
1016 | fmt: "inlined call to %qE from %qE" , |
1017 | m_apparent_callee_fndecl, |
1018 | m_apparent_caller_fndecl); |
1019 | } |
1020 | |
1021 | /* Implementation of diagnostic_event::get_meaning vfunc for |
1022 | reconstructed inlined function calls. */ |
1023 | |
1024 | diagnostic_event::meaning |
1025 | inlined_call_event::get_meaning () const |
1026 | { |
1027 | return meaning (VERB_call, NOUN_function); |
1028 | } |
1029 | |
1030 | /* class setjmp_event : public checker_event. */ |
1031 | |
1032 | /* Implementation of diagnostic_event::get_desc vfunc for |
1033 | setjmp_event. */ |
1034 | |
1035 | label_text |
1036 | setjmp_event::get_desc (bool can_colorize) const |
1037 | { |
1038 | return make_label_text (can_colorize, |
1039 | fmt: "%qs called here" , |
1040 | get_user_facing_name (call: m_setjmp_call)); |
1041 | } |
1042 | |
1043 | /* Implementation of checker_event::prepare_for_emission vfunc for setjmp_event. |
1044 | |
1045 | Record this setjmp's event ID into the path, so that rewind events can |
1046 | use it. */ |
1047 | |
1048 | void |
1049 | setjmp_event::prepare_for_emission (checker_path *path, |
1050 | pending_diagnostic *pd, |
1051 | diagnostic_event_id_t emission_id) |
1052 | { |
1053 | checker_event::prepare_for_emission (path, pd, emission_id); |
1054 | path->record_setjmp_event (enode: m_enode, setjmp_emission_id: emission_id); |
1055 | } |
1056 | |
1057 | /* class rewind_event : public checker_event. */ |
1058 | |
1059 | /* Get the fndecl containing the site of the longjmp call. */ |
1060 | |
1061 | tree |
1062 | rewind_event::get_longjmp_caller () const |
1063 | { |
1064 | return m_eedge->m_src->get_function ()->decl; |
1065 | } |
1066 | |
1067 | /* Get the fndecl containing the site of the setjmp call. */ |
1068 | |
1069 | tree |
1070 | rewind_event::get_setjmp_caller () const |
1071 | { |
1072 | return m_eedge->m_dest->get_function ()->decl; |
1073 | } |
1074 | |
1075 | /* rewind_event's ctor. */ |
1076 | |
1077 | rewind_event::rewind_event (const exploded_edge *eedge, |
1078 | enum event_kind kind, |
1079 | const event_loc_info &loc_info, |
1080 | const rewind_info_t *rewind_info) |
1081 | : checker_event (kind, loc_info), |
1082 | m_rewind_info (rewind_info), |
1083 | m_eedge (eedge) |
1084 | { |
1085 | gcc_assert (m_eedge->m_custom_info.get () == m_rewind_info); |
1086 | } |
1087 | |
1088 | /* class rewind_from_longjmp_event : public rewind_event. */ |
1089 | |
1090 | /* Implementation of diagnostic_event::get_desc vfunc for |
1091 | rewind_from_longjmp_event. */ |
1092 | |
1093 | label_text |
1094 | rewind_from_longjmp_event::get_desc (bool can_colorize) const |
1095 | { |
1096 | const char *src_name |
1097 | = get_user_facing_name (call: m_rewind_info->get_longjmp_call ()); |
1098 | |
1099 | if (get_longjmp_caller () == get_setjmp_caller ()) |
1100 | /* Special-case: purely intraprocedural rewind. */ |
1101 | return make_label_text (can_colorize, |
1102 | fmt: "rewinding within %qE from %qs..." , |
1103 | get_longjmp_caller (), |
1104 | src_name); |
1105 | else |
1106 | return make_label_text (can_colorize, |
1107 | fmt: "rewinding from %qs in %qE..." , |
1108 | src_name, |
1109 | get_longjmp_caller ()); |
1110 | } |
1111 | |
1112 | /* class rewind_to_setjmp_event : public rewind_event. */ |
1113 | |
1114 | /* Implementation of diagnostic_event::get_desc vfunc for |
1115 | rewind_to_setjmp_event. */ |
1116 | |
1117 | label_text |
1118 | rewind_to_setjmp_event::get_desc (bool can_colorize) const |
1119 | { |
1120 | const char *dst_name |
1121 | = get_user_facing_name (call: m_rewind_info->get_setjmp_call ()); |
1122 | |
1123 | /* If we can, identify the ID of the setjmp_event. */ |
1124 | if (m_original_setjmp_event_id.known_p ()) |
1125 | { |
1126 | if (get_longjmp_caller () == get_setjmp_caller ()) |
1127 | /* Special-case: purely intraprocedural rewind. */ |
1128 | return make_label_text (can_colorize, |
1129 | fmt: "...to %qs (saved at %@)" , |
1130 | dst_name, |
1131 | &m_original_setjmp_event_id); |
1132 | else |
1133 | return make_label_text (can_colorize, |
1134 | fmt: "...to %qs in %qE (saved at %@)" , |
1135 | dst_name, |
1136 | get_setjmp_caller (), |
1137 | &m_original_setjmp_event_id); |
1138 | } |
1139 | else |
1140 | { |
1141 | if (get_longjmp_caller () == get_setjmp_caller ()) |
1142 | /* Special-case: purely intraprocedural rewind. */ |
1143 | return make_label_text (can_colorize, |
1144 | fmt: "...to %qs" , |
1145 | dst_name, |
1146 | get_setjmp_caller ()); |
1147 | else |
1148 | return make_label_text (can_colorize, |
1149 | fmt: "...to %qs in %qE" , |
1150 | dst_name, |
1151 | get_setjmp_caller ()); |
1152 | } |
1153 | } |
1154 | |
1155 | /* Implementation of checker_event::prepare_for_emission vfunc for |
1156 | rewind_to_setjmp_event. |
1157 | |
1158 | Attempt to look up the setjmp event ID that recorded the jmp_buf |
1159 | for this rewind. */ |
1160 | |
1161 | void |
1162 | rewind_to_setjmp_event::prepare_for_emission (checker_path *path, |
1163 | pending_diagnostic *pd, |
1164 | diagnostic_event_id_t emission_id) |
1165 | { |
1166 | checker_event::prepare_for_emission (path, pd, emission_id); |
1167 | path->get_setjmp_event (enode: m_rewind_info->get_enode_origin (), |
1168 | out_emission_id: &m_original_setjmp_event_id); |
1169 | } |
1170 | |
1171 | /* class warning_event : public checker_event. */ |
1172 | |
1173 | /* Implementation of diagnostic_event::get_desc vfunc for |
1174 | warning_event. |
1175 | |
1176 | If the pending diagnostic implements describe_final_event, use it, |
1177 | generating a precise description e.g. |
1178 | "second 'free' here; first 'free' was at (7)" |
1179 | |
1180 | Otherwise generate a generic description. */ |
1181 | |
1182 | label_text |
1183 | warning_event::get_desc (bool can_colorize) const |
1184 | { |
1185 | if (m_pending_diagnostic) |
1186 | { |
1187 | tree var = fixup_tree_for_diagnostic (m_var); |
1188 | label_text ev_desc |
1189 | = m_pending_diagnostic->describe_final_event |
1190 | (evdesc::final_event (can_colorize, var, m_state, *this)); |
1191 | if (ev_desc.get ()) |
1192 | { |
1193 | if (m_sm && flag_analyzer_verbose_state_changes) |
1194 | { |
1195 | if (var) |
1196 | return make_label_text (can_colorize, |
1197 | fmt: "%s (%qE is in state %qs)" , |
1198 | ev_desc.get (), |
1199 | var, m_state->get_name ()); |
1200 | else |
1201 | return make_label_text (can_colorize, |
1202 | fmt: "%s (in global state %qs)" , |
1203 | ev_desc.get (), |
1204 | m_state->get_name ()); |
1205 | } |
1206 | else |
1207 | return ev_desc; |
1208 | } |
1209 | } |
1210 | |
1211 | if (m_sm) |
1212 | { |
1213 | if (m_var) |
1214 | return make_label_text (can_colorize, |
1215 | fmt: "here (%qE is in state %qs)" , |
1216 | m_var, m_state->get_name ()); |
1217 | else |
1218 | return make_label_text (can_colorize, |
1219 | fmt: "here (in global state %qs)" , |
1220 | m_state->get_name ()); |
1221 | } |
1222 | else |
1223 | return label_text::borrow (buffer: "here" ); |
1224 | } |
1225 | |
1226 | /* Implementation of diagnostic_event::get_meaning vfunc for |
1227 | warning_event. */ |
1228 | |
1229 | diagnostic_event::meaning |
1230 | warning_event::get_meaning () const |
1231 | { |
1232 | return meaning (VERB_danger, NOUN_unknown); |
1233 | } |
1234 | |
1235 | } // namespace ana |
1236 | |
1237 | #endif /* #if ENABLE_ANALYZER */ |
1238 | |