1/* Utility functions for the analyzer.
2   Copyright (C) 2019-2020 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#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
35/* Helper function for checkers.  Is the CALL to the given function name,
36   and with the given number of arguments?
37
38   This doesn't resolve function pointers via the region model;
39   is_named_call_p should be used instead, using a fndecl from
40   get_fndecl_for_call; this function should only be used for special cases
41   where it's not practical to get at the region model, or for special
42   analyzer functions such as __analyzer_dump.  */
43
44bool
45is_special_named_call_p (const gcall *call, const char *funcname,
46			 unsigned int num_args)
47{
48  gcc_assert (funcname);
49
50  tree fndecl = gimple_call_fndecl (call);
51  if (!fndecl)
52    return false;
53
54  return is_named_call_p (fndecl, funcname, call, num_args);
55}
56
57/* Helper function for checkers.  Is FNDECL an extern fndecl at file scope
58   that has the given FUNCNAME?
59
60   Compare with special_function_p in calls.c.  */
61
62bool
63is_named_call_p (tree fndecl, const char *funcname)
64{
65  gcc_assert (fndecl);
66  gcc_assert (funcname);
67
68  if (!maybe_special_function_p (fndecl))
69    return false;
70
71  tree identifier = DECL_NAME (fndecl);
72  const char *name = IDENTIFIER_POINTER (identifier);
73  const char *tname = name;
74
75  /* Potentially disregard prefix _ or __ in FNDECL's name, but not if
76     FUNCNAME itself has leading underscores (e.g. when looking for
77     "__analyzer_eval").  */
78  if (funcname[0] != '_' && name[0] == '_')
79    {
80      if (name[1] == '_')
81	tname += 2;
82      else
83	tname += 1;
84    }
85
86  return 0 == strcmp (tname, funcname);
87}
88
89/* Return true if FNDECL is within the namespace "std".
90   Compare with cp/typeck.c: decl_in_std_namespace_p, but this doesn't
91   rely on being the C++ FE (or handle inline namespaces inside of std).  */
92
93static inline bool
94is_std_function_p (const_tree fndecl)
95{
96  tree name_decl = DECL_NAME (fndecl);
97  if (!name_decl)
98    return false;
99  if (!DECL_CONTEXT (fndecl))
100    return false;
101  if (TREE_CODE (DECL_CONTEXT (fndecl)) != NAMESPACE_DECL)
102    return false;
103  tree ns = DECL_CONTEXT (fndecl);
104  if (!(DECL_CONTEXT (ns) == NULL_TREE
105	|| TREE_CODE (DECL_CONTEXT (ns)) == TRANSLATION_UNIT_DECL))
106    return false;
107  if (!DECL_NAME (ns))
108    return false;
109  return id_equal ("std", DECL_NAME (ns));
110}
111
112/* Like is_named_call_p, but look for std::FUNCNAME.  */
113
114bool
115is_std_named_call_p (tree fndecl, const char *funcname)
116{
117  gcc_assert (fndecl);
118  gcc_assert (funcname);
119
120  if (!is_std_function_p (fndecl))
121    return false;
122
123  tree identifier = DECL_NAME (fndecl);
124  const char *name = IDENTIFIER_POINTER (identifier);
125  const char *tname = name;
126
127  /* Don't disregard prefix _ or __ in FNDECL's name.  */
128
129  return 0 == strcmp (tname, funcname);
130}
131
132/* Helper function for checkers.  Is FNDECL an extern fndecl at file scope
133   that has the given FUNCNAME, and does CALL have the given number of
134   arguments?  */
135
136bool
137is_named_call_p (tree fndecl, const char *funcname,
138		 const gcall *call, unsigned int num_args)
139{
140  gcc_assert (fndecl);
141  gcc_assert (funcname);
142
143  if (!is_named_call_p (fndecl, funcname))
144    return false;
145
146  if (gimple_call_num_args (call) != num_args)
147    return false;
148
149  return true;
150}
151
152/* Like is_named_call_p, but check for std::FUNCNAME.  */
153
154bool
155is_std_named_call_p (tree fndecl, const char *funcname,
156		     const gcall *call, unsigned int num_args)
157{
158  gcc_assert (fndecl);
159  gcc_assert (funcname);
160
161  if (!is_std_named_call_p (fndecl, funcname))
162    return false;
163
164  if (gimple_call_num_args (call) != num_args)
165    return false;
166
167  return true;
168}
169
170/* Return true if stmt is a setjmp or sigsetjmp call.  */
171
172bool
173is_setjmp_call_p (const gcall *call)
174{
175  if (is_special_named_call_p (call, "setjmp", 1)
176      || is_special_named_call_p (call, "sigsetjmp", 2))
177    return true;
178
179  return false;
180}
181
182/* Return true if stmt is a longjmp or siglongjmp call.  */
183
184bool
185is_longjmp_call_p (const gcall *call)
186{
187  if (is_special_named_call_p (call, "longjmp", 2)
188      || is_special_named_call_p (call, "siglongjmp", 2))
189    return true;
190
191  return false;
192}
193
194/* For a CALL that matched is_special_named_call_p or is_named_call_p for
195   some name, return a name for the called function suitable for use in
196   diagnostics (stripping the leading underscores).  */
197
198const char *
199get_user_facing_name (const gcall *call)
200{
201  tree fndecl = gimple_call_fndecl (call);
202  gcc_assert (fndecl);
203
204  tree identifier = DECL_NAME (fndecl);
205  gcc_assert (identifier);
206
207  const char *name = IDENTIFIER_POINTER (identifier);
208
209  /* Strip prefix _ or __ in FNDECL's name.  */
210  if (name[0] == '_')
211    {
212      if (name[1] == '_')
213	return name + 2;
214      else
215	return name + 1;
216    }
217
218  return name;
219}
220
221/* Generate a label_text instance by formatting FMT, using a
222   temporary clone of the global_dc's printer (thus using its
223   formatting callbacks).
224
225   Colorize if the global_dc supports colorization and CAN_COLORIZE is
226   true.  */
227
228label_text
229make_label_text (bool can_colorize, const char *fmt, ...)
230{
231  pretty_printer *pp = global_dc->printer->clone ();
232  pp_clear_output_area (pp);
233
234  if (!can_colorize)
235    pp_show_color (pp) = false;
236
237  text_info ti;
238  rich_location rich_loc (line_table, UNKNOWN_LOCATION);
239
240  va_list ap;
241
242  va_start (ap, fmt);
243
244  ti.format_spec = _(fmt);
245  ti.args_ptr = &ap;
246  ti.err_no = 0;
247  ti.x_data = NULL;
248  ti.m_richloc = &rich_loc;
249
250  pp_format (pp, &ti);
251  pp_output_formatted_text (pp);
252
253  va_end (ap);
254
255  label_text result = label_text::take (xstrdup (pp_formatted_text (pp)));
256  delete pp;
257  return result;
258}
259
260#endif /* #if ENABLE_ANALYZER */
261