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 137854 : get_stmt_location (const gimple *stmt, function *fun)
48 : {
49 137854 : if (!stmt)
50 : return UNKNOWN_LOCATION;
51 137854 : 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 12318 : if (gimple_clobber_p (stmt) && fun)
64 3439 : return fun->function_end_locus;
65 : }
66 :
67 134415 : return stmt->location;
68 : }
69 :
70 : static tree
71 : fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited);
72 :
73 : /* Attempt 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 436038 : get_diagnostic_tree_for_gassign_1 (const gassign *assign_stmt,
78 : hash_set<tree> *visited)
79 : {
80 436038 : enum tree_code code = gimple_assign_rhs_code (assign_stmt);
81 :
82 : /* Reverse the effect of extract_ops_from_tree during
83 : gimplification. */
84 436038 : switch (get_gimple_rhs_class (code))
85 : {
86 0 : default:
87 0 : case GIMPLE_INVALID_RHS:
88 0 : gcc_unreachable ();
89 224925 : case GIMPLE_TERNARY_RHS:
90 224925 : case GIMPLE_BINARY_RHS:
91 224925 : case GIMPLE_UNARY_RHS:
92 224925 : {
93 224925 : tree t = make_node (code);
94 224925 : TREE_TYPE (t) = TREE_TYPE (gimple_assign_lhs (assign_stmt));
95 224925 : unsigned num_rhs_args = gimple_num_ops (assign_stmt) - 1;
96 586379 : for (unsigned i = 0; i < num_rhs_args; i++)
97 : {
98 361454 : tree op = gimple_op (assign_stmt, i + 1);
99 361454 : if (op)
100 : {
101 361454 : op = fixup_tree_for_diagnostic_1 (op, visited);
102 361454 : if (op == NULL_TREE)
103 : return NULL_TREE;
104 : }
105 361454 : TREE_OPERAND (t, i) = op;
106 : }
107 : return t;
108 : }
109 211113 : case GIMPLE_SINGLE_RHS:
110 211113 : {
111 211113 : tree op = gimple_op (assign_stmt, 1);
112 211113 : op = fixup_tree_for_diagnostic_1 (op, visited);
113 211113 : 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 199330 : maybe_reconstruct_from_def_stmt (tree ssa_name,
128 : hash_set<tree> *visited)
129 : {
130 : /* Ensure termination. */
131 199330 : if (visited->contains (ssa_name))
132 : return NULL_TREE;
133 199256 : visited->add (ssa_name);
134 :
135 199256 : gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
136 :
137 199256 : 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 178718 : case GIMPLE_ASSIGN:
147 178718 : return get_diagnostic_tree_for_gassign_1
148 178718 : (as_a <const gassign *> (def_stmt), visited);
149 18077 : case GIMPLE_CALL:
150 18077 : {
151 18077 : gcall *call_stmt = as_a <gcall *> (def_stmt);
152 18077 : tree return_type = gimple_call_return_type (call_stmt);
153 18077 : tree fn = fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt),
154 : visited);
155 18077 : if (fn == NULL_TREE)
156 : return NULL_TREE;
157 17710 : unsigned num_args = gimple_call_num_args (call_stmt);
158 17710 : auto_vec<tree> args (num_args);
159 58872 : for (unsigned i = 0; i < num_args; i++)
160 : {
161 23452 : tree arg = gimple_call_arg (call_stmt, i);
162 23452 : arg = fixup_tree_for_diagnostic_1 (arg, visited);
163 23452 : if (arg == NULL_TREE)
164 0 : return NULL_TREE;
165 23452 : args.quick_push (arg);
166 : }
167 17710 : gcc_assert (fn);
168 17710 : return build_call_array_loc (gimple_location (call_stmt),
169 : return_type, fn,
170 17710 : num_args, args.address ());
171 17710 : }
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 642347 : fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited)
182 : {
183 642347 : if (expr
184 629509 : && TREE_CODE (expr) == SSA_NAME
185 959859 : && (SSA_NAME_VAR (expr) == NULL_TREE
186 119354 : || DECL_ARTIFICIAL (SSA_NAME_VAR (expr))))
187 : {
188 199467 : if (tree var = SSA_NAME_VAR (expr))
189 1309 : if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
190 137 : return DECL_DEBUG_EXPR (var);
191 199330 : if (tree expr2 = maybe_reconstruct_from_def_stmt (expr, visited))
192 196428 : 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 28251 : fixup_tree_for_diagnostic (tree expr)
210 : {
211 28251 : hash_set<tree> visited;
212 28251 : return fixup_tree_for_diagnostic_1 (expr, &visited);
213 28251 : }
214 :
215 : /* Attempt to generate a tree for the LHS of ASSIGN_STMT. */
216 :
217 : tree
218 257320 : get_diagnostic_tree_for_gassign (const gassign *assign_stmt)
219 : {
220 257320 : hash_set<tree> visited;
221 257320 : return get_diagnostic_tree_for_gassign_1 (assign_stmt, &visited);
222 257320 : }
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 messages (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 42409 : get_ssa_default_def (const function &fun, tree var)
285 : {
286 42409 : 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 424003 : is_special_named_call_p (const gcall &call, const char *funcname,
304 : unsigned int num_args, bool look_in_std)
305 : {
306 424003 : gcc_assert (funcname);
307 :
308 424003 : tree fndecl = gimple_call_fndecl (&call);
309 424003 : if (!fndecl)
310 : return false;
311 :
312 395895 : if (is_named_call_p (fndecl, funcname, call, num_args))
313 : return true;
314 394319 : 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 1928047 : is_named_call_p (const_tree fndecl, const char *funcname)
327 : {
328 1928047 : gcc_assert (fndecl);
329 1928047 : gcc_assert (funcname);
330 :
331 1928047 : if (!maybe_special_function_p (fndecl))
332 : return false;
333 :
334 1876058 : tree identifier = DECL_NAME (fndecl);
335 1876058 : const char *name = IDENTIFIER_POINTER (identifier);
336 1876058 : 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 1876058 : if (funcname[0] != '_' && name[0] == '_')
342 : {
343 768661 : if (name[1] == '_')
344 764297 : tname += 2;
345 : else
346 4364 : tname += 1;
347 : }
348 :
349 1876058 : return 0 == strcmp (tname, funcname);
350 : }
351 :
352 : /* Return true if FNDECL is declared directly within a top-level
353 : namespace named NS_NAME (e.g. "std" or "__cxxabiv1").
354 : Compare with cp/typeck.cc: decl_in_std_namespace_p, but this doesn't
355 : rely on being the C++ FE (or handle inline namespaces inside of std). */
356 :
357 : bool
358 899078 : is_fndecl_in_toplevel_namespace_p (const_tree fndecl, const char *ns_name)
359 : {
360 899078 : tree name_decl = DECL_NAME (fndecl);
361 899078 : if (!name_decl)
362 : return false;
363 :
364 899078 : if (!DECL_CONTEXT (fndecl))
365 : return false;
366 891559 : if (TREE_CODE (DECL_CONTEXT (fndecl)) != NAMESPACE_DECL)
367 : return false;
368 3716 : tree ns = DECL_CONTEXT (fndecl);
369 : /* Require the namespace itself to be at top level. */
370 3716 : if (!(DECL_CONTEXT (ns) == NULL_TREE
371 3706 : || TREE_CODE (DECL_CONTEXT (ns)) == TRANSLATION_UNIT_DECL))
372 : return false;
373 3620 : if (!DECL_NAME (ns))
374 : return false;
375 :
376 3620 : return id_equal (ns_name, DECL_NAME (ns));
377 : }
378 :
379 : /* Return true if FNDECL is within the namespace "std". */
380 :
381 : bool
382 553405 : is_std_function_p (const_tree fndecl)
383 : {
384 553405 : return is_fndecl_in_toplevel_namespace_p (fndecl, "std");
385 : }
386 :
387 : /* Return true if FNDECL is within the namespace "__cxxabiv1". */
388 :
389 : bool
390 345673 : is_cxxabi_function_p (const_tree fndecl)
391 : {
392 345673 : return is_fndecl_in_toplevel_namespace_p (fndecl, "__cxxabiv1");
393 : }
394 :
395 : /* Like is_named_call_p, but look for std::FUNCNAME. */
396 :
397 : bool
398 206533 : is_std_named_call_p (const_tree fndecl, const char *funcname)
399 : {
400 206533 : gcc_assert (fndecl);
401 206533 : gcc_assert (funcname);
402 :
403 206533 : if (!is_std_function_p (fndecl))
404 : return false;
405 :
406 564 : tree identifier = DECL_NAME (fndecl);
407 564 : const char *name = IDENTIFIER_POINTER (identifier);
408 564 : const char *tname = name;
409 :
410 : /* Don't disregard prefix _ or __ in FNDECL's name. */
411 :
412 564 : return 0 == strcmp (tname, funcname);
413 : }
414 :
415 : /* Helper function for checkers. Is FNDECL an extern fndecl at file scope
416 : that has the given FUNCNAME, and does CALL have the given number of
417 : arguments? */
418 :
419 : bool
420 1668276 : is_named_call_p (const_tree fndecl, const char *funcname,
421 : const gcall &call, unsigned int num_args)
422 : {
423 1668276 : gcc_assert (fndecl);
424 1668276 : gcc_assert (funcname);
425 :
426 1668276 : if (!is_named_call_p (fndecl, funcname))
427 : return false;
428 :
429 20350 : if (gimple_call_num_args (&call) != num_args)
430 1228 : return false;
431 :
432 : return true;
433 : }
434 :
435 : /* Like is_named_call_p, but check for std::FUNCNAME. */
436 :
437 : bool
438 206462 : is_std_named_call_p (const_tree fndecl, const char *funcname,
439 : const gcall &call, unsigned int num_args)
440 : {
441 206462 : gcc_assert (fndecl);
442 206462 : gcc_assert (funcname);
443 :
444 206462 : if (!is_std_named_call_p (fndecl, funcname))
445 : return false;
446 :
447 12 : if (gimple_call_num_args (&call) != num_args)
448 0 : return false;
449 :
450 : return true;
451 : }
452 :
453 : bool
454 44298 : is_cxa_throw_p (const gcall &call)
455 : {
456 44298 : tree fndecl = gimple_call_fndecl (&call);
457 44298 : if (!fndecl)
458 : return false;
459 :
460 41288 : return is_named_call_p (fndecl, "__cxa_throw");
461 : }
462 :
463 : bool
464 44188 : is_cxa_rethrow_p (const gcall &call)
465 : {
466 44188 : tree fndecl = gimple_call_fndecl (&call);
467 44188 : if (!fndecl)
468 : return false;
469 :
470 41178 : return is_named_call_p (fndecl, "__cxa_rethrow");
471 : }
472 :
473 : bool
474 526 : is_cxa_end_catch_p (const gcall &call)
475 : {
476 526 : tree fndecl = gimple_call_fndecl (&call);
477 526 : if (!fndecl)
478 : return false;
479 :
480 514 : return is_named_call_p (fndecl, "__cxa_end_catch");
481 : }
482 :
483 : /* For a CALL that matched is_special_named_call_p or is_named_call_p for
484 : some name, return a name for the called function suitable for use in
485 : diagnostics (stripping the leading underscores). */
486 :
487 : const char *
488 133 : get_user_facing_name (const gcall &call)
489 : {
490 133 : tree fndecl = gimple_call_fndecl (&call);
491 133 : gcc_assert (fndecl);
492 :
493 133 : tree identifier = DECL_NAME (fndecl);
494 133 : gcc_assert (identifier);
495 :
496 133 : const char *name = IDENTIFIER_POINTER (identifier);
497 :
498 : /* Strip prefix _ or __ in FNDECL's name. */
499 133 : if (name[0] == '_')
500 : {
501 0 : if (name[1] == '_')
502 0 : return name + 2;
503 : else
504 0 : return name + 1;
505 : }
506 :
507 : return name;
508 : }
509 :
510 : /* Generate a label_text instance by formatting FMT, using a
511 : temporary clone of the global_dc's printer (thus using its
512 : formatting callbacks).
513 :
514 : Colorize if the global_dc supports colorization and CAN_COLORIZE is
515 : true. */
516 :
517 : label_text
518 4126 : make_label_text (bool can_colorize, const char *fmt, ...)
519 : {
520 4126 : std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
521 4126 : pp_clear_output_area (pp.get ());
522 :
523 4126 : if (!can_colorize)
524 4123 : pp_show_color (pp.get ()) = false;
525 :
526 4126 : rich_location rich_loc (line_table, UNKNOWN_LOCATION);
527 :
528 4126 : va_list ap;
529 :
530 4126 : va_start (ap, fmt);
531 :
532 4126 : text_info ti (_(fmt), &ap, 0, nullptr, &rich_loc);
533 4126 : pp_format (pp.get (), &ti);
534 4126 : pp_output_formatted_text (pp.get ());
535 :
536 4126 : va_end (ap);
537 :
538 4126 : label_text result = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
539 8252 : return result;
540 4126 : }
541 :
542 : /* As above, but with singular vs plural. */
543 :
544 : label_text
545 0 : make_label_text_n (bool can_colorize, unsigned HOST_WIDE_INT n,
546 : const char *singular_fmt,
547 : const char *plural_fmt, ...)
548 : {
549 0 : std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
550 0 : pp_clear_output_area (pp.get ());
551 :
552 0 : if (!can_colorize)
553 0 : pp_show_color (pp.get ()) = false;
554 :
555 0 : rich_location rich_loc (line_table, UNKNOWN_LOCATION);
556 :
557 0 : va_list ap;
558 :
559 0 : va_start (ap, plural_fmt);
560 :
561 0 : const char *fmt = ngettext (singular_fmt, plural_fmt, n);
562 :
563 0 : text_info ti (fmt, &ap, 0, nullptr, &rich_loc);
564 :
565 0 : pp_format (pp.get (), &ti);
566 0 : pp_output_formatted_text (pp.get ());
567 :
568 0 : va_end (ap);
569 :
570 0 : label_text result
571 0 : = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
572 0 : return result;
573 0 : }
574 :
575 : #endif /* #if ENABLE_ANALYZER */
|