Line data Source code
1 : /* Utility functions for the analyzer.
2 : Copyright (C) 2019-2026 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 126987 : get_stmt_location (const gimple *stmt, function *fun)
38 : {
39 126987 : if (!stmt)
40 : return UNKNOWN_LOCATION;
41 126987 : 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 11278 : if (gimple_clobber_p (stmt) && fun)
54 3097 : return fun->function_end_locus;
55 : }
56 :
57 123890 : 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 396721 : get_diagnostic_tree_for_gassign_1 (const gassign *assign_stmt,
68 : hash_set<tree> *visited)
69 : {
70 396721 : enum tree_code code = gimple_assign_rhs_code (assign_stmt);
71 :
72 : /* Reverse the effect of extract_ops_from_tree during
73 : gimplification. */
74 396721 : switch (get_gimple_rhs_class (code))
75 : {
76 0 : default:
77 0 : case GIMPLE_INVALID_RHS:
78 0 : gcc_unreachable ();
79 208261 : case GIMPLE_TERNARY_RHS:
80 208261 : case GIMPLE_BINARY_RHS:
81 208261 : case GIMPLE_UNARY_RHS:
82 208261 : {
83 208261 : tree t = make_node (code);
84 208261 : TREE_TYPE (t) = TREE_TYPE (gimple_assign_lhs (assign_stmt));
85 208261 : unsigned num_rhs_args = gimple_num_ops (assign_stmt) - 1;
86 541556 : for (unsigned i = 0; i < num_rhs_args; i++)
87 : {
88 333295 : tree op = gimple_op (assign_stmt, i + 1);
89 333295 : if (op)
90 : {
91 333295 : op = fixup_tree_for_diagnostic_1 (op, visited);
92 333295 : if (op == NULL_TREE)
93 : return NULL_TREE;
94 : }
95 333295 : TREE_OPERAND (t, i) = op;
96 : }
97 : return t;
98 : }
99 188460 : case GIMPLE_SINGLE_RHS:
100 188460 : {
101 188460 : tree op = gimple_op (assign_stmt, 1);
102 188460 : op = fixup_tree_for_diagnostic_1 (op, visited);
103 188460 : 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 182825 : maybe_reconstruct_from_def_stmt (tree ssa_name,
118 : hash_set<tree> *visited)
119 : {
120 : /* Ensure termination. */
121 182825 : if (visited->contains (ssa_name))
122 : return NULL_TREE;
123 182774 : visited->add (ssa_name);
124 :
125 182774 : gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
126 :
127 182774 : 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 165957 : case GIMPLE_ASSIGN:
137 165957 : return get_diagnostic_tree_for_gassign_1
138 165957 : (as_a <const gassign *> (def_stmt), visited);
139 15123 : case GIMPLE_CALL:
140 15123 : {
141 15123 : gcall *call_stmt = as_a <gcall *> (def_stmt);
142 15123 : tree return_type = gimple_call_return_type (call_stmt);
143 15123 : tree fn = fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt),
144 : visited);
145 15123 : if (fn == NULL_TREE)
146 : return NULL_TREE;
147 14787 : unsigned num_args = gimple_call_num_args (call_stmt);
148 14787 : auto_vec<tree> args (num_args);
149 34786 : for (unsigned i = 0; i < num_args; i++)
150 : {
151 19999 : tree arg = gimple_call_arg (call_stmt, i);
152 19999 : arg = fixup_tree_for_diagnostic_1 (arg, visited);
153 19999 : if (arg == NULL_TREE)
154 0 : return NULL_TREE;
155 19999 : args.quick_push (arg);
156 : }
157 14787 : gcc_assert (fn);
158 14787 : return build_call_array_loc (gimple_location (call_stmt),
159 : return_type, fn,
160 14787 : num_args, args.address ());
161 14787 : }
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 583983 : fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited)
172 : {
173 583983 : if (expr
174 571833 : && TREE_CODE (expr) == SSA_NAME
175 875904 : && (SSA_NAME_VAR (expr) == NULL_TREE
176 109473 : || DECL_ARTIFICIAL (SSA_NAME_VAR (expr))))
177 : {
178 182962 : if (tree var = SSA_NAME_VAR (expr))
179 514 : if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
180 137 : return DECL_DEBUG_EXPR (var);
181 182825 : 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 27106 : fixup_tree_for_diagnostic (tree expr)
200 : {
201 27106 : hash_set<tree> visited;
202 27106 : return fixup_tree_for_diagnostic_1 (expr, &visited);
203 27106 : }
204 :
205 : /* Attempt to generate a tree for the LHS of ASSIGN_STMT. */
206 :
207 : tree
208 230764 : get_diagnostic_tree_for_gassign (const gassign *assign_stmt)
209 : {
210 230764 : hash_set<tree> visited;
211 230764 : return get_diagnostic_tree_for_gassign_1 (assign_stmt, &visited);
212 230764 : }
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 44 : tree_to_json (tree node)
220 : {
221 44 : if (!node)
222 0 : return std::make_unique<json::literal> (json::JSON_NULL);
223 :
224 44 : pretty_printer pp;
225 44 : dump_generic_node (&pp, node, 0, TDF_VOPS|TDF_MEMSYMS, false);
226 44 : return std::make_unique<json::string> (pp_formatted_text (&pp));
227 44 : }
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 159 : diagnostic_event_id_to_json (const diagnostics::paths::event_id_t &event_id)
236 : {
237 159 : if (event_id.known_p ())
238 : {
239 159 : pretty_printer pp;
240 159 : pp_printf (&pp, "%@", &event_id);
241 159 : return std::make_unique<json::string> (pp_formatted_text (&pp));
242 159 : }
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 31954 : get_ssa_default_def (const function &fun, tree var)
275 : {
276 31954 : 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 397132 : is_special_named_call_p (const gcall &call, const char *funcname,
294 : unsigned int num_args, bool look_in_std)
295 : {
296 397132 : gcc_assert (funcname);
297 :
298 397132 : tree fndecl = gimple_call_fndecl (&call);
299 397132 : if (!fndecl)
300 : return false;
301 :
302 369632 : if (is_named_call_p (fndecl, funcname, call, num_args))
303 : return true;
304 368056 : if (look_in_std)
305 0 : 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 1841265 : is_named_call_p (const_tree fndecl, const char *funcname)
317 : {
318 1841265 : gcc_assert (fndecl);
319 1841265 : gcc_assert (funcname);
320 :
321 1841265 : if (!maybe_special_function_p (fndecl))
322 : return false;
323 :
324 1807892 : tree identifier = DECL_NAME (fndecl);
325 1807892 : const char *name = IDENTIFIER_POINTER (identifier);
326 1807892 : 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 1807892 : if (funcname[0] != '_' && name[0] == '_')
332 : {
333 733376 : if (name[1] == '_')
334 729012 : tname += 2;
335 : else
336 4364 : tname += 1;
337 : }
338 :
339 1807892 : 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 521516 : is_std_function_p (const_tree fndecl)
348 : {
349 521516 : tree name_decl = DECL_NAME (fndecl);
350 521516 : if (!name_decl)
351 : return false;
352 521516 : if (!DECL_CONTEXT (fndecl))
353 : return false;
354 516811 : if (TREE_CODE (DECL_CONTEXT (fndecl)) != NAMESPACE_DECL)
355 : return false;
356 457 : tree ns = DECL_CONTEXT (fndecl);
357 457 : if (!(DECL_CONTEXT (ns) == NULL_TREE
358 452 : || TREE_CODE (DECL_CONTEXT (ns)) == TRANSLATION_UNIT_DECL))
359 : return false;
360 457 : if (!DECL_NAME (ns))
361 : return false;
362 457 : return id_equal ("std", DECL_NAME (ns));
363 : }
364 :
365 : /* Like is_named_call_p, but look for std::FUNCNAME. */
366 :
367 : bool
368 198460 : is_std_named_call_p (const_tree fndecl, const char *funcname)
369 : {
370 198460 : gcc_assert (fndecl);
371 198460 : gcc_assert (funcname);
372 :
373 198460 : if (!is_std_function_p (fndecl))
374 : return false;
375 :
376 44 : tree identifier = DECL_NAME (fndecl);
377 44 : const char *name = IDENTIFIER_POINTER (identifier);
378 44 : const char *tname = name;
379 :
380 : /* Don't disregard prefix _ or __ in FNDECL's name. */
381 :
382 44 : 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 1593574 : is_named_call_p (const_tree fndecl, const char *funcname,
391 : const gcall &call, unsigned int num_args)
392 : {
393 1593574 : gcc_assert (fndecl);
394 1593574 : gcc_assert (funcname);
395 :
396 1593574 : if (!is_named_call_p (fndecl, funcname))
397 : return false;
398 :
399 19503 : if (gimple_call_num_args (&call) != num_args)
400 681 : return false;
401 :
402 : return true;
403 : }
404 :
405 : /* Like is_named_call_p, but check for std::FUNCNAME. */
406 :
407 : bool
408 198389 : is_std_named_call_p (const_tree fndecl, const char *funcname,
409 : const gcall &call, unsigned int num_args)
410 : {
411 198389 : gcc_assert (fndecl);
412 198389 : gcc_assert (funcname);
413 :
414 198389 : 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 41569 : is_setjmp_call_p (const gcall &call)
427 : {
428 41569 : if (is_special_named_call_p (call, "setjmp", 1)
429 41569 : || is_special_named_call_p (call, "sigsetjmp", 2))
430 : /* region_model::on_setjmp requires a pointer. */
431 33 : 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 41540 : is_longjmp_call_p (const gcall &call)
441 : {
442 41540 : if (is_special_named_call_p (call, "longjmp", 2)
443 41540 : || is_special_named_call_p (call, "siglongjmp", 2))
444 : /* exploded_node::on_longjmp requires a pointer for the initial
445 : argument. */
446 45 : if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (&call, 0))))
447 : return true;
448 :
449 : return false;
450 : }
451 :
452 : bool
453 41499 : is_cxa_throw_p (const gcall &call)
454 : {
455 41499 : tree fndecl = gimple_call_fndecl (&call);
456 41499 : if (!fndecl)
457 : return false;
458 :
459 38557 : return is_named_call_p (fndecl, "__cxa_throw");
460 : }
461 :
462 : bool
463 41426 : is_cxa_rethrow_p (const gcall &call)
464 : {
465 41426 : tree fndecl = gimple_call_fndecl (&call);
466 41426 : if (!fndecl)
467 : return false;
468 :
469 38484 : return is_named_call_p (fndecl, "__cxa_rethrow");
470 : }
471 :
472 : bool
473 554 : is_cxa_end_catch_p (const gcall &call)
474 : {
475 554 : tree fndecl = gimple_call_fndecl (&call);
476 554 : if (!fndecl)
477 : return false;
478 :
479 542 : return is_named_call_p (fndecl, "__cxa_end_catch");
480 : }
481 :
482 : /* For a CALL that matched is_special_named_call_p or is_named_call_p for
483 : some name, return a name for the called function suitable for use in
484 : diagnostics (stripping the leading underscores). */
485 :
486 : const char *
487 133 : get_user_facing_name (const gcall &call)
488 : {
489 133 : tree fndecl = gimple_call_fndecl (&call);
490 133 : gcc_assert (fndecl);
491 :
492 133 : tree identifier = DECL_NAME (fndecl);
493 133 : gcc_assert (identifier);
494 :
495 133 : const char *name = IDENTIFIER_POINTER (identifier);
496 :
497 : /* Strip prefix _ or __ in FNDECL's name. */
498 133 : if (name[0] == '_')
499 : {
500 0 : if (name[1] == '_')
501 0 : return name + 2;
502 : else
503 0 : return name + 1;
504 : }
505 :
506 : return name;
507 : }
508 :
509 : /* Generate a label_text instance by formatting FMT, using a
510 : temporary clone of the global_dc's printer (thus using its
511 : formatting callbacks).
512 :
513 : Colorize if the global_dc supports colorization and CAN_COLORIZE is
514 : true. */
515 :
516 : label_text
517 3940 : make_label_text (bool can_colorize, const char *fmt, ...)
518 : {
519 3940 : std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
520 3940 : pp_clear_output_area (pp.get ());
521 :
522 3940 : if (!can_colorize)
523 3937 : pp_show_color (pp.get ()) = false;
524 :
525 3940 : rich_location rich_loc (line_table, UNKNOWN_LOCATION);
526 :
527 3940 : va_list ap;
528 :
529 3940 : va_start (ap, fmt);
530 :
531 3940 : text_info ti (_(fmt), &ap, 0, nullptr, &rich_loc);
532 3940 : pp_format (pp.get (), &ti);
533 3940 : pp_output_formatted_text (pp.get ());
534 :
535 3940 : va_end (ap);
536 :
537 3940 : label_text result = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
538 7880 : return result;
539 3940 : }
540 :
541 : /* As above, but with singular vs plural. */
542 :
543 : label_text
544 0 : make_label_text_n (bool can_colorize, unsigned HOST_WIDE_INT n,
545 : const char *singular_fmt,
546 : const char *plural_fmt, ...)
547 : {
548 0 : std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
549 0 : pp_clear_output_area (pp.get ());
550 :
551 0 : if (!can_colorize)
552 0 : pp_show_color (pp.get ()) = false;
553 :
554 0 : rich_location rich_loc (line_table, UNKNOWN_LOCATION);
555 :
556 0 : va_list ap;
557 :
558 0 : va_start (ap, plural_fmt);
559 :
560 0 : const char *fmt = ngettext (singular_fmt, plural_fmt, n);
561 :
562 0 : text_info ti (fmt, &ap, 0, nullptr, &rich_loc);
563 :
564 0 : pp_format (pp.get (), &ti);
565 0 : pp_output_formatted_text (pp.get ());
566 :
567 0 : va_end (ap);
568 :
569 0 : label_text result
570 0 : = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
571 0 : return result;
572 0 : }
573 :
574 : #endif /* #if ENABLE_ANALYZER */
|