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