1 /* Utility functions for the analyzer.
2 Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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)
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.
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/>. */
22 #define INCLUDE_MEMORY
24 #include "coretypes.h"
27 #include "basic-block.h"
29 #include "diagnostic.h"
31 #include "analyzer/analyzer.h"
32 #include "tree-pretty-print.h"
33 #include "diagnostic-event-id.h"
40 /* Workaround for missing location information for some stmts,
41 which ultimately should be solved by fixing the frontends
42 to provide the locations (TODO). */
45 get_stmt_location (const gimple
*stmt
, function
*fun
)
48 return UNKNOWN_LOCATION
;
49 if (get_pure_location (stmt
->location
) == UNKNOWN_LOCATION
)
51 /* Workaround for missing location information for clobber
52 stmts, which seem to lack location information in the C frontend
53 at least. Created by gimplify_bind_expr, which uses the
54 BLOCK_SOURCE_END_LOCATION (BIND_EXPR_BLOCK (bind_expr))
55 but this is never set up when the block is created in
56 c_end_compound_stmt's pop_scope.
57 TODO: fix this missing location information.
59 For now, as a hackish workaround, use the location of the end of
61 if (gimple_clobber_p (stmt
) && fun
)
62 return fun
->function_end_locus
;
65 return stmt
->location
;
69 fixup_tree_for_diagnostic_1 (tree expr
, hash_set
<tree
> *visited
);
71 /* Attemp to generate a tree for the LHS of ASSIGN_STMT.
72 VISITED must be non-NULL; it is used to ensure termination. */
75 get_diagnostic_tree_for_gassign_1 (const gassign
*assign_stmt
,
76 hash_set
<tree
> *visited
)
78 enum tree_code code
= gimple_assign_rhs_code (assign_stmt
);
80 /* Reverse the effect of extract_ops_from_tree during
82 switch (get_gimple_rhs_class (code
))
85 case GIMPLE_INVALID_RHS
:
87 case GIMPLE_TERNARY_RHS
:
88 case GIMPLE_BINARY_RHS
:
89 case GIMPLE_UNARY_RHS
:
91 tree t
= make_node (code
);
92 TREE_TYPE (t
) = TREE_TYPE (gimple_assign_lhs (assign_stmt
));
93 unsigned num_rhs_args
= gimple_num_ops (assign_stmt
) - 1;
94 for (unsigned i
= 0; i
< num_rhs_args
; i
++)
96 tree op
= gimple_op (assign_stmt
, i
+ 1);
99 op
= fixup_tree_for_diagnostic_1 (op
, visited
);
103 TREE_OPERAND (t
, i
) = op
;
107 case GIMPLE_SINGLE_RHS
:
109 tree op
= gimple_op (assign_stmt
, 1);
110 op
= fixup_tree_for_diagnostic_1 (op
, visited
);
116 /* Subroutine of fixup_tree_for_diagnostic_1, called on SSA names.
117 Attempt to reconstruct a tree expression for SSA_NAME
118 based on its def-stmt.
119 SSA_NAME must be non-NULL.
120 VISITED must be non-NULL; it is used to ensure termination.
122 Return NULL_TREE if there is a problem. */
125 maybe_reconstruct_from_def_stmt (tree ssa_name
,
126 hash_set
<tree
> *visited
)
128 /* Ensure termination. */
129 if (visited
->contains (ssa_name
))
131 visited
->add (ssa_name
);
133 gimple
*def_stmt
= SSA_NAME_DEF_STMT (ssa_name
);
135 switch (gimple_code (def_stmt
))
142 /* Can't handle these. */
145 return get_diagnostic_tree_for_gassign_1
146 (as_a
<const gassign
*> (def_stmt
), visited
);
149 gcall
*call_stmt
= as_a
<gcall
*> (def_stmt
);
150 tree return_type
= gimple_call_return_type (call_stmt
);
151 tree fn
= fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt
),
155 unsigned num_args
= gimple_call_num_args (call_stmt
);
156 auto_vec
<tree
> args (num_args
);
157 for (unsigned i
= 0; i
< num_args
; i
++)
159 tree arg
= gimple_call_arg (call_stmt
, i
);
160 arg
= fixup_tree_for_diagnostic_1 (arg
, visited
);
161 if (arg
== NULL_TREE
)
163 args
.quick_push (arg
);
166 return build_call_array_loc (gimple_location (call_stmt
),
168 num_args
, args
.address ());
174 /* Subroutine of fixup_tree_for_diagnostic: attempt to fixup EXPR,
176 VISITED must be non-NULL; it is used to ensure termination. */
179 fixup_tree_for_diagnostic_1 (tree expr
, hash_set
<tree
> *visited
)
182 && TREE_CODE (expr
) == SSA_NAME
183 && (SSA_NAME_VAR (expr
) == NULL_TREE
184 || DECL_ARTIFICIAL (SSA_NAME_VAR (expr
))))
186 if (tree var
= SSA_NAME_VAR (expr
))
187 if (VAR_P (var
) && DECL_HAS_DEBUG_EXPR_P (var
))
188 return DECL_DEBUG_EXPR (var
);
189 if (tree expr2
= maybe_reconstruct_from_def_stmt (expr
, visited
))
195 /* We don't want to print '<unknown>' in our diagnostics (PR analyzer/99771),
196 but sometimes we generate diagnostics involving an ssa name for a
199 Work around this by attempting to reconstruct a tree expression for
200 such temporaries based on their def-stmts.
202 Otherwise return EXPR.
207 fixup_tree_for_diagnostic (tree expr
)
209 hash_set
<tree
> visited
;
210 return fixup_tree_for_diagnostic_1 (expr
, &visited
);
213 /* Attempt to generate a tree for the LHS of ASSIGN_STMT. */
216 get_diagnostic_tree_for_gassign (const gassign
*assign_stmt
)
218 hash_set
<tree
> visited
;
219 return get_diagnostic_tree_for_gassign_1 (assign_stmt
, &visited
);
222 /* Generate a JSON value for NODE, which can be NULL_TREE.
223 This is intended for debugging the analyzer rather than serialization and
224 thus is a string (or null, for NULL_TREE). */
227 tree_to_json (tree node
)
230 return new json::literal (json::JSON_NULL
);
233 dump_generic_node (&pp
, node
, 0, TDF_VOPS
|TDF_MEMSYMS
, false);
234 return new json::string (pp_formatted_text (&pp
));
237 /* Generate a JSON value for EVENT_ID.
238 This is intended for debugging the analyzer rather than serialization and
239 thus is a string matching those seen in event messags (or null,
243 diagnostic_event_id_to_json (const diagnostic_event_id_t
&event_id
)
245 if (event_id
.known_p ())
248 pp_printf (&pp
, "%@", &event_id
);
249 return new json::string (pp_formatted_text (&pp
));
252 return new json::literal (json::JSON_NULL
);
255 /* Generate a JSON value for OFFSET.
256 This is intended for debugging the analyzer rather than serialization and
260 bit_offset_to_json (const bit_offset_t
&offset
)
263 pp_wide_int_large (&pp
, offset
, SIGNED
);
264 return new json::string (pp_formatted_text (&pp
));
267 /* Generate a JSON value for OFFSET.
268 This is intended for debugging the analyzer rather than serialization and
272 byte_offset_to_json (const byte_offset_t
&offset
)
275 pp_wide_int_large (&pp
, offset
, SIGNED
);
276 return new json::string (pp_formatted_text (&pp
));
279 /* Workaround for lack of const-correctness of ssa_default_def. */
282 get_ssa_default_def (const function
&fun
, tree var
)
284 return ssa_default_def (const_cast <function
*> (&fun
), var
);
289 /* Helper function for checkers. Is the CALL to the given function name,
290 and with the given number of arguments?
292 This doesn't resolve function pointers via the region model;
293 is_named_call_p should be used instead, using a fndecl from
294 get_fndecl_for_call; this function should only be used for special cases
295 where it's not practical to get at the region model, or for special
296 analyzer functions such as __analyzer_dump. */
299 is_special_named_call_p (const gcall
*call
, const char *funcname
,
300 unsigned int num_args
)
302 gcc_assert (funcname
);
304 tree fndecl
= gimple_call_fndecl (call
);
308 return is_named_call_p (fndecl
, funcname
, call
, num_args
);
311 /* Helper function for checkers. Is FNDECL an extern fndecl at file scope
312 that has the given FUNCNAME?
314 Compare with special_function_p in calls.cc. */
317 is_named_call_p (const_tree fndecl
, const char *funcname
)
320 gcc_assert (funcname
);
322 if (!maybe_special_function_p (fndecl
))
325 tree identifier
= DECL_NAME (fndecl
);
326 const char *name
= IDENTIFIER_POINTER (identifier
);
327 const char *tname
= name
;
329 /* Potentially disregard prefix _ or __ in FNDECL's name, but not if
330 FUNCNAME itself has leading underscores (e.g. when looking for
331 "__analyzer_eval"). */
332 if (funcname
[0] != '_' && name
[0] == '_')
340 return 0 == strcmp (tname
, funcname
);
343 /* Return true if FNDECL is within the namespace "std".
344 Compare with cp/typeck.cc: decl_in_std_namespace_p, but this doesn't
345 rely on being the C++ FE (or handle inline namespaces inside of std). */
348 is_std_function_p (const_tree fndecl
)
350 tree name_decl
= DECL_NAME (fndecl
);
353 if (!DECL_CONTEXT (fndecl
))
355 if (TREE_CODE (DECL_CONTEXT (fndecl
)) != NAMESPACE_DECL
)
357 tree ns
= DECL_CONTEXT (fndecl
);
358 if (!(DECL_CONTEXT (ns
) == NULL_TREE
359 || TREE_CODE (DECL_CONTEXT (ns
)) == TRANSLATION_UNIT_DECL
))
363 return id_equal ("std", DECL_NAME (ns
));
366 /* Like is_named_call_p, but look for std::FUNCNAME. */
369 is_std_named_call_p (const_tree fndecl
, const char *funcname
)
372 gcc_assert (funcname
);
374 if (!is_std_function_p (fndecl
))
377 tree identifier
= DECL_NAME (fndecl
);
378 const char *name
= IDENTIFIER_POINTER (identifier
);
379 const char *tname
= name
;
381 /* Don't disregard prefix _ or __ in FNDECL's name. */
383 return 0 == strcmp (tname
, funcname
);
386 /* Helper function for checkers. Is FNDECL an extern fndecl at file scope
387 that has the given FUNCNAME, and does CALL have the given number of
391 is_named_call_p (const_tree fndecl
, const char *funcname
,
392 const gcall
*call
, unsigned int num_args
)
395 gcc_assert (funcname
);
397 if (!is_named_call_p (fndecl
, funcname
))
400 if (gimple_call_num_args (call
) != num_args
)
406 /* Like is_named_call_p, but check for std::FUNCNAME. */
409 is_std_named_call_p (const_tree fndecl
, const char *funcname
,
410 const gcall
*call
, unsigned int num_args
)
413 gcc_assert (funcname
);
415 if (!is_std_named_call_p (fndecl
, funcname
))
418 if (gimple_call_num_args (call
) != num_args
)
424 /* Return true if stmt is a setjmp or sigsetjmp call. */
427 is_setjmp_call_p (const gcall
*call
)
429 if (is_special_named_call_p (call
, "setjmp", 1)
430 || is_special_named_call_p (call
, "sigsetjmp", 2))
431 /* region_model::on_setjmp requires a pointer. */
432 if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call
, 0))))
438 /* Return true if stmt is a longjmp or siglongjmp call. */
441 is_longjmp_call_p (const gcall
*call
)
443 if (is_special_named_call_p (call
, "longjmp", 2)
444 || is_special_named_call_p (call
, "siglongjmp", 2))
445 /* exploded_node::on_longjmp requires a pointer for the initial
447 if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call
, 0))))
453 /* For a CALL that matched is_special_named_call_p or is_named_call_p for
454 some name, return a name for the called function suitable for use in
455 diagnostics (stripping the leading underscores). */
458 get_user_facing_name (const gcall
*call
)
460 tree fndecl
= gimple_call_fndecl (call
);
463 tree identifier
= DECL_NAME (fndecl
);
464 gcc_assert (identifier
);
466 const char *name
= IDENTIFIER_POINTER (identifier
);
468 /* Strip prefix _ or __ in FNDECL's name. */
480 /* Generate a label_text instance by formatting FMT, using a
481 temporary clone of the global_dc's printer (thus using its
482 formatting callbacks).
484 Colorize if the global_dc supports colorization and CAN_COLORIZE is
488 make_label_text (bool can_colorize
, const char *fmt
, ...)
490 pretty_printer
*pp
= global_dc
->printer
->clone ();
491 pp_clear_output_area (pp
);
494 pp_show_color (pp
) = false;
496 rich_location
rich_loc (line_table
, UNKNOWN_LOCATION
);
502 text_info
ti (_(fmt
), &ap
, 0, NULL
, &rich_loc
);
504 pp_output_formatted_text (pp
);
508 label_text result
= label_text::take (xstrdup (pp_formatted_text (pp
)));
513 /* As above, but with singular vs plural. */
516 make_label_text_n (bool can_colorize
, unsigned HOST_WIDE_INT n
,
517 const char *singular_fmt
,
518 const char *plural_fmt
, ...)
520 pretty_printer
*pp
= global_dc
->printer
->clone ();
521 pp_clear_output_area (pp
);
524 pp_show_color (pp
) = false;
526 rich_location
rich_loc (line_table
, UNKNOWN_LOCATION
);
530 va_start (ap
, plural_fmt
);
532 const char *fmt
= ngettext (singular_fmt
, plural_fmt
, n
);
534 text_info
ti (fmt
, &ap
, 0, NULL
, &rich_loc
);
537 pp_output_formatted_text (pp
);
541 label_text result
= label_text::take (xstrdup (pp_formatted_text (pp
)));
546 #endif /* #if ENABLE_ANALYZER */