Clang Project

clang_source_code/test/Analysis/plist-macros-with-expansion.cpp
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
15void 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
24void 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
37void 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
50void setToNull(int **vptr) {
51  *vptr = nullptr;
52}
53
54#define TO_NULL(x) \
55  setToNull(x)
56
57void 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(&amp;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
77void 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(&amp;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
96void 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
113void 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
133void 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
160void 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( &quot;Will this , cause a crash?&quot;)</string>
168
169void 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( &quot;Will this ( cause a crash?&quot;)</string>
177
178void 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( &quot;Will this ) cause a crash?&quot;)</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
192void 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(&amp;a)</string>
200
201void setToNullAndPrint(int **vptr, const char *str) {
202  setToNull(vptr);
203  print(str);
204}
205
206void 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(&amp;a, &quot;Hello!&quot;)</string>
214
215#define CALL_FUNCTION_WITH_TWO_PARAMS(funcCall, param1, param2) \
216  funcCall(param1, param2)
217
218void 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( &amp;a, &quot;Hello!&quot;)</string>
226
227#define CALL_LAMBDA(l) \
228  l()
229
230void 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>([&amp;ptr, str] () mutable { setToNull(&amp;ptr); })()</string>
241
242#define PASTE_CODE(code) \
243  code
244
245void 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
267void 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
281void 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
294void 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( &amp;ptr)</string>
302
303//===----------------------------------------------------------------------===//
304// Tests for variadic macro arguments.
305//===----------------------------------------------------------------------===//
306
307template <typename ...Args>
308void variadicFunc(Args ...args);
309
310#define VARIADIC_SET_TO_NULL(ptr, ...) \
311  ptr = nullptr;                       \
312  variadicFunc(__VA_ARGS__)
313
314void 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, &quot;haha!&quot;)</string>
322
323void 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
342void 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
351void 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( &quot;Will this ## cause a crash?&quot;)</string>
359
360#define PRINT_STR(str, ptr) \
361  print(#str);              \
362  ptr = nullptr
363
364void 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(&quot;Hello&quot;); ptr = nullptr</string>
372
373void 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( &quot;Will this # cause a crash?&quot;)</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
417int getLowestCommonDenominator(int A, int B) {
418  EUCLIDEAN_ALGORITHM(A, B) // expected-warning{{Division by zero}}
419}
420
421void 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&lt;0 ){A=-A;} if ( B&lt;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
436void 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 *)&quot;Remember the Vasa&quot;); ptr = nullptr;</string>
443
444int garbage_value;
445
446#define REC_MACRO_FUNC(REC_MACRO_PARAM) garbage_##REC_MACRO_PARAM
447#define value REC_MACRO_FUNC(value)
448
449void 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
458APPLY_ZERO1(FOO)
459void 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
467APPLY_ZERO2
468void 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