1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s |
2 | // |
3 | // RUN: %clang_analyze_cc1 -analyzer-checker=core %s \ |
4 | // RUN: -analyzer-output=plist -o %t.plist \ |
5 | // RUN: -analyzer-config expand-macros=true |
6 | // |
7 | // Check the actual plist output. |
8 | // RUN: cat %t.plist | %diff_plist \ |
9 | // RUN: %S/Inputs/expected-plists/plist-macros-with-expansion.cpp.plist - |
10 | // |
11 | // Check the macro expansions from the plist output here, to make the test more |
12 | // understandable. |
13 | // RUN: FileCheck --input-file=%t.plist %s |
14 | |
15 | void print(const void*); |
16 | |
17 | //===----------------------------------------------------------------------===// |
18 | // Tests for non-function-like macro expansions. |
19 | //===----------------------------------------------------------------------===// |
20 | |
21 | #define SET_PTR_VAR_TO_NULL \ |
22 | ptr = 0 |
23 | |
24 | void nonFunctionLikeMacroTest() { |
25 | int *ptr; |
26 | SET_PTR_VAR_TO_NULL; |
27 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
28 | } |
29 | |
30 | // CHECK: <key>name</key><string>SET_PTR_VAR_TO_NULL</string> |
31 | // CHECK-NEXT: <key>expansion</key><string>ptr = 0</string> |
32 | |
33 | #define NULL 0 |
34 | #define SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO \ |
35 | ptr = NULL |
36 | |
37 | void nonFunctionLikeNestedMacroTest() { |
38 | int *ptr; |
39 | SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO; |
40 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
41 | } |
42 | |
43 | // CHECK: <key>name</key><string>SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO</string> |
44 | // CHECK-NEXT: <key>expansion</key><string>ptr =0</string> |
45 | |
46 | //===----------------------------------------------------------------------===// |
47 | // Tests for function-like macro expansions. |
48 | //===----------------------------------------------------------------------===// |
49 | |
50 | void setToNull(int **vptr) { |
51 | *vptr = nullptr; |
52 | } |
53 | |
54 | #define TO_NULL(x) \ |
55 | setToNull(x) |
56 | |
57 | void functionLikeMacroTest() { |
58 | int *ptr; |
59 | TO_NULL(&ptr); |
60 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
61 | } |
62 | |
63 | // CHECK: <key>name</key><string>TO_NULL</string> |
64 | // CHECK-NEXT: <key>expansion</key><string>setToNull(&ptr)</string> |
65 | |
66 | #define DOES_NOTHING(x) \ |
67 | { \ |
68 | int b; \ |
69 | b = 5; \ |
70 | } \ |
71 | print(x) |
72 | |
73 | #define DEREF(x) \ |
74 | DOES_NOTHING(x); \ |
75 | *x |
76 | |
77 | void functionLikeNestedMacroTest() { |
78 | int *a; |
79 | TO_NULL(&a); |
80 | DEREF(a) = 5; // expected-warning{{Dereference of null pointer}} |
81 | } |
82 | |
83 | // CHECK: <key>name</key><string>TO_NULL</string> |
84 | // CHECK-NEXT: <key>expansion</key><string>setToNull(&a)</string> |
85 | |
86 | // CHECK: <key>name</key><string>DEREF</string> |
87 | // CHECK-NEXT: <key>expansion</key><string>{ int b; b = 5; } print(a); *a</string> |
88 | |
89 | //===----------------------------------------------------------------------===// |
90 | // Tests for undefining and/or redifining macros. |
91 | //===----------------------------------------------------------------------===// |
92 | |
93 | #define WILL_UNDEF_SET_NULL_TO_PTR(ptr) \ |
94 | ptr = nullptr; |
95 | |
96 | void undefinedMacroByTheEndOfParsingTest() { |
97 | int *ptr; |
98 | WILL_UNDEF_SET_NULL_TO_PTR(ptr); |
99 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
100 | } |
101 | |
102 | #undef WILL_UNDEF_SET_NULL_TO_PTR |
103 | |
104 | // CHECK: <key>name</key><string>WILL_UNDEF_SET_NULL_TO_PTR</string> |
105 | // CHECK-NEXT: <key>expansion</key><string>ptr = nullptr;</string> |
106 | |
107 | #define WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr) \ |
108 | /* Nothing */ |
109 | #undef WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL |
110 | #define WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr) \ |
111 | ptr = nullptr; |
112 | |
113 | void macroRedefinedMultipleTimesTest() { |
114 | int *ptr; |
115 | WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr) |
116 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
117 | } |
118 | |
119 | #undef WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL |
120 | #define WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr) \ |
121 | print("This string shouldn't be in the plist file at all. Or anywhere, " \ |
122 | "but here."); |
123 | |
124 | // CHECK: <key>name</key><string>WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL</string> |
125 | // CHECK-NEXT: <key>expansion</key><string>ptr = nullptr;</string> |
126 | |
127 | #define WILL_UNDEF_SET_NULL_TO_PTR_2(ptr) \ |
128 | ptr = nullptr; |
129 | |
130 | #define PASS_PTR_TO_MACRO_THAT_WILL_BE_UNDEFD(ptr) \ |
131 | WILL_UNDEF_SET_NULL_TO_PTR_2(ptr) |
132 | |
133 | void undefinedMacroInsideAnotherMacroTest() { |
134 | int *ptr; |
135 | PASS_PTR_TO_MACRO_THAT_WILL_BE_UNDEFD(ptr); |
136 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
137 | } |
138 | |
139 | // TODO: Expand arguments. |
140 | // CHECK: <key>name</key><string>PASS_PTR_TO_MACRO_THAT_WILL_BE_UNDEFD</string> |
141 | // CHECK-NEXT: <key>expansion</key><string>ptr = nullptr;</string> |
142 | |
143 | #undef WILL_UNDEF_SET_NULL_TO_PTR_2 |
144 | |
145 | //===----------------------------------------------------------------------===// |
146 | // Tests for macro arguments containing commas and parantheses. |
147 | // |
148 | // As of writing these tests, the algorithm expands macro arguments by lexing |
149 | // the macro's expansion location, and relies on finding tok::comma and |
150 | // tok::l_paren/tok::r_paren. |
151 | //===----------------------------------------------------------------------===// |
152 | |
153 | // Note that this commas, parantheses in strings aren't parsed as tok::comma or |
154 | // tok::l_paren/tok::r_paren, but why not test them. |
155 | |
156 | #define TO_NULL_AND_PRINT(x, str) \ |
157 | x = 0; \ |
158 | print(str) |
159 | |
160 | void macroArgContainsCommaInStringTest() { |
161 | int *a; |
162 | TO_NULL_AND_PRINT(a, "Will this , cause a crash?"); |
163 | *a = 5; // expected-warning{{Dereference of null pointer}} |
164 | } |
165 | |
166 | // CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string> |
167 | // CHECK-NEXT: <key>expansion</key><string>a = 0; print( "Will this , cause a crash?")</string> |
168 | |
169 | void macroArgContainsLParenInStringTest() { |
170 | int *a; |
171 | TO_NULL_AND_PRINT(a, "Will this ( cause a crash?"); |
172 | *a = 5; // expected-warning{{Dereference of null pointer}} |
173 | } |
174 | |
175 | // CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string> |
176 | // CHECK-NEXT: <key>expansion</key><string>a = 0; print( "Will this ( cause a crash?")</string> |
177 | |
178 | void macroArgContainsRParenInStringTest() { |
179 | int *a; |
180 | TO_NULL_AND_PRINT(a, "Will this ) cause a crash?"); |
181 | *a = 5; // expected-warning{{Dereference of null pointer}} |
182 | } |
183 | |
184 | // CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string> |
185 | // CHECK-NEXT: <key>expansion</key><string>a = 0; print( "Will this ) cause a crash?")</string> |
186 | |
187 | #define CALL_FUNCTION(funcCall) \ |
188 | funcCall |
189 | |
190 | // Function calls do contain both tok::comma and tok::l_paren/tok::r_paren. |
191 | |
192 | void macroArgContainsLParenRParenTest() { |
193 | int *a; |
194 | CALL_FUNCTION(setToNull(&a)); |
195 | *a = 5; // expected-warning{{Dereference of null pointer}} |
196 | } |
197 | |
198 | // CHECK: <key>name</key><string>CALL_FUNCTION</string> |
199 | // CHECK-NEXT: <key>expansion</key><string>setToNull(&a)</string> |
200 | |
201 | void setToNullAndPrint(int **vptr, const char *str) { |
202 | setToNull(vptr); |
203 | print(str); |
204 | } |
205 | |
206 | void macroArgContainsCommaLParenRParenTest() { |
207 | int *a; |
208 | CALL_FUNCTION(setToNullAndPrint(&a, "Hello!")); |
209 | *a = 5; // expected-warning{{Dereference of null pointer}} |
210 | } |
211 | |
212 | // CHECK: <key>name</key><string>CALL_FUNCTION</string> |
213 | // CHECK-NEXT: <key>expansion</key><string>setToNullAndPrint(&a, "Hello!")</string> |
214 | |
215 | #define CALL_FUNCTION_WITH_TWO_PARAMS(funcCall, param1, param2) \ |
216 | funcCall(param1, param2) |
217 | |
218 | void macroArgContainsCommaLParenRParenTest2() { |
219 | int *a; |
220 | CALL_FUNCTION_WITH_TWO_PARAMS(setToNullAndPrint, &a, "Hello!"); |
221 | *a = 5; // expected-warning{{Dereference of null pointer}} |
222 | } |
223 | |
224 | // CHECK: <key>name</key><string>CALL_FUNCTION_WITH_TWO_PARAMS</string> |
225 | // CHECK-NEXT: <key>expansion</key><string>setToNullAndPrint( &a, "Hello!")</string> |
226 | |
227 | #define CALL_LAMBDA(l) \ |
228 | l() |
229 | |
230 | void commaInBracketsTest() { |
231 | int *ptr; |
232 | const char str[] = "Hello!"; |
233 | // You need to add parantheses around a lambda expression to compile this, |
234 | // else the comma in the capture will be parsed as divider of macro args. |
235 | CALL_LAMBDA(([&ptr, str] () mutable { TO_NULL(&ptr); })); |
236 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
237 | } |
238 | |
239 | // CHECK: <key>name</key><string>CALL_LAMBDA</string> |
240 | // CHECK-NEXT: <key>expansion</key><string>([&ptr, str] () mutable { setToNull(&ptr); })()</string> |
241 | |
242 | #define PASTE_CODE(code) \ |
243 | code |
244 | |
245 | void commaInBracesTest() { |
246 | PASTE_CODE({ // expected-warning{{Dereference of null pointer}} |
247 | // NOTE: If we were to add a new variable here after a comma, we'd get a |
248 | // compilation error, so this test is mainly here to show that this was also |
249 | // investigated. |
250 | |
251 | // int *ptr = nullptr, a; |
252 | int *ptr = nullptr; |
253 | *ptr = 5; |
254 | }) |
255 | } |
256 | |
257 | // CHECK: <key>name</key><string>PASTE_CODE</string> |
258 | // CHECK-NEXT: <key>expansion</key><string>{ int *ptr = nullptr; *ptr = 5; }</string> |
259 | |
260 | // Example taken from |
261 | // https://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments.html#Macro-Arguments. |
262 | |
263 | #define POTENTIALLY_EMPTY_PARAM(x, y) \ |
264 | x; \ |
265 | y = nullptr |
266 | |
267 | void emptyParamTest() { |
268 | int *ptr; |
269 | |
270 | POTENTIALLY_EMPTY_PARAM(,ptr); |
271 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
272 | } |
273 | |
274 | // CHECK: <key>name</key><string>POTENTIALLY_EMPTY_PARAM</string> |
275 | // CHECK-NEXT: <key>expansion</key><string>;ptr = nullptr</string> |
276 | |
277 | #define NESTED_EMPTY_PARAM(a, b) \ |
278 | POTENTIALLY_EMPTY_PARAM(a, b); |
279 | |
280 | |
281 | void nestedEmptyParamTest() { |
282 | int *ptr; |
283 | |
284 | NESTED_EMPTY_PARAM(, ptr); |
285 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
286 | } |
287 | |
288 | // CHECK: <key>name</key><string>NESTED_EMPTY_PARAM</string> |
289 | // CHECK-NEXT: <key>expansion</key><string>; ptr = nullptr;</string> |
290 | |
291 | #define CALL_FUNCTION_WITH_ONE_PARAM_THROUGH_MACRO(func, param) \ |
292 | CALL_FUNCTION(func(param)) |
293 | |
294 | void lParenRParenInNestedMacro() { |
295 | int *ptr; |
296 | CALL_FUNCTION_WITH_ONE_PARAM_THROUGH_MACRO(setToNull, &ptr); |
297 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
298 | } |
299 | |
300 | // CHECK: <key>name</key><string>CALL_FUNCTION_WITH_ONE_PARAM_THROUGH_MACRO</string> |
301 | // CHECK-NEXT: <key>expansion</key><string>setToNull( &ptr)</string> |
302 | |
303 | //===----------------------------------------------------------------------===// |
304 | // Tests for variadic macro arguments. |
305 | //===----------------------------------------------------------------------===// |
306 | |
307 | template <typename ...Args> |
308 | void variadicFunc(Args ...args); |
309 | |
310 | #define VARIADIC_SET_TO_NULL(ptr, ...) \ |
311 | ptr = nullptr; \ |
312 | variadicFunc(__VA_ARGS__) |
313 | |
314 | void variadicMacroArgumentTest() { |
315 | int *ptr; |
316 | VARIADIC_SET_TO_NULL(ptr, 1, 5, "haha!"); |
317 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
318 | } |
319 | |
320 | // CHECK: <key>name</key><string>VARIADIC_SET_TO_NULL</string> |
321 | // CHECK-NEXT: <key>expansion</key><string>ptr = nullptr; variadicFunc( 1, 5, "haha!")</string> |
322 | |
323 | void variadicMacroArgumentWithoutAnyArgumentTest() { |
324 | int *ptr; |
325 | // Not adding a single parameter to ... is silly (and also causes a |
326 | // preprocessor warning), but is not an excuse to crash on it. |
327 | VARIADIC_SET_TO_NULL(ptr); |
328 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
329 | } |
330 | |
331 | // CHECK: <key>name</key><string>VARIADIC_SET_TO_NULL</string> |
332 | // CHECK-NEXT: <key>expansion</key><string>ptr = nullptr; variadicFunc()</string> |
333 | |
334 | //===----------------------------------------------------------------------===// |
335 | // Tests for # and ##. |
336 | //===----------------------------------------------------------------------===// |
337 | |
338 | #define DECLARE_FUNC_AND_SET_TO_NULL(funcName, ptr) \ |
339 | void generated_##funcName(); \ |
340 | ptr = nullptr; |
341 | |
342 | void hashHashOperatorTest() { |
343 | int *ptr; |
344 | DECLARE_FUNC_AND_SET_TO_NULL(whatever, ptr); |
345 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
346 | } |
347 | |
348 | // CHECK: <key>name</key><string>DECLARE_FUNC_AND_SET_TO_NULL</string> |
349 | // CHECK-NEXT: <key>expansion</key><string>void generated_whatever(); ptr = nullptr;</string> |
350 | |
351 | void macroArgContainsHashHashInStringTest() { |
352 | int *a; |
353 | TO_NULL_AND_PRINT(a, "Will this ## cause a crash?"); |
354 | *a = 5; // expected-warning{{Dereference of null pointer}} |
355 | } |
356 | |
357 | // CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string> |
358 | // CHECK-NEXT: <key>expansion</key><string>a = 0; print( "Will this ## cause a crash?")</string> |
359 | |
360 | #define PRINT_STR(str, ptr) \ |
361 | print(#str); \ |
362 | ptr = nullptr |
363 | |
364 | void hashOperatorTest() { |
365 | int *ptr; |
366 | PRINT_STR(Hello, ptr); |
367 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
368 | } |
369 | |
370 | // CHECK: <key>name</key><string>PRINT_STR</string> |
371 | // CHECK-NEXT: <key>expansion</key><string>print("Hello"); ptr = nullptr</string> |
372 | |
373 | void macroArgContainsHashInStringTest() { |
374 | int *a; |
375 | TO_NULL_AND_PRINT(a, "Will this # cause a crash?"); |
376 | *a = 5; // expected-warning{{Dereference of null pointer}} |
377 | } |
378 | |
379 | // CHECK: <key>name</key><string>TO_NULL_AND_PRINT</string> |
380 | // CHECK-NEXT: <key>expansion</key><string>a = 0; print( "Will this # cause a crash?")</string> |
381 | |
382 | //===----------------------------------------------------------------------===// |
383 | // Tests for more complex macro expansions. |
384 | // |
385 | // We won't cover anything that wasn't covered up to this point, but rather |
386 | // show more complex, macros with deeper nesting, more arguments (some unused) |
387 | // and so on. |
388 | //===----------------------------------------------------------------------===// |
389 | |
390 | #define IF(Condition) \ |
391 | if ( Condition ) |
392 | |
393 | #define L_BRACE { |
394 | #define R_BRACE } |
395 | #define LESS < |
396 | #define GREATER > |
397 | #define EQUALS = |
398 | #define SEMICOLON ; |
399 | #define NEGATIVE - |
400 | #define RETURN return |
401 | #define ZERO 0 |
402 | |
403 | #define EUCLIDEAN_ALGORITHM(A, B) \ |
404 | IF(A LESS ZERO) L_BRACE \ |
405 | A EQUALS NEGATIVE A SEMICOLON \ |
406 | R_BRACE \ |
407 | IF(B LESS ZERO) L_BRACE \ |
408 | B EQUALS NEGATIVE B SEMICOLON \ |
409 | R_BRACE \ |
410 | \ |
411 | /* This is where a while loop would be, but that seems to be too complex */ \ |
412 | /* for the analyzer just yet. Let's just pretend that this algorithm */ \ |
413 | /* works. */ \ |
414 | \ |
415 | RETURN B / (B - B) SEMICOLON |
416 | |
417 | int getLowestCommonDenominator(int A, int B) { |
418 | EUCLIDEAN_ALGORITHM(A, B) // expected-warning{{Division by zero}} |
419 | } |
420 | |
421 | void testVeryComplexAlgorithm() { |
422 | int tmp = 8 / (getLowestCommonDenominator(5, 7) - 1); |
423 | print(&tmp); |
424 | } |
425 | // CHECK: <key>name</key><string>EUCLIDEAN_ALGORITHM</string> |
426 | // CHECK-NEXT: <key>expansion</key><string>if (A<0 ){A=-A;} if ( B<0 ){ B=- B;}return B / ( B - B);</string> |
427 | |
428 | #define YET_ANOTHER_SET_TO_NULL(x, y, z) \ |
429 | print((void *) x); \ |
430 | print((void *) y); \ |
431 | z = nullptr; |
432 | |
433 | #define DO_NOTHING(str) str |
434 | #define DO_NOTHING2(str2) DO_NOTHING(str2) |
435 | |
436 | void test() { |
437 | int *ptr; |
438 | YET_ANOTHER_SET_TO_NULL(5, DO_NOTHING2("Remember the Vasa"), ptr); |
439 | *ptr = 5; // expected-warning{{Dereference of null pointer}} |
440 | } |
441 | // CHECK: <key>name</key><string>YET_ANOTHER_SET_TO_NULL</string> |
442 | // CHECK-NEXT: <key>expansion</key><string>print((void *)5); print((void *)"Remember the Vasa"); ptr = nullptr;</string> |
443 | |
444 | int garbage_value; |
445 | |
446 | #define REC_MACRO_FUNC(REC_MACRO_PARAM) garbage_##REC_MACRO_PARAM |
447 | #define value REC_MACRO_FUNC(value) |
448 | |
449 | void recursiveMacroUser() { |
450 | if (value == 0) |
451 | 1 / value; // expected-warning{{Division by zero}} |
452 | // expected-warning@-1{{expression result unused}} |
453 | } |
454 | |
455 | #define FOO(x) int foo() { return x; } |
456 | #define APPLY_ZERO1(function) function(0) |
457 | |
458 | APPLY_ZERO1(FOO) |
459 | void useZeroApplier1() { (void)(1 / foo()); } // expected-warning{{Division by zero}} |
460 | |
461 | // CHECK: <key>name</key><string>APPLY_ZERO1</string> |
462 | // CHECK-NEXT: <key>expansion</key><string>int foo() { return x; }(0)</string> |
463 | |
464 | #define BAR(x) int bar() { return x; } |
465 | #define APPLY_ZERO2 BAR(0) |
466 | |
467 | APPLY_ZERO2 |
468 | void useZeroApplier2() { (void)(1 / bar()); } // expected-warning{{Division by zero}} |
469 | |
470 | // CHECK: <key>name</key><string>APPLY_ZERO2</string> |
471 | // CHECK-NEXT: <key>expansion</key><string>int bar() { return 0; }</string> |
472 | |