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

source code of gcc/analyzer/analyzer.cc