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
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#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
63namespace ana {
64
65/* Get a string for EK. */
66
67const char *
68event_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
115checker_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
141diagnostic_event::meaning
142checker_event::get_meaning () const
143{
144 return meaning ();
145}
146
147/* Implementation of diagnostic_event::maybe_add_sarif_properties
148 for checker_event. */
149
150void
151checker_event::
152maybe_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
173void
174checker_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
195DEBUG_FUNCTION void
196checker_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
222void
223checker_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
239label_text
240debug_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
251label_text
252precanned_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
261statement_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
274label_text
275statement_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
285region_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
293label_text
294region_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
307label_text
308region_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
325label_text
326region_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
344label_text
345region_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
358function_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
372label_text
373function_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
381diagnostic_event::meaning
382function_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
391state_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
424label_text
425state_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
532diagnostic_event::meaning
533state_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
553void
554superedge_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
568const callgraph_superedge&
569superedge_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
578bool
579superedge_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
608superedge_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
623const cfg_superedge &
624cfg_edge_event::get_cfg_superedge () const
625{
626 return *m_sedge->dyn_cast_cfg_superedge ();
627}
628
629/* cfg_edge_event's ctor. */
630
631cfg_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
642diagnostic_event::meaning
643cfg_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
676label_text
677start_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
742label_text
743start_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
770label_text
771start_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
824bool
825start_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
848call_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
870label_text
871call_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
897diagnostic_event::meaning
898call_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
905bool
906call_event::is_call_p () const
907{
908 return true;
909}
910
911tree
912call_event::get_caller_fndecl () const
913{
914 return m_src_snode->m_fun->decl;
915}
916
917tree
918call_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
927return_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
949label_text
950return_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
976diagnostic_event::meaning
977return_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
984bool
985return_event::is_return_p () const
986{
987 return true;
988}
989
990/* class start_consolidated_cfg_edges_event : public checker_event. */
991
992label_text
993start_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
1003diagnostic_event::meaning
1004start_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
1012label_text
1013inlined_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
1024diagnostic_event::meaning
1025inlined_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
1035label_text
1036setjmp_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
1048void
1049setjmp_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
1061tree
1062rewind_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
1069tree
1070rewind_event::get_setjmp_caller () const
1071{
1072 return m_eedge->m_dest->get_function ()->decl;
1073}
1074
1075/* rewind_event's ctor. */
1076
1077rewind_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
1093label_text
1094rewind_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
1117label_text
1118rewind_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
1161void
1162rewind_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
1182label_text
1183warning_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
1229diagnostic_event::meaning
1230warning_event::get_meaning () const
1231{
1232 return meaning (VERB_danger, NOUN_unknown);
1233}
1234
1235} // namespace ana
1236
1237#endif /* #if ENABLE_ANALYZER */
1238

source code of gcc/analyzer/checker-event.cc