xref: /llvm-project/clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp (revision 979c4e9a411a8c5116c859788a738cc1cd9f23f1)
1 // RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage \
2 // RUN:            -fsafe-buffer-usage-suggestions \
3 // RUN:            -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
4 typedef int * Int_ptr_t;
5 typedef int Int_t;
6 
local_array_subscript_simple()7 void local_array_subscript_simple() {
8   int tmp;
9   int *p = new int[10];
10   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
11   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
12   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
13   const int *q = new int[10];
14   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span<int const> "
15   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:18-[[@LINE-2]]:18}:"{"
16   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:29-[[@LINE-3]]:29}:", 10}"
17   tmp = p[5];
18   tmp = q[5];
19 
20   // We do not fix the following declaration. Because if the
21   // definition of `Int_ptr_t` gets changed, the fixed code becomes
22   // incorrect and may NOT be noticed.
23   // FIXME: Fix with std::span<std::remove_pointer_t<Int_ptr_t>>?
24   Int_ptr_t x = new int[10];
25   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
26   Int_t * z = new int[10];
27   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<Int_t>"
28   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
29   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:", 10}"
30   Int_t * w = new Int_t[10];
31   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<Int_t>"
32   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"{"
33   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:28-[[@LINE-3]]:28}:", 10}"
34 
35   tmp = x[5];
36   tmp = z[5];
37   tmp = w[5];
38 }
39 
local_array_subscript_auto()40 void local_array_subscript_auto() {
41   int tmp;
42   // We do not fix the following declaration because
43   // that'd cause us to hardcode the element type.
44   // FIXME: Can we use the C++17 class template argument deduction
45   // to avoid spelling out the element type?
46   auto p = new int[10];
47   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
48   tmp = p[5];
49 }
50 
local_variable_qualifiers_specifiers()51 void local_variable_qualifiers_specifiers() {
52   int a[10];
53   const int * p = a;
54   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span<int const>"
55   const int * const q = a;
56   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span<int const>"
57   int tmp;
58   tmp = p[5];
59   tmp = q[5];
60 
61   [[deprecated]] const int * x = a;
62   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:18-[[@LINE-1]]:29}:"std::span<int const>"
63   const int * y [[deprecated]];
64   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:14}:"std::span<int const>"
65   tmp = x[5];
66   tmp = y[5];
67 }
68 
local_variable_unsupported_specifiers()69 void local_variable_unsupported_specifiers() {
70   int a[10];
71   const int * p [[deprecated]] = a; //  not supported because the attribute overlaps the source range of the declaration
72   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
73 
74   static const int * q = a; //  storage specifier not supported yet
75   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
76 
77   extern int * x; //  storage specifier not supported yet
78   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
79 
80   constexpr int * y = 0; //  `constexpr` specifier not supported yet
81   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
82 
83   int tmp;
84 
85   tmp = p[5];
86   tmp = q[5];
87   tmp = x[5];
88   tmp = y[5];
89 }
90 
local_array_subscript_variable_extent()91 void local_array_subscript_variable_extent() {
92   int n = 10;
93   int tmp;
94   int *p = new int[n];
95   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
96   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
97   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:22-[[@LINE-3]]:22}:", n}"
98   // If the extent expression does not have a constant value, we cannot fill the extent for users...
99   int *q = new int[n++];
100   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
101   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
102   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", <# placeholder #>}"
103   tmp = p[5];
104   tmp = q[5];
105 }
106 
107 
local_ptr_to_array()108 void local_ptr_to_array() {
109   int tmp;
110   int n = 10;
111   int a[10];
112   int b[n];  // If the extent expression does not have a constant value, we cannot fill the extent for users...
113   int *p = a;
114   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
115   int *q = b;
116   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int> "
117   // No way to know if `n` is ever mutated since `int b[n];`, so no way to figure out the extent
118   tmp = p[5];
119   tmp = q[5];
120 }
121 
local_ptr_addrof_init()122 void local_ptr_addrof_init() {
123   int var;
124   int * q = &var;
125   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
126   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
127   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:17-[[@LINE-3]]:17}:", 1}"
128   // This expression involves unsafe buffer accesses, which will crash
129   // at runtime after applying the fix-it,
130   var = q[5];
131 }
132 
decl_without_init()133 void decl_without_init() {
134   int tmp;
135   int * p;
136   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
137   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:{{^3}}
138   Int_t * q;
139   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<Int_t>"
140   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:{{^3}}
141   tmp = p[5];
142   tmp = q[5];
143 }
144 
145 // Explicit casts are required in the following cases. No way to
146 // figure out span extent for them automatically.
explict_cast()147 void explict_cast() {
148   int tmp;
149   int * p = (int*) new int[10][10];
150   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
151   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
152   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:35-[[@LINE-3]]:35}:", <# placeholder #>}"
153   tmp = p[5];
154 
155   int a;
156   char * q = (char *)&a;
157   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:9}:"std::span<char>"
158   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:14-[[@LINE-2]]:14}:"{"
159   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", <# placeholder #>}"
160   tmp = (int) q[5];
161 
162   void * r = &a;
163   char * s = (char *) r;
164   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:9}:"std::span<char>"
165   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:14-[[@LINE-2]]:14}:"{"
166   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", <# placeholder #>}"
167   tmp = (int) s[5];
168 }
169 
null_init()170 void null_init() {
171 #define NULL 0
172   int tmp;
173   int * my_null = 0;
174   int * p = 0;
175   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
176   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:{{^3}}
177   int * g = NULL; // cannot handle fix-its involving macros for now
178   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
179   int * f = nullptr;
180   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
181   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:{{^3}}
182 
183   // In case of value dependencies, we give up
184   int * q = my_null;
185   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
186   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
187   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:20-[[@LINE-3]]:20}:", <# placeholder #>}"
188   int * r = my_null + 0;
189   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
190   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
191   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", <# placeholder #>}"
192 
193   tmp = p[5]; // `p[5]` will cause crash after `p` being transformed to be a `std::span`
194   tmp = q[5]; // Similar for the rests.
195   tmp = r[5];
196   tmp = g[5];
197   tmp = f[5];
198 #undef NULL
199 }
200 
201 
unsupported_multi_decl(int * x)202 void unsupported_multi_decl(int * x) {
203   int * p = x, * q = new int[10];
204   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
205   *p = q[5];
206 }
207 
macroVariableIdentifier()208 void macroVariableIdentifier() {
209 #define MY_NAME p
210 #define MY_NAME_ARG(x) q
211 
212   // Although fix-its include macros, the macros do not overlap with
213   // the bounds of the source range of these fix-its. So these fix-its
214   // are valid.
215 
216   int * MY_NAME = new int[10];
217   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
218   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:19-[[@LINE-2]]:19}:"{"
219   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:30-[[@LINE-3]]:30}:", 10}"
220   int * MY_NAME_ARG( 'x' ) = new int[10];
221   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
222   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:30-[[@LINE-2]]:30}:"{"
223   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:41-[[@LINE-3]]:41}:", 10}"
224   p[5] = 5;
225   q[5] = 5;
226 #undef MY_NAME
227 #undef MY_NAME_ARG
228 }
229 
unsupported_fixit_overlapping_macro(int * x)230 void unsupported_fixit_overlapping_macro(int * x) {
231   int tmp;
232   // In the case below, a tentative fix-it replaces `MY_INT * p =` with `std::span<MY_INT> p `.
233   // The bounds of the source range of the fix-it overlap with the use of the macro
234   // `MY_INT`.  The fix-it is discarded then.
235 
236   // FIXME: we do not have to discard a fix-it if its begin location
237   // overlaps with the begin location of a macro. Similar for end
238   // locations.
239 
240 #define MY_INT int
241   MY_INT * p = new int[10];
242   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
243   tmp = p[5];
244 
245 #define MY_VAR(name) int * name
246   MY_VAR(q) = new int[10];
247   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
248   tmp = q[5];
249 
250   // In cases where fix-its do not change the original code where
251   // macros are used, those fix-its will be emitted.  For example,
252   // fixits are inserted before and after `new MY_INT[MY_TEN]` below.
253 #define MY_TEN 10
254   int * g = new MY_INT[MY_TEN];
255   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
256   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
257   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:31-[[@LINE-3]]:31}:", MY_TEN}"
258   tmp = g[5];
259 
260 #undef MY_INT
261 #undef MY_VAR
262 #undef MY_TEN
263 }
264 
unsupported_subscript_negative(int i,unsigned j,unsigned long k)265 void unsupported_subscript_negative(int i, unsigned j, unsigned long k) {
266   int tmp;
267   int * p = new int[10];
268   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
269 
270   tmp = p[-1]; // If `p` is made a span, this `[]` operation is wrong,
271          // so no fix-it emitted.
272 
273   int * q = new int[10];
274   // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
275 
276   tmp = q[5];
277   tmp = q[i];  // If `q` is made a span, this `[]` operation may be
278          // wrong as we do not know if `i` is non-negative, so
279          // no fix-it emitted.
280 
281   int * r = new int[10];
282   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
283   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
284   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
285 
286   tmp = r[j] + r[k]; // both `j` and `k` are unsigned so they must be non-negative
287   tmp = r[(unsigned int)-1]; // a cast-to-unsigned-expression is also non-negative
288 }
289 
290 #define DEFINE_PTR(X) int* ptr = (X);
291 
all_vars_in_macro()292 void all_vars_in_macro() {
293   int* local;
294   DEFINE_PTR(local)
295   ptr[1] = 0;
296 }
297 
few_vars_in_macro()298 void few_vars_in_macro() {
299   int* local;
300   DEFINE_PTR(local)
301   ptr[1] = 0;
302   int tmp;
303   ptr[2] = 30;
304   int * p = new int[10];
305   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:8}:"std::span<int>"
306   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:13-[[@LINE-2]]:13}:"{"
307   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:24-[[@LINE-3]]:24}:", 10}"
308   tmp = p[5];
309   int val = *p;
310   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:14}:""
311   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:15-[[@LINE-2]]:15}:"[0]"
312   val = *p + 30;
313   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:10}:""
314   // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:11-[[@LINE-2]]:11}:"[0]"
315 }
316