1 /* Utility functions for the analyzer.
2    Copyright (C) 2019-2020 Free Software Foundation, Inc.
3    Contributed by David Malcolm <dmalcolm@redhat.com>.
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11 
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along 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 
44 bool
is_special_named_call_p(const gcall * call,const char * funcname,unsigned int num_args)45 is_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 
62 bool
is_named_call_p(tree fndecl,const char * funcname)63 is_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 
93 static inline bool
is_std_function_p(const_tree fndecl)94 is_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 
114 bool
is_std_named_call_p(tree fndecl,const char * funcname)115 is_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 
136 bool
is_named_call_p(tree fndecl,const char * funcname,const gcall * call,unsigned int num_args)137 is_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 
154 bool
is_std_named_call_p(tree fndecl,const char * funcname,const gcall * call,unsigned int num_args)155 is_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 
172 bool
is_setjmp_call_p(const gcall * call)173 is_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 
184 bool
is_longjmp_call_p(const gcall * call)185 is_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 
198 const char *
get_user_facing_name(const gcall * call)199 get_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 
228 label_text
make_label_text(bool can_colorize,const char * fmt,...)229 make_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