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