Branch data Line data Source code
1 : : /* Utility functions for the analyzer.
2 : : Copyright (C) 2019-2025 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 "analyzer/common.h"
22 : :
23 : : #include "tree-pretty-print.h"
24 : : #include "diagnostics/event-id.h"
25 : : #include "tree-dfa.h"
26 : : #include "intl.h"
27 : :
28 : : #if ENABLE_ANALYZER
29 : :
30 : : namespace ana {
31 : :
32 : : /* Workaround for missing location information for some stmts,
33 : : which ultimately should be solved by fixing the frontends
34 : : to provide the locations (TODO). */
35 : :
36 : : location_t
37 : 17735 : get_stmt_location (const gimple *stmt, function *fun)
38 : : {
39 : 17735 : if (!stmt)
40 : : return UNKNOWN_LOCATION;
41 : 17656 : if (get_pure_location (stmt->location) == UNKNOWN_LOCATION)
42 : : {
43 : : /* Workaround for missing location information for clobber
44 : : stmts, which seem to lack location information in the C frontend
45 : : at least. Created by gimplify_bind_expr, which uses the
46 : : BLOCK_SOURCE_END_LOCATION (BIND_EXPR_BLOCK (bind_expr))
47 : : but this is never set up when the block is created in
48 : : c_end_compound_stmt's pop_scope.
49 : : TODO: fix this missing location information.
50 : :
51 : : For now, as a hackish workaround, use the location of the end of
52 : : the function. */
53 : 347 : if (gimple_clobber_p (stmt) && fun)
54 : 347 : return fun->function_end_locus;
55 : : }
56 : :
57 : 17309 : return stmt->location;
58 : : }
59 : :
60 : : static tree
61 : : fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited);
62 : :
63 : : /* Attemp to generate a tree for the LHS of ASSIGN_STMT.
64 : : VISITED must be non-NULL; it is used to ensure termination. */
65 : :
66 : : static tree
67 : 309361 : get_diagnostic_tree_for_gassign_1 (const gassign *assign_stmt,
68 : : hash_set<tree> *visited)
69 : : {
70 : 309361 : enum tree_code code = gimple_assign_rhs_code (assign_stmt);
71 : :
72 : : /* Reverse the effect of extract_ops_from_tree during
73 : : gimplification. */
74 : 309361 : switch (get_gimple_rhs_class (code))
75 : : {
76 : 0 : default:
77 : 0 : case GIMPLE_INVALID_RHS:
78 : 0 : gcc_unreachable ();
79 : 134178 : case GIMPLE_TERNARY_RHS:
80 : 134178 : case GIMPLE_BINARY_RHS:
81 : 134178 : case GIMPLE_UNARY_RHS:
82 : 134178 : {
83 : 134178 : tree t = make_node (code);
84 : 134178 : TREE_TYPE (t) = TREE_TYPE (gimple_assign_lhs (assign_stmt));
85 : 134178 : unsigned num_rhs_args = gimple_num_ops (assign_stmt) - 1;
86 : 358415 : for (unsigned i = 0; i < num_rhs_args; i++)
87 : : {
88 : 224237 : tree op = gimple_op (assign_stmt, i + 1);
89 : 224237 : if (op)
90 : : {
91 : 224237 : op = fixup_tree_for_diagnostic_1 (op, visited);
92 : 224237 : if (op == NULL_TREE)
93 : : return NULL_TREE;
94 : : }
95 : 224237 : TREE_OPERAND (t, i) = op;
96 : : }
97 : : return t;
98 : : }
99 : 175183 : case GIMPLE_SINGLE_RHS:
100 : 175183 : {
101 : 175183 : tree op = gimple_op (assign_stmt, 1);
102 : 175183 : op = fixup_tree_for_diagnostic_1 (op, visited);
103 : 175183 : return op;
104 : : }
105 : : }
106 : : }
107 : :
108 : : /* Subroutine of fixup_tree_for_diagnostic_1, called on SSA names.
109 : : Attempt to reconstruct a tree expression for SSA_NAME
110 : : based on its def-stmt.
111 : : SSA_NAME must be non-NULL.
112 : : VISITED must be non-NULL; it is used to ensure termination.
113 : :
114 : : Return NULL_TREE if there is a problem. */
115 : :
116 : : static tree
117 : 128745 : maybe_reconstruct_from_def_stmt (tree ssa_name,
118 : : hash_set<tree> *visited)
119 : : {
120 : : /* Ensure termination. */
121 : 128745 : if (visited->contains (ssa_name))
122 : : return NULL_TREE;
123 : 128655 : visited->add (ssa_name);
124 : :
125 : 128655 : gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
126 : :
127 : 128655 : switch (gimple_code (def_stmt))
128 : : {
129 : 0 : default:
130 : 0 : gcc_unreachable ();
131 : : case GIMPLE_ASM:
132 : : case GIMPLE_NOP:
133 : : case GIMPLE_PHI:
134 : : /* Can't handle these. */
135 : : return NULL_TREE;
136 : 106809 : case GIMPLE_ASSIGN:
137 : 106809 : return get_diagnostic_tree_for_gassign_1
138 : 106809 : (as_a <const gassign *> (def_stmt), visited);
139 : 19507 : case GIMPLE_CALL:
140 : 19507 : {
141 : 19507 : gcall *call_stmt = as_a <gcall *> (def_stmt);
142 : 19507 : tree return_type = gimple_call_return_type (call_stmt);
143 : 19507 : tree fn = fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt),
144 : : visited);
145 : 19507 : if (fn == NULL_TREE)
146 : : return NULL_TREE;
147 : 19267 : unsigned num_args = gimple_call_num_args (call_stmt);
148 : 19267 : auto_vec<tree> args (num_args);
149 : 42011 : for (unsigned i = 0; i < num_args; i++)
150 : : {
151 : 22744 : tree arg = gimple_call_arg (call_stmt, i);
152 : 22744 : arg = fixup_tree_for_diagnostic_1 (arg, visited);
153 : 22744 : if (arg == NULL_TREE)
154 : 0 : return NULL_TREE;
155 : 22744 : args.quick_push (arg);
156 : : }
157 : 19267 : gcc_assert (fn);
158 : 19267 : return build_call_array_loc (gimple_location (call_stmt),
159 : : return_type, fn,
160 : 19267 : num_args, args.address ());
161 : 19267 : }
162 : : break;
163 : : }
164 : : }
165 : :
166 : : /* Subroutine of fixup_tree_for_diagnostic: attempt to fixup EXPR,
167 : : which can be NULL.
168 : : VISITED must be non-NULL; it is used to ensure termination. */
169 : :
170 : : static tree
171 : 469487 : fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited)
172 : : {
173 : 469487 : if (expr
174 : 456961 : && TREE_CODE (expr) == SSA_NAME
175 : 690293 : && (SSA_NAME_VAR (expr) == NULL_TREE
176 : 92551 : || DECL_ARTIFICIAL (SSA_NAME_VAR (expr))))
177 : : {
178 : 128944 : if (tree var = SSA_NAME_VAR (expr))
179 : 689 : if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
180 : 199 : return DECL_DEBUG_EXPR (var);
181 : 128745 : if (tree expr2 = maybe_reconstruct_from_def_stmt (expr, visited))
182 : : return expr2;
183 : : }
184 : : return expr;
185 : : }
186 : :
187 : : /* We don't want to print '<unknown>' in our diagnostics (PR analyzer/99771),
188 : : but sometimes we generate diagnostics involving an ssa name for a
189 : : temporary.
190 : :
191 : : Work around this by attempting to reconstruct a tree expression for
192 : : such temporaries based on their def-stmts.
193 : :
194 : : Otherwise return EXPR.
195 : :
196 : : EXPR can be NULL. */
197 : :
198 : : tree
199 : 27816 : fixup_tree_for_diagnostic (tree expr)
200 : : {
201 : 27816 : hash_set<tree> visited;
202 : 27816 : return fixup_tree_for_diagnostic_1 (expr, &visited);
203 : 27816 : }
204 : :
205 : : /* Attempt to generate a tree for the LHS of ASSIGN_STMT. */
206 : :
207 : : tree
208 : 202552 : get_diagnostic_tree_for_gassign (const gassign *assign_stmt)
209 : : {
210 : 202552 : hash_set<tree> visited;
211 : 202552 : return get_diagnostic_tree_for_gassign_1 (assign_stmt, &visited);
212 : 202552 : }
213 : :
214 : : /* Generate a JSON value for NODE, which can be NULL_TREE.
215 : : This is intended for debugging the analyzer rather than serialization and
216 : : thus is a string (or null, for NULL_TREE). */
217 : :
218 : : std::unique_ptr<json::value>
219 : 38 : tree_to_json (tree node)
220 : : {
221 : 38 : if (!node)
222 : 0 : return std::make_unique<json::literal> (json::JSON_NULL);
223 : :
224 : 38 : pretty_printer pp;
225 : 38 : dump_generic_node (&pp, node, 0, TDF_VOPS|TDF_MEMSYMS, false);
226 : 38 : return std::make_unique<json::string> (pp_formatted_text (&pp));
227 : 38 : }
228 : :
229 : : /* Generate a JSON value for EVENT_ID.
230 : : This is intended for debugging the analyzer rather than serialization and
231 : : thus is a string matching those seen in event messags (or null,
232 : : for unknown). */
233 : :
234 : : std::unique_ptr<json::value>
235 : 85 : diagnostic_event_id_to_json (const diagnostics::paths::event_id_t &event_id)
236 : : {
237 : 85 : if (event_id.known_p ())
238 : : {
239 : 85 : pretty_printer pp;
240 : 85 : pp_printf (&pp, "%@", &event_id);
241 : 85 : return std::make_unique<json::string> (pp_formatted_text (&pp));
242 : 85 : }
243 : : else
244 : 0 : return std::make_unique<json::literal> (json::JSON_NULL);
245 : : }
246 : :
247 : : /* Generate a JSON value for OFFSET.
248 : : This is intended for debugging the analyzer rather than serialization and
249 : : thus is a string. */
250 : :
251 : : std::unique_ptr<json::value>
252 : 8 : bit_offset_to_json (const bit_offset_t &offset)
253 : : {
254 : 8 : pretty_printer pp;
255 : 8 : pp_wide_int_large (&pp, offset, SIGNED);
256 : 8 : return std::make_unique<json::string> (pp_formatted_text (&pp));
257 : 8 : }
258 : :
259 : : /* Generate a JSON value for OFFSET.
260 : : This is intended for debugging the analyzer rather than serialization and
261 : : thus is a string. */
262 : :
263 : : std::unique_ptr<json::value>
264 : 8 : byte_offset_to_json (const byte_offset_t &offset)
265 : : {
266 : 8 : pretty_printer pp;
267 : 8 : pp_wide_int_large (&pp, offset, SIGNED);
268 : 8 : return std::make_unique<json::string> (pp_formatted_text (&pp));
269 : 8 : }
270 : :
271 : : /* Workaround for lack of const-correctness of ssa_default_def. */
272 : :
273 : : tree
274 : 33098 : get_ssa_default_def (const function &fun, tree var)
275 : : {
276 : 33098 : return ssa_default_def (const_cast <function *> (&fun), var);
277 : : }
278 : :
279 : : } // namespace ana
280 : :
281 : : /* Helper function for checkers. Is the CALL to the given function name,
282 : : and with the given number of arguments?
283 : :
284 : : This doesn't resolve function pointers via the region model;
285 : : is_named_call_p should be used instead, using a fndecl from
286 : : get_fndecl_for_call; this function should only be used for special cases
287 : : where it's not practical to get at the region model, or for special
288 : : analyzer functions such as __analyzer_dump.
289 : :
290 : : If LOOK_IN_STD is true, then also look for within std:: for the name. */
291 : :
292 : : bool
293 : 565193 : is_special_named_call_p (const gcall &call, const char *funcname,
294 : : unsigned int num_args, bool look_in_std)
295 : : {
296 : 565193 : gcc_assert (funcname);
297 : :
298 : 565193 : tree fndecl = gimple_call_fndecl (&call);
299 : 565193 : if (!fndecl)
300 : : return false;
301 : :
302 : 549687 : if (is_named_call_p (fndecl, funcname, call, num_args))
303 : : return true;
304 : 547770 : if (look_in_std)
305 : 18994 : if (is_std_named_call_p (fndecl, funcname, call, num_args))
306 : : return true;
307 : : return false;
308 : : }
309 : :
310 : : /* Helper function for checkers. Is FNDECL an extern fndecl at file scope
311 : : that has the given FUNCNAME?
312 : :
313 : : Compare with special_function_p in calls.cc. */
314 : :
315 : : bool
316 : 2384597 : is_named_call_p (const_tree fndecl, const char *funcname)
317 : : {
318 : 2384597 : gcc_assert (fndecl);
319 : 2384597 : gcc_assert (funcname);
320 : :
321 : 2384597 : if (!maybe_special_function_p (fndecl))
322 : : return false;
323 : :
324 : 2200334 : tree identifier = DECL_NAME (fndecl);
325 : 2200334 : const char *name = IDENTIFIER_POINTER (identifier);
326 : 2200334 : const char *tname = name;
327 : :
328 : : /* Potentially disregard prefix _ or __ in FNDECL's name, but not if
329 : : FUNCNAME itself has leading underscores (e.g. when looking for
330 : : "__analyzer_eval"). */
331 : 2200334 : if (funcname[0] != '_' && name[0] == '_')
332 : : {
333 : 789938 : if (name[1] == '_')
334 : 785301 : tname += 2;
335 : : else
336 : 4637 : tname += 1;
337 : : }
338 : :
339 : 2200334 : return 0 == strcmp (tname, funcname);
340 : : }
341 : :
342 : : /* Return true if FNDECL is within the namespace "std".
343 : : Compare with cp/typeck.cc: decl_in_std_namespace_p, but this doesn't
344 : : rely on being the C++ FE (or handle inline namespaces inside of std). */
345 : :
346 : : bool
347 : 613797 : is_std_function_p (const_tree fndecl)
348 : : {
349 : 613797 : tree name_decl = DECL_NAME (fndecl);
350 : 613797 : if (!name_decl)
351 : : return false;
352 : 613797 : if (!DECL_CONTEXT (fndecl))
353 : : return false;
354 : 607902 : if (TREE_CODE (DECL_CONTEXT (fndecl)) != NAMESPACE_DECL)
355 : : return false;
356 : 2845 : tree ns = DECL_CONTEXT (fndecl);
357 : 2845 : if (!(DECL_CONTEXT (ns) == NULL_TREE
358 : 2777 : || TREE_CODE (DECL_CONTEXT (ns)) == TRANSLATION_UNIT_DECL))
359 : : return false;
360 : 2845 : if (!DECL_NAME (ns))
361 : : return false;
362 : 2845 : return id_equal ("std", DECL_NAME (ns));
363 : : }
364 : :
365 : : /* Like is_named_call_p, but look for std::FUNCNAME. */
366 : :
367 : : bool
368 : 266339 : is_std_named_call_p (const_tree fndecl, const char *funcname)
369 : : {
370 : 266339 : gcc_assert (fndecl);
371 : 266339 : gcc_assert (funcname);
372 : :
373 : 266339 : if (!is_std_function_p (fndecl))
374 : : return false;
375 : :
376 : 1301 : tree identifier = DECL_NAME (fndecl);
377 : 1301 : const char *name = IDENTIFIER_POINTER (identifier);
378 : 1301 : const char *tname = name;
379 : :
380 : : /* Don't disregard prefix _ or __ in FNDECL's name. */
381 : :
382 : 1301 : return 0 == strcmp (tname, funcname);
383 : : }
384 : :
385 : : /* Helper function for checkers. Is FNDECL an extern fndecl at file scope
386 : : that has the given FUNCNAME, and does CALL have the given number of
387 : : arguments? */
388 : :
389 : : bool
390 : 2062581 : is_named_call_p (const_tree fndecl, const char *funcname,
391 : : const gcall &call, unsigned int num_args)
392 : : {
393 : 2062581 : gcc_assert (fndecl);
394 : 2062581 : gcc_assert (funcname);
395 : :
396 : 2062581 : if (!is_named_call_p (fndecl, funcname))
397 : : return false;
398 : :
399 : 19999 : if (gimple_call_num_args (&call) != num_args)
400 : 744 : return false;
401 : :
402 : : return true;
403 : : }
404 : :
405 : : /* Like is_named_call_p, but check for std::FUNCNAME. */
406 : :
407 : : bool
408 : 266268 : is_std_named_call_p (const_tree fndecl, const char *funcname,
409 : : const gcall &call, unsigned int num_args)
410 : : {
411 : 266268 : gcc_assert (fndecl);
412 : 266268 : gcc_assert (funcname);
413 : :
414 : 266268 : if (!is_std_named_call_p (fndecl, funcname))
415 : : return false;
416 : :
417 : 12 : if (gimple_call_num_args (&call) != num_args)
418 : 0 : return false;
419 : :
420 : : return true;
421 : : }
422 : :
423 : : /* Return true if stmt is a setjmp or sigsetjmp call. */
424 : :
425 : : bool
426 : 66303 : is_setjmp_call_p (const gcall &call)
427 : : {
428 : 66303 : if (is_special_named_call_p (call, "setjmp", 1)
429 : 66303 : || is_special_named_call_p (call, "sigsetjmp", 2))
430 : : /* region_model::on_setjmp requires a pointer. */
431 : 70 : if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (&call, 0))))
432 : : return true;
433 : :
434 : : return false;
435 : : }
436 : :
437 : : /* Return true if stmt is a longjmp or siglongjmp call. */
438 : :
439 : : bool
440 : 57716 : is_longjmp_call_p (const gcall &call)
441 : : {
442 : 57716 : if (is_special_named_call_p (call, "longjmp", 2)
443 : 57716 : || is_special_named_call_p (call, "siglongjmp", 2))
444 : : /* exploded_node::on_longjmp requires a pointer for the initial
445 : : argument. */
446 : 67 : if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (&call, 0))))
447 : : return true;
448 : :
449 : : return false;
450 : : }
451 : :
452 : : bool
453 : 57653 : is_cxa_throw_p (const gcall &call)
454 : : {
455 : 57653 : tree fndecl = gimple_call_fndecl (&call);
456 : 57653 : if (!fndecl)
457 : : return false;
458 : :
459 : 56033 : return is_named_call_p (fndecl, "__cxa_throw");
460 : : }
461 : :
462 : : bool
463 : 57544 : is_cxa_rethrow_p (const gcall &call)
464 : : {
465 : 57544 : tree fndecl = gimple_call_fndecl (&call);
466 : 57544 : if (!fndecl)
467 : : return false;
468 : :
469 : 55924 : return is_named_call_p (fndecl, "__cxa_rethrow");
470 : : }
471 : :
472 : : /* For a CALL that matched is_special_named_call_p or is_named_call_p for
473 : : some name, return a name for the called function suitable for use in
474 : : diagnostics (stripping the leading underscores). */
475 : :
476 : : const char *
477 : 130 : get_user_facing_name (const gcall &call)
478 : : {
479 : 130 : tree fndecl = gimple_call_fndecl (&call);
480 : 130 : gcc_assert (fndecl);
481 : :
482 : 130 : tree identifier = DECL_NAME (fndecl);
483 : 130 : gcc_assert (identifier);
484 : :
485 : 130 : const char *name = IDENTIFIER_POINTER (identifier);
486 : :
487 : : /* Strip prefix _ or __ in FNDECL's name. */
488 : 130 : if (name[0] == '_')
489 : : {
490 : 0 : if (name[1] == '_')
491 : 0 : return name + 2;
492 : : else
493 : 0 : return name + 1;
494 : : }
495 : :
496 : : return name;
497 : : }
498 : :
499 : : /* Generate a label_text instance by formatting FMT, using a
500 : : temporary clone of the global_dc's printer (thus using its
501 : : formatting callbacks).
502 : :
503 : : Colorize if the global_dc supports colorization and CAN_COLORIZE is
504 : : true. */
505 : :
506 : : label_text
507 : 4063 : make_label_text (bool can_colorize, const char *fmt, ...)
508 : : {
509 : 4063 : std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
510 : 4063 : pp_clear_output_area (pp.get ());
511 : :
512 : 4063 : if (!can_colorize)
513 : 4060 : pp_show_color (pp.get ()) = false;
514 : :
515 : 4063 : rich_location rich_loc (line_table, UNKNOWN_LOCATION);
516 : :
517 : 4063 : va_list ap;
518 : :
519 : 4063 : va_start (ap, fmt);
520 : :
521 : 4063 : text_info ti (_(fmt), &ap, 0, nullptr, &rich_loc);
522 : 4063 : pp_format (pp.get (), &ti);
523 : 4063 : pp_output_formatted_text (pp.get ());
524 : :
525 : 4063 : va_end (ap);
526 : :
527 : 4063 : label_text result = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
528 : 8126 : return result;
529 : 4063 : }
530 : :
531 : : /* As above, but with singular vs plural. */
532 : :
533 : : label_text
534 : 0 : make_label_text_n (bool can_colorize, unsigned HOST_WIDE_INT n,
535 : : const char *singular_fmt,
536 : : const char *plural_fmt, ...)
537 : : {
538 : 0 : std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
539 : 0 : pp_clear_output_area (pp.get ());
540 : :
541 : 0 : if (!can_colorize)
542 : 0 : pp_show_color (pp.get ()) = false;
543 : :
544 : 0 : rich_location rich_loc (line_table, UNKNOWN_LOCATION);
545 : :
546 : 0 : va_list ap;
547 : :
548 : 0 : va_start (ap, plural_fmt);
549 : :
550 : 0 : const char *fmt = ngettext (singular_fmt, plural_fmt, n);
551 : :
552 : 0 : text_info ti (fmt, &ap, 0, nullptr, &rich_loc);
553 : :
554 : 0 : pp_format (pp.get (), &ti);
555 : 0 : pp_output_formatted_text (pp.get ());
556 : :
557 : 0 : va_end (ap);
558 : :
559 : 0 : label_text result
560 : 0 : = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
561 : 0 : return result;
562 : 0 : }
563 : :
564 : : #endif /* #if ENABLE_ANALYZER */
|