// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=core %s \ // RUN: -analyzer-output=plist -o %t.plist \ // RUN: -analyzer-config expand-macros=true // // Check the actual plist output. // RUN: %normalize_plist <%t.plist | diff -ub \ // RUN: %S/Inputs/expected-plists/plist-macros-with-expansion.cpp.plist - // // Check the macro expansions from the plist output here, to make the test more // understandable. // RUN: FileCheck --input-file=%t.plist %s void print(const void*); //===----------------------------------------------------------------------===// // Tests for non-function-like macro expansions. //===----------------------------------------------------------------------===// #define SET_PTR_VAR_TO_NULL \ ptr = 0 void nonFunctionLikeMacroTest() { int *ptr; SET_PTR_VAR_TO_NULL; *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameSET_PTR_VAR_TO_NULL // CHECK-NEXT: expansionptr = 0 #define NULL 0 #define SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO \ ptr = NULL void nonFunctionLikeNestedMacroTest() { int *ptr; SET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO; *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameSET_PTR_VAR_TO_NULL_WITH_NESTED_MACRO // CHECK-NEXT: expansionptr =0 //===----------------------------------------------------------------------===// // Tests for function-like macro expansions. //===----------------------------------------------------------------------===// void setToNull(int **vptr) { *vptr = nullptr; } #define TO_NULL(x) \ setToNull(x) void functionLikeMacroTest() { int *ptr; TO_NULL(&ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameTO_NULL // CHECK-NEXT: expansionsetToNull(&ptr) #define DOES_NOTHING(x) \ { \ int b; \ b = 5; \ } \ print(x) #define DEREF(x) \ DOES_NOTHING(x); \ *x void functionLikeNestedMacroTest() { int *a; TO_NULL(&a); DEREF(a) = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameTO_NULL // CHECK-NEXT: expansionsetToNull(&a) // CHECK: nameDEREF // CHECK-NEXT: expansion{ int b; b = 5; } print(a); *a //===----------------------------------------------------------------------===// // Tests for undefining and/or redifining macros. //===----------------------------------------------------------------------===// #define WILL_UNDEF_SET_NULL_TO_PTR(ptr) \ ptr = nullptr; void undefinedMacroByTheEndOfParsingTest() { int *ptr; WILL_UNDEF_SET_NULL_TO_PTR(ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } #undef WILL_UNDEF_SET_NULL_TO_PTR // CHECK: nameWILL_UNDEF_SET_NULL_TO_PTR // CHECK-NEXT: expansionptr = nullptr; #define WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr) \ /* Nothing */ #undef WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL #define WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr) \ ptr = nullptr; void macroRedefinedMultipleTimesTest() { int *ptr; WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr) *ptr = 5; // expected-warning{{Dereference of null pointer}} } #undef WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL #define WILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL(ptr) \ print("This string shouldn't be in the plist file at all. Or anywhere, " \ "but here."); // CHECK: nameWILL_REDIFINE_MULTIPLE_TIMES_SET_TO_NULL // CHECK-NEXT: expansionptr = nullptr; #define WILL_UNDEF_SET_NULL_TO_PTR_2(ptr) \ ptr = nullptr; #define PASS_PTR_TO_MACRO_THAT_WILL_BE_UNDEFD(ptr) \ WILL_UNDEF_SET_NULL_TO_PTR_2(ptr) void undefinedMacroInsideAnotherMacroTest() { int *ptr; PASS_PTR_TO_MACRO_THAT_WILL_BE_UNDEFD(ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // TODO: Expand arguments. // CHECK: namePASS_PTR_TO_MACRO_THAT_WILL_BE_UNDEFD // CHECK-NEXT: expansionptr = nullptr; #undef WILL_UNDEF_SET_NULL_TO_PTR_2 //===----------------------------------------------------------------------===// // Tests for macro arguments containing commas and parantheses. // // As of writing these tests, the algorithm expands macro arguments by lexing // the macro's expansion location, and relies on finding tok::comma and // tok::l_paren/tok::r_paren. //===----------------------------------------------------------------------===// // Note that this commas, parantheses in strings aren't parsed as tok::comma or // tok::l_paren/tok::r_paren, but why not test them. #define TO_NULL_AND_PRINT(x, str) \ x = 0; \ print(str) void macroArgContainsCommaInStringTest() { int *a; TO_NULL_AND_PRINT(a, "Will this , cause a crash?"); *a = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameTO_NULL_AND_PRINT // CHECK-NEXT: expansiona = 0; print( "Will this , cause a crash?") void macroArgContainsLParenInStringTest() { int *a; TO_NULL_AND_PRINT(a, "Will this ( cause a crash?"); *a = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameTO_NULL_AND_PRINT // CHECK-NEXT: expansiona = 0; print( "Will this ( cause a crash?") void macroArgContainsRParenInStringTest() { int *a; TO_NULL_AND_PRINT(a, "Will this ) cause a crash?"); *a = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameTO_NULL_AND_PRINT // CHECK-NEXT: expansiona = 0; print( "Will this ) cause a crash?") #define CALL_FUNCTION(funcCall) \ funcCall // Function calls do contain both tok::comma and tok::l_paren/tok::r_paren. void macroArgContainsLParenRParenTest() { int *a; CALL_FUNCTION(setToNull(&a)); *a = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameCALL_FUNCTION // CHECK-NEXT: expansionsetToNull(&a) void setToNullAndPrint(int **vptr, const char *str) { setToNull(vptr); print(str); } void macroArgContainsCommaLParenRParenTest() { int *a; CALL_FUNCTION(setToNullAndPrint(&a, "Hello!")); *a = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameCALL_FUNCTION // CHECK-NEXT: expansionsetToNullAndPrint(&a, "Hello!") #define CALL_FUNCTION_WITH_TWO_PARAMS(funcCall, param1, param2) \ funcCall(param1, param2) void macroArgContainsCommaLParenRParenTest2() { int *a; CALL_FUNCTION_WITH_TWO_PARAMS(setToNullAndPrint, &a, "Hello!"); *a = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameCALL_FUNCTION_WITH_TWO_PARAMS // CHECK-NEXT: expansionsetToNullAndPrint( &a, "Hello!") #define CALL_LAMBDA(l) \ l() void commaInBracketsTest() { int *ptr; const char str[] = "Hello!"; // You need to add parantheses around a lambda expression to compile this, // else the comma in the capture will be parsed as divider of macro args. CALL_LAMBDA(([&ptr, str] () mutable { TO_NULL(&ptr); })); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameCALL_LAMBDA // CHECK-NEXT: expansion([&ptr, str] () mutable { setToNull(&ptr); })() #define PASTE_CODE(code) \ code void commaInBracesTest() { PASTE_CODE({ // expected-warning{{Dereference of null pointer}} // NOTE: If we were to add a new variable here after a comma, we'd get a // compilation error, so this test is mainly here to show that this was also // investigated. // int *ptr = nullptr, a; int *ptr = nullptr; *ptr = 5; }) } // CHECK: namePASTE_CODE // CHECK-NEXT: expansion{ int *ptr = nullptr; *ptr = 5; } // Example taken from // https://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments.html#Macro-Arguments. #define POTENTIALLY_EMPTY_PARAM(x, y) \ x; \ y = nullptr void emptyParamTest() { int *ptr; POTENTIALLY_EMPTY_PARAM(,ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: namePOTENTIALLY_EMPTY_PARAM // CHECK-NEXT: expansion;ptr = nullptr #define NESTED_EMPTY_PARAM(a, b) \ POTENTIALLY_EMPTY_PARAM(a, b); void nestedEmptyParamTest() { int *ptr; NESTED_EMPTY_PARAM(, ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameNESTED_EMPTY_PARAM // CHECK-NEXT: expansion; ptr = nullptr; #define CALL_FUNCTION_WITH_ONE_PARAM_THROUGH_MACRO(func, param) \ CALL_FUNCTION(func(param)) void lParenRParenInNestedMacro() { int *ptr; CALL_FUNCTION_WITH_ONE_PARAM_THROUGH_MACRO(setToNull, &ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameCALL_FUNCTION_WITH_ONE_PARAM_THROUGH_MACRO // CHECK-NEXT: expansionsetToNull( &ptr) //===----------------------------------------------------------------------===// // Tests for variadic macro arguments. //===----------------------------------------------------------------------===// template void variadicFunc(Args ...args); #define VARIADIC_SET_TO_NULL(ptr, ...) \ ptr = nullptr; \ variadicFunc(__VA_ARGS__) void variadicMacroArgumentTest() { int *ptr; VARIADIC_SET_TO_NULL(ptr, 1, 5, "haha!"); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameVARIADIC_SET_TO_NULL // CHECK-NEXT: expansionptr = nullptr; variadicFunc( 1, 5, "haha!") void variadicMacroArgumentWithoutAnyArgumentTest() { int *ptr; // Not adding a single parameter to ... is silly (and also causes a // preprocessor warning), but is not an excuse to crash on it. VARIADIC_SET_TO_NULL(ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameVARIADIC_SET_TO_NULL // CHECK-NEXT: expansionptr = nullptr; variadicFunc() //===----------------------------------------------------------------------===// // Tests for # and ##. //===----------------------------------------------------------------------===// #define DECLARE_FUNC_AND_SET_TO_NULL(funcName, ptr) \ void generated_##funcName(); \ ptr = nullptr; void hashHashOperatorTest() { int *ptr; DECLARE_FUNC_AND_SET_TO_NULL(whatever, ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameDECLARE_FUNC_AND_SET_TO_NULL // CHECK-NEXT: expansionvoid generated_whatever(); ptr = nullptr; void macroArgContainsHashHashInStringTest() { int *a; TO_NULL_AND_PRINT(a, "Will this ## cause a crash?"); *a = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameTO_NULL_AND_PRINT // CHECK-NEXT: expansiona = 0; print( "Will this ## cause a crash?") #define PRINT_STR(str, ptr) \ print(#str); \ ptr = nullptr void hashOperatorTest() { int *ptr; PRINT_STR(Hello, ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: namePRINT_STR // CHECK-NEXT: expansionprint("Hello"); ptr = nullptr void macroArgContainsHashInStringTest() { int *a; TO_NULL_AND_PRINT(a, "Will this # cause a crash?"); *a = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameTO_NULL_AND_PRINT // CHECK-NEXT: expansiona = 0; print( "Will this # cause a crash?") //===----------------------------------------------------------------------===// // Tests for more complex macro expansions. // // We won't cover anything that wasn't covered up to this point, but rather // show more complex, macros with deeper nesting, more arguments (some unused) // and so on. //===----------------------------------------------------------------------===// #define IF(Condition) \ if ( Condition ) #define L_BRACE { #define R_BRACE } #define LESS < #define GREATER > #define EQUALS = #define SEMICOLON ; #define NEGATIVE - #define RETURN return #define ZERO 0 #define EUCLIDEAN_ALGORITHM(A, B) \ IF(A LESS ZERO) L_BRACE \ A EQUALS NEGATIVE A SEMICOLON \ R_BRACE \ IF(B LESS ZERO) L_BRACE \ B EQUALS NEGATIVE B SEMICOLON \ R_BRACE \ \ /* This is where a while loop would be, but that seems to be too complex */ \ /* for the analyzer just yet. Let's just pretend that this algorithm */ \ /* works. */ \ \ RETURN B / (B - B) SEMICOLON int getLowestCommonDenominator(int A, int B) { EUCLIDEAN_ALGORITHM(A, B) // expected-warning{{Division by zero}} } void testVeryComplexAlgorithm() { int tmp = 8 / (getLowestCommonDenominator(5, 7) - 1); print(&tmp); } // CHECK: nameEUCLIDEAN_ALGORITHM // CHECK-NEXT: expansionif (A<0 ){A=-A;} if ( B<0 ){ B=- B;}return B / ( B - B); #define YET_ANOTHER_SET_TO_NULL(x, y, z) \ print((void *) x); \ print((void *) y); \ z = nullptr; #define DO_NOTHING(str) str #define DO_NOTHING2(str2) DO_NOTHING(str2) void test() { int *ptr; YET_ANOTHER_SET_TO_NULL(5, DO_NOTHING2("Remember the Vasa"), ptr); *ptr = 5; // expected-warning{{Dereference of null pointer}} } // CHECK: nameYET_ANOTHER_SET_TO_NULL // CHECK-NEXT: expansionprint((void *)5); print((void *)"Remember the Vasa"); ptr = nullptr; int garbage_value; #define REC_MACRO_FUNC(REC_MACRO_PARAM) garbage_##REC_MACRO_PARAM #define value REC_MACRO_FUNC(value) void recursiveMacroUser() { if (value == 0) 1 / value; // expected-warning{{Division by zero}} // expected-warning@-1{{expression result unused}} } // CHECK: namevalue // CHECK-NEXT: expansiongarbage_ #define FOO(x) int foo() { return x; } #define APPLY_ZERO1(function) function(0) APPLY_ZERO1(FOO) void useZeroApplier1() { (void)(1 / foo()); } // expected-warning{{Division by zero}} // CHECK: nameAPPLY_ZERO1 // CHECK-NEXT: expansionint foo() { return x; }(0) #define BAR(x) int bar() { return x; } #define APPLY_ZERO2 BAR(0) APPLY_ZERO2 void useZeroApplier2() { (void)(1 / bar()); } // expected-warning{{Division by zero}} // CHECK: nameAPPLY_ZERO2 // CHECK-NEXT: expansionint bar() { return 0; } void foo(int &x, const char *str); #define PARAMS_RESOLVE_TO_VA_ARGS(i, fmt) foo(i, fmt); \ i = 0; #define DISPATCH(...) PARAMS_RESOLVE_TO_VA_ARGS(__VA_ARGS__); void mulitpleParamsResolveToVA_ARGS(void) { int x = 1; DISPATCH(x, "LF1M healer"); (void)(10 / x); // expected-warning{{Division by zero}} } // CHECK: nameDISPATCH // CHECK-NEXT: expansionfoo(x, "LF1M healer");x = 0;; void variadicCFunction(int &x, const char *str, ...); #define CONCAT_VA_ARGS(i, fmt, ...) variadicCFunction(i, fmt, ##__VA_ARGS__); \ i = 0; void concatVA_ARGS(void) { int x = 1; CONCAT_VA_ARGS(x, "You need to construct additional pylons.", 'c', 9); (void)(10 / x); // expected-warning{{Division by zero}} } // CHECK: nameCONCAT_VA_ARGS // CHECK-NEXT: expansionvariadicCFunction(x, "You need to construct additional pylons.",'c', 9);x = 0; void concatVA_ARGSEmpty(void) { int x = 1; CONCAT_VA_ARGS(x, "You need to construct"); (void)(10 / x); // expected-warning{{Division by zero}} } // FIXME: The comma shouldn't be present after the last argument. // CHECK: nameCONCAT_VA_ARGS // CHECK-NEXT: expansionvariadicCFunction(x, "You need to construct",);x = 0; #define STRINGIFIED_VA_ARGS(i, fmt, ...) variadicCFunction(i, fmt, #__VA_ARGS__); \ i = 0; void stringifyVA_ARGS(void) { int x = 1; STRINGIFIED_VA_ARGS(x, "Additional supply depots required.", 'a', 10); (void)(10 / x); // expected-warning{{Division by zero}} } // FIXME: Stringify and escape __VA_ARGS__ correctly. // CHECK: nameSTRINGIFIED_VA_ARGS // CHECK-NEXT: expansionvariadicCFunction(x, "Additional supply depots required.", "'a'", 10);x = 0; void stringifyVA_ARGSEmpty(void) { int x = 1; STRINGIFIED_VA_ARGS(x, "Additional supply depots required."); (void)(10 / x); // expected-warning{{Division by zero}} } // FIXME: Stringify and escape __VA_ARGS__ correctly. // CHECK: nameSTRINGIFIED_VA_ARGS // CHECK-NEXT: expansionvariadicCFunction(x, "Additional supply depots required.", ")";x = 0; // bz44493: Support GNU-style named variadic arguments in plister #define BZ44493_GNUVA(i, args...) --(i); int bz44493(void) { int a = 2; BZ44493_GNUVA(a); BZ44493_GNUVA(a, "arg2"); (void)(10 / a); // expected-warning{{Division by zero}} return 0; } // CHECK: nameBZ44493_GNUVA // CHECK-NEXT: expansion--(a);