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