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