]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/analyzer/analyzer.cc
analyzer: handle self-referential phis
[thirdparty/gcc.git] / gcc / analyzer / analyzer.cc
CommitLineData
757bf1df 1/* Utility functions for the analyzer.
99dee823 2 Copyright (C) 2019-2021 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"
22#include "system.h"
23#include "coretypes.h"
24#include "tree.h"
25#include "function.h"
26#include "basic-block.h"
27#include "gimple.h"
28#include "diagnostic.h"
29#include "intl.h"
30#include "function.h"
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
66/* Subroutine of fixup_tree_for_diagnostic_1, called on SSA names.
67 Attempt to reconstruct a a tree expression for SSA_NAME
68 based on its def-stmt.
69 SSA_NAME must be non-NULL.
70 VISITED must be non-NULL; it is used to ensure termination.
71
72 Return NULL_TREE if there is a problem. */
73
74static tree
75maybe_reconstruct_from_def_stmt (tree ssa_name,
76 hash_set<tree> *visited)
77{
78 /* Ensure termination. */
79 if (visited->contains (ssa_name))
80 return NULL_TREE;
81 visited->add (ssa_name);
82
83 gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
84
85 switch (gimple_code (def_stmt))
86 {
87 default:
88 gcc_unreachable ();
89 case GIMPLE_NOP:
90 case GIMPLE_PHI:
91 /* Can't handle these. */
92 return NULL_TREE;
93 case GIMPLE_ASSIGN:
94 {
95 enum tree_code code = gimple_assign_rhs_code (def_stmt);
96
97 /* Reverse the effect of extract_ops_from_tree during
98 gimplification. */
99 switch (get_gimple_rhs_class (code))
100 {
101 default:
102 case GIMPLE_INVALID_RHS:
103 gcc_unreachable ();
104 case GIMPLE_TERNARY_RHS:
105 case GIMPLE_BINARY_RHS:
106 case GIMPLE_UNARY_RHS:
107 {
108 tree t = make_node (code);
109 TREE_TYPE (t) = TREE_TYPE (ssa_name);
110 unsigned num_rhs_args = gimple_num_ops (def_stmt) - 1;
111 for (unsigned i = 0; i < num_rhs_args; i++)
112 {
113 tree op = gimple_op (def_stmt, i + 1);
114 if (op)
115 {
116 op = fixup_tree_for_diagnostic_1 (op, visited);
117 if (op == NULL_TREE)
118 return NULL_TREE;
119 }
120 TREE_OPERAND (t, i) = op;
121 }
122 return t;
123 }
124 case GIMPLE_SINGLE_RHS:
125 {
126 tree op = gimple_op (def_stmt, 1);
127 op = fixup_tree_for_diagnostic_1 (op, visited);
128 return op;
129 }
130 }
131 }
132 break;
133 case GIMPLE_CALL:
134 {
135 gcall *call_stmt = as_a <gcall *> (def_stmt);
136 tree return_type = gimple_call_return_type (call_stmt);
137 tree fn = fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt),
138 visited);
139 unsigned num_args = gimple_call_num_args (call_stmt);
140 auto_vec<tree> args (num_args);
141 for (unsigned i = 0; i < num_args; i++)
142 {
143 tree arg = gimple_call_arg (call_stmt, i);
144 arg = fixup_tree_for_diagnostic_1 (arg, visited);
145 if (arg == NULL_TREE)
146 return NULL_TREE;
147 args.quick_push (arg);
148 }
149 return build_call_array_loc (gimple_location (call_stmt),
150 return_type, fn,
7d8f4240 151 num_args, args.address ());
e4bb1bd6
DM
152 }
153 break;
154 }
155}
156
157/* Subroutine of fixup_tree_for_diagnostic: attempt to fixup EXPR,
158 which can be NULL.
159 VISITED must be non-NULL; it is used to ensure termination. */
160
161static tree
162fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited)
163{
164 if (expr
165 && TREE_CODE (expr) == SSA_NAME
166 && (SSA_NAME_VAR (expr) == NULL_TREE
167 || DECL_ARTIFICIAL (SSA_NAME_VAR (expr))))
168 if (tree expr2 = maybe_reconstruct_from_def_stmt (expr, visited))
169 return expr2;
170 return expr;
171}
172
173/* We don't want to print '<unknown>' in our diagnostics (PR analyzer/99771),
174 but sometimes we generate diagnostics involving an ssa name for a
175 temporary.
176
177 Work around this by attempting to reconstruct a tree expression for
178 such temporaries based on their def-stmts.
179
180 Otherwise return EXPR.
181
182 EXPR can be NULL. */
183
184tree
185fixup_tree_for_diagnostic (tree expr)
186{
187 hash_set<tree> visited;
188 return fixup_tree_for_diagnostic_1 (expr, &visited);
189}
190
808f4dfe
DM
191} // namespace ana
192
757bf1df
DM
193/* Helper function for checkers. Is the CALL to the given function name,
194 and with the given number of arguments?
195
196 This doesn't resolve function pointers via the region model;
197 is_named_call_p should be used instead, using a fndecl from
198 get_fndecl_for_call; this function should only be used for special cases
199 where it's not practical to get at the region model, or for special
200 analyzer functions such as __analyzer_dump. */
201
202bool
203is_special_named_call_p (const gcall *call, const char *funcname,
204 unsigned int num_args)
205{
206 gcc_assert (funcname);
207
208 tree fndecl = gimple_call_fndecl (call);
209 if (!fndecl)
210 return false;
211
212 return is_named_call_p (fndecl, funcname, call, num_args);
213}
214
342e14ff
DM
215/* Helper function for checkers. Is FNDECL an extern fndecl at file scope
216 that has the given FUNCNAME?
217
218 Compare with special_function_p in calls.c. */
757bf1df
DM
219
220bool
221is_named_call_p (tree fndecl, const char *funcname)
222{
223 gcc_assert (fndecl);
224 gcc_assert (funcname);
225
182ce042 226 if (!maybe_special_function_p (fndecl))
342e14ff
DM
227 return false;
228
229 tree identifier = DECL_NAME (fndecl);
342e14ff
DM
230 const char *name = IDENTIFIER_POINTER (identifier);
231 const char *tname = name;
232
233 /* Potentially disregard prefix _ or __ in FNDECL's name, but not if
234 FUNCNAME itself has leading underscores (e.g. when looking for
235 "__analyzer_eval"). */
236 if (funcname[0] != '_' && name[0] == '_')
237 {
238 if (name[1] == '_')
239 tname += 2;
240 else
241 tname += 1;
242 }
243
244 return 0 == strcmp (tname, funcname);
757bf1df
DM
245}
246
9f00b22f
DM
247/* Return true if FNDECL is within the namespace "std".
248 Compare with cp/typeck.c: decl_in_std_namespace_p, but this doesn't
249 rely on being the C++ FE (or handle inline namespaces inside of std). */
250
251static inline bool
252is_std_function_p (const_tree fndecl)
253{
254 tree name_decl = DECL_NAME (fndecl);
255 if (!name_decl)
256 return false;
257 if (!DECL_CONTEXT (fndecl))
258 return false;
259 if (TREE_CODE (DECL_CONTEXT (fndecl)) != NAMESPACE_DECL)
260 return false;
261 tree ns = DECL_CONTEXT (fndecl);
262 if (!(DECL_CONTEXT (ns) == NULL_TREE
263 || TREE_CODE (DECL_CONTEXT (ns)) == TRANSLATION_UNIT_DECL))
264 return false;
265 if (!DECL_NAME (ns))
266 return false;
267 return id_equal ("std", DECL_NAME (ns));
268}
269
270/* Like is_named_call_p, but look for std::FUNCNAME. */
271
272bool
273is_std_named_call_p (tree fndecl, const char *funcname)
274{
275 gcc_assert (fndecl);
276 gcc_assert (funcname);
277
278 if (!is_std_function_p (fndecl))
279 return false;
280
281 tree identifier = DECL_NAME (fndecl);
282 const char *name = IDENTIFIER_POINTER (identifier);
283 const char *tname = name;
284
285 /* Don't disregard prefix _ or __ in FNDECL's name. */
286
287 return 0 == strcmp (tname, funcname);
288}
289
342e14ff
DM
290/* Helper function for checkers. Is FNDECL an extern fndecl at file scope
291 that has the given FUNCNAME, and does CALL have the given number of
292 arguments? */
757bf1df
DM
293
294bool
295is_named_call_p (tree fndecl, const char *funcname,
296 const gcall *call, unsigned int num_args)
297{
298 gcc_assert (fndecl);
299 gcc_assert (funcname);
300
301 if (!is_named_call_p (fndecl, funcname))
302 return false;
303
304 if (gimple_call_num_args (call) != num_args)
9f00b22f
DM
305 return false;
306
307 return true;
308}
309
310/* Like is_named_call_p, but check for std::FUNCNAME. */
311
312bool
313is_std_named_call_p (tree fndecl, const char *funcname,
314 const gcall *call, unsigned int num_args)
315{
316 gcc_assert (fndecl);
317 gcc_assert (funcname);
318
319 if (!is_std_named_call_p (fndecl, funcname))
320 return false;
321
322 if (gimple_call_num_args (call) != num_args)
757bf1df
DM
323 return false;
324
325 return true;
326}
327
342e14ff 328/* Return true if stmt is a setjmp or sigsetjmp call. */
757bf1df
DM
329
330bool
342e14ff 331is_setjmp_call_p (const gcall *call)
757bf1df 332{
342e14ff
DM
333 if (is_special_named_call_p (call, "setjmp", 1)
334 || is_special_named_call_p (call, "sigsetjmp", 2))
35e3f082
DM
335 /* region_model::on_setjmp requires a pointer. */
336 if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, 0))))
337 return true;
757bf1df
DM
338
339 return false;
340}
341
342e14ff 342/* Return true if stmt is a longjmp or siglongjmp call. */
757bf1df
DM
343
344bool
345is_longjmp_call_p (const gcall *call)
346{
342e14ff
DM
347 if (is_special_named_call_p (call, "longjmp", 2)
348 || is_special_named_call_p (call, "siglongjmp", 2))
01eabbea
DM
349 /* exploded_node::on_longjmp requires a pointer for the initial
350 argument. */
351 if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, 0))))
352 return true;
757bf1df
DM
353
354 return false;
355}
356
342e14ff
DM
357/* For a CALL that matched is_special_named_call_p or is_named_call_p for
358 some name, return a name for the called function suitable for use in
359 diagnostics (stripping the leading underscores). */
360
361const char *
362get_user_facing_name (const gcall *call)
363{
364 tree fndecl = gimple_call_fndecl (call);
365 gcc_assert (fndecl);
366
367 tree identifier = DECL_NAME (fndecl);
368 gcc_assert (identifier);
369
370 const char *name = IDENTIFIER_POINTER (identifier);
371
372 /* Strip prefix _ or __ in FNDECL's name. */
373 if (name[0] == '_')
374 {
375 if (name[1] == '_')
376 return name + 2;
377 else
378 return name + 1;
379 }
380
381 return name;
382}
383
757bf1df
DM
384/* Generate a label_text instance by formatting FMT, using a
385 temporary clone of the global_dc's printer (thus using its
386 formatting callbacks).
387
388 Colorize if the global_dc supports colorization and CAN_COLORIZE is
389 true. */
390
391label_text
392make_label_text (bool can_colorize, const char *fmt, ...)
393{
394 pretty_printer *pp = global_dc->printer->clone ();
395 pp_clear_output_area (pp);
396
397 if (!can_colorize)
398 pp_show_color (pp) = false;
399
400 text_info ti;
401 rich_location rich_loc (line_table, UNKNOWN_LOCATION);
402
403 va_list ap;
404
405 va_start (ap, fmt);
406
407 ti.format_spec = _(fmt);
408 ti.args_ptr = &ap;
409 ti.err_no = 0;
410 ti.x_data = NULL;
411 ti.m_richloc = &rich_loc;
412
413 pp_format (pp, &ti);
414 pp_output_formatted_text (pp);
415
416 va_end (ap);
417
418 label_text result = label_text::take (xstrdup (pp_formatted_text (pp)));
419 delete pp;
420 return result;
421}
422
423#endif /* #if ENABLE_ANALYZER */