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