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 133506 : get_stmt_location (const gimple *stmt, function *fun)
48 : {
49 133506 : if (!stmt)
50 : return UNKNOWN_LOCATION;
51 133506 : 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 12066 : if (gimple_clobber_p (stmt) && fun)
64 3363 : return fun->function_end_locus;
65 : }
66 :
67 130143 : 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 425987 : get_diagnostic_tree_for_gassign_1 (const gassign *assign_stmt,
78 : hash_set<tree> *visited)
79 : {
80 425987 : enum tree_code code = gimple_assign_rhs_code (assign_stmt);
81 :
82 : /* Reverse the effect of extract_ops_from_tree during
83 : gimplification. */
84 425987 : switch (get_gimple_rhs_class (code))
85 : {
86 0 : default:
87 0 : case GIMPLE_INVALID_RHS:
88 0 : gcc_unreachable ();
89 220569 : case GIMPLE_TERNARY_RHS:
90 220569 : case GIMPLE_BINARY_RHS:
91 220569 : case GIMPLE_UNARY_RHS:
92 220569 : {
93 220569 : tree t = make_node (code);
94 220569 : TREE_TYPE (t) = TREE_TYPE (gimple_assign_lhs (assign_stmt));
95 220569 : unsigned num_rhs_args = gimple_num_ops (assign_stmt) - 1;
96 573949 : for (unsigned i = 0; i < num_rhs_args; i++)
97 : {
98 353380 : tree op = gimple_op (assign_stmt, i + 1);
99 353380 : if (op)
100 : {
101 353380 : op = fixup_tree_for_diagnostic_1 (op, visited);
102 353380 : if (op == NULL_TREE)
103 : return NULL_TREE;
104 : }
105 353380 : TREE_OPERAND (t, i) = op;
106 : }
107 : return t;
108 : }
109 205418 : case GIMPLE_SINGLE_RHS:
110 205418 : {
111 205418 : tree op = gimple_op (assign_stmt, 1);
112 205418 : op = fixup_tree_for_diagnostic_1 (op, visited);
113 205418 : 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 195490 : maybe_reconstruct_from_def_stmt (tree ssa_name,
128 : hash_set<tree> *visited)
129 : {
130 : /* Ensure termination. */
131 195490 : if (visited->contains (ssa_name))
132 : return NULL_TREE;
133 195416 : visited->add (ssa_name);
134 :
135 195416 : gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
136 :
137 195416 : 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 175615 : case GIMPLE_ASSIGN:
147 175615 : return get_diagnostic_tree_for_gassign_1
148 175615 : (as_a <const gassign *> (def_stmt), visited);
149 17836 : case GIMPLE_CALL:
150 17836 : {
151 17836 : gcall *call_stmt = as_a <gcall *> (def_stmt);
152 17836 : tree return_type = gimple_call_return_type (call_stmt);
153 17836 : tree fn = fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt),
154 : visited);
155 17836 : if (fn == NULL_TREE)
156 : return NULL_TREE;
157 17469 : unsigned num_args = gimple_call_num_args (call_stmt);
158 17469 : auto_vec<tree> args (num_args);
159 40634 : for (unsigned i = 0; i < num_args; i++)
160 : {
161 23165 : tree arg = gimple_call_arg (call_stmt, i);
162 23165 : arg = fixup_tree_for_diagnostic_1 (arg, visited);
163 23165 : if (arg == NULL_TREE)
164 0 : return NULL_TREE;
165 23165 : args.quick_push (arg);
166 : }
167 17469 : gcc_assert (fn);
168 17469 : return build_call_array_loc (gimple_location (call_stmt),
169 : return_type, fn,
170 17469 : num_args, args.address ());
171 17469 : }
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 628004 : fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited)
182 : {
183 628004 : if (expr
184 615212 : && TREE_CODE (expr) == SSA_NAME
185 941586 : && (SSA_NAME_VAR (expr) == NULL_TREE
186 118931 : || DECL_ARTIFICIAL (SSA_NAME_VAR (expr))))
187 : {
188 195627 : if (tree var = SSA_NAME_VAR (expr))
189 976 : if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
190 137 : return DECL_DEBUG_EXPR (var);
191 195490 : 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 28205 : fixup_tree_for_diagnostic (tree expr)
210 : {
211 28205 : hash_set<tree> visited;
212 28205 : return fixup_tree_for_diagnostic_1 (expr, &visited);
213 28205 : }
214 :
215 : /* Attempt to generate a tree for the LHS of ASSIGN_STMT. */
216 :
217 : tree
218 250372 : get_diagnostic_tree_for_gassign (const gassign *assign_stmt)
219 : {
220 250372 : hash_set<tree> visited;
221 250372 : return get_diagnostic_tree_for_gassign_1 (assign_stmt, &visited);
222 250372 : }
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 40732 : get_ssa_default_def (const function &fun, tree var)
285 : {
286 40732 : 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 413803 : is_special_named_call_p (const gcall &call, const char *funcname,
304 : unsigned int num_args, bool look_in_std)
305 : {
306 413803 : gcc_assert (funcname);
307 :
308 413803 : tree fndecl = gimple_call_fndecl (&call);
309 413803 : if (!fndecl)
310 : return false;
311 :
312 386051 : if (is_named_call_p (fndecl, funcname, call, num_args))
313 : return true;
314 384475 : 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 1901157 : is_named_call_p (const_tree fndecl, const char *funcname)
327 : {
328 1901157 : gcc_assert (fndecl);
329 1901157 : gcc_assert (funcname);
330 :
331 1901157 : if (!maybe_special_function_p (fndecl))
332 : return false;
333 :
334 1858847 : tree identifier = DECL_NAME (fndecl);
335 1858847 : const char *name = IDENTIFIER_POINTER (identifier);
336 1858847 : 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 1858847 : if (funcname[0] != '_' && name[0] == '_')
342 : {
343 759687 : if (name[1] == '_')
344 755323 : tname += 2;
345 : else
346 4364 : tname += 1;
347 : }
348 :
349 1858847 : 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 546895 : is_std_function_p (const_tree fndecl)
358 : {
359 546895 : tree name_decl = DECL_NAME (fndecl);
360 546895 : if (!name_decl)
361 : return false;
362 546895 : if (!DECL_CONTEXT (fndecl))
363 : return false;
364 542184 : if (TREE_CODE (DECL_CONTEXT (fndecl)) != NAMESPACE_DECL)
365 : return false;
366 1899 : tree ns = DECL_CONTEXT (fndecl);
367 1899 : if (!(DECL_CONTEXT (ns) == NULL_TREE
368 1894 : || TREE_CODE (DECL_CONTEXT (ns)) == TRANSLATION_UNIT_DECL))
369 : return false;
370 1851 : if (!DECL_NAME (ns))
371 : return false;
372 1851 : return id_equal ("std", DECL_NAME (ns));
373 : }
374 :
375 : /* Like is_named_call_p, but look for std::FUNCNAME. */
376 :
377 : bool
378 204362 : is_std_named_call_p (const_tree fndecl, const char *funcname)
379 : {
380 204362 : gcc_assert (fndecl);
381 204362 : gcc_assert (funcname);
382 :
383 204362 : if (!is_std_function_p (fndecl))
384 : return false;
385 :
386 564 : tree identifier = DECL_NAME (fndecl);
387 564 : const char *name = IDENTIFIER_POINTER (identifier);
388 564 : const char *tname = name;
389 :
390 : /* Don't disregard prefix _ or __ in FNDECL's name. */
391 :
392 564 : 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 1645313 : is_named_call_p (const_tree fndecl, const char *funcname,
401 : const gcall &call, unsigned int num_args)
402 : {
403 1645313 : gcc_assert (fndecl);
404 1645313 : gcc_assert (funcname);
405 :
406 1645313 : if (!is_named_call_p (fndecl, funcname))
407 : return false;
408 :
409 20122 : if (gimple_call_num_args (&call) != num_args)
410 1112 : return false;
411 :
412 : return true;
413 : }
414 :
415 : /* Like is_named_call_p, but check for std::FUNCNAME. */
416 :
417 : bool
418 204291 : is_std_named_call_p (const_tree fndecl, const char *funcname,
419 : const gcall &call, unsigned int num_args)
420 : {
421 204291 : gcc_assert (fndecl);
422 204291 : gcc_assert (funcname);
423 :
424 204291 : 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 43227 : is_cxa_throw_p (const gcall &call)
435 : {
436 43227 : tree fndecl = gimple_call_fndecl (&call);
437 43227 : if (!fndecl)
438 : return false;
439 :
440 40257 : return is_named_call_p (fndecl, "__cxa_throw");
441 : }
442 :
443 : bool
444 43154 : is_cxa_rethrow_p (const gcall &call)
445 : {
446 43154 : tree fndecl = gimple_call_fndecl (&call);
447 43154 : if (!fndecl)
448 : return false;
449 :
450 40184 : 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 4132 : make_label_text (bool can_colorize, const char *fmt, ...)
499 : {
500 4132 : std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
501 4132 : pp_clear_output_area (pp.get ());
502 :
503 4132 : if (!can_colorize)
504 4129 : pp_show_color (pp.get ()) = false;
505 :
506 4132 : rich_location rich_loc (line_table, UNKNOWN_LOCATION);
507 :
508 4132 : va_list ap;
509 :
510 4132 : va_start (ap, fmt);
511 :
512 4132 : text_info ti (_(fmt), &ap, 0, nullptr, &rich_loc);
513 4132 : pp_format (pp.get (), &ti);
514 4132 : pp_output_formatted_text (pp.get ());
515 :
516 4132 : va_end (ap);
517 :
518 4132 : label_text result = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
519 8264 : return result;
520 4132 : }
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 */
|