// RUN: %clang %s -std=c++17 -Xclang -verify --analyze \ // RUN: -Xclang -analyzer-checker=core \ // RUN: -Xclang -analyzer-checker=debug.ExprInspection \ // RUN: -Xclang -analyzer-checker=core,alpha.core.StdVariant #include "Inputs/system-header-simulator-cxx.h" class Foo{}; void clang_analyzer_warnIfReached(); void clang_analyzer_eval(int); //helper functions void changeVariantType(std::variant &v) { v = 25; } void changesToInt(std::variant &v); void changesToInt(std::variant *v); void cannotChangePtr(const std::variant &v); void cannotChangePtr(const std::variant *v); char getUnknownChar(); void swap(std::variant &v1, std::variant &v2) { std::variant tmp = v1; v1 = v2; v2 = tmp; } void cantDo(const std::variant& v) { std::variant vtmp = v; vtmp = 5; int a = std::get (vtmp); (void) a; } void changeVariantPtr(std::variant *v) { *v = 'c'; } using var_t = std::variant; using var_tt = var_t; using int_t = int; using char_t = char; // A quick sanity check to see that std::variant's std::get // is not being confused with std::pairs std::get. void wontConfuseStdGets() { std::pair p{15, '1'}; int a = std::get(p); char c = std::get(p); (void)a; (void)c; } //----------------------------------------------------------------------------// // std::get //----------------------------------------------------------------------------// void stdGetType() { std::variant v = 25; int a = std::get(v); char c = std::get(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} (void)a; (void)c; } void stdGetPointer() { int *p = new int; std::variant v = p; int *a = std::get(v); char c = std::get(v); // expected-warning {{std::variant 'v' held an 'int *', not a 'char'}} (void)a; (void)c; delete p; } void stdGetObject() { std::variant v = Foo{}; Foo f = std::get(v); int i = std::get(v); // expected-warning {{std::variant 'v' held a 'Foo', not an 'int'}} (void)i; } void stdGetPointerAndPointee() { int a = 5; std::variant v = &a; int *b = std::get(v); int c = std::get(v); // expected-warning {{std::variant 'v' held an 'int *', not an 'int'}} (void)c; (void)b; } void variantHoldingVariant() { std::variant, std::variant> v = std::variant(25); std::variant v1 = std::get>(v); std::variant v2 = std::get>(v); // expected-warning {{std::variant 'v' held a 'std::variant', not a 'class std::variant'}} } //----------------------------------------------------------------------------// // Constructors and assignments //----------------------------------------------------------------------------// void copyConstructor() { std::variant v = 25; std::variant t(v); int a = std::get (t); char c = std::get (t); // expected-warning {{std::variant 't' held an 'int', not a 'char'}} (void)a; (void)c; } void copyAssignmentOperator() { std::variant v = 25; std::variant t = 'c'; t = v; int a = std::get (t); char c = std::get (t); // expected-warning {{std::variant 't' held an 'int', not a 'char'}} (void)a; (void)c; } void assignmentOperator() { std::variant v = 25; int a = std::get (v); (void)a; v = 'c'; char c = std::get(v); a = std::get(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} (void)a; (void)c; } void typeChangeThreeTimes() { std::variant v = 25; int a = std::get (v); (void)a; v = 'c'; char c = std::get(v); v = 25; a = std::get(v); (void)a; v = 1.25f; float f = std::get(v); a = std::get(v); // expected-warning {{std::variant 'v' held a 'float', not an 'int'}} (void)a; (void)c; (void)f; } void defaultConstructor() { std::variant v; int i = std::get(v); char c = std::get(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} (void)i; (void)c; } // Verify that we handle temporary objects correctly void temporaryObjectsConstructor() { std::variant v(std::variant('c')); char c = std::get(v); int a = std::get(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} (void)a; (void)c; } void temporaryObjectsAssignment() { std::variant v = std::variant('c'); char c = std::get(v); int a = std::get(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} (void)a; (void)c; } // Verify that we handle pointer types correctly void pointerTypeHeld() { int *p = new int; std::variant v = p; int *a = std::get(v); char c = std::get(v); // expected-warning {{std::variant 'v' held an 'int *', not a 'char'}} (void)a; (void)c; delete p; } std::variant get_unknown_variant(); // Verify that the copy constructor is handles properly when the std::variant // has no previously activated type and we copy an object of unknown value in it. void copyFromUnknownVariant() { std::variant u = get_unknown_variant(); std::variant v(u); int a = std::get(v); // no-waring char c = std::get(v); // no-warning (void)a; (void)c; } // Verify that the copy constructor is handles properly when the std::variant // has previously activated type and we copy an object of unknown value in it. void copyFromUnknownVariantBef() { std::variant v = 25; std::variant u = get_unknown_variant(); v = u; int a = std::get(v); // no-waring char c = std::get(v); // no-warning (void)a; (void)c; } //----------------------------------------------------------------------------// // typedef //----------------------------------------------------------------------------// void typefdefedVariant() { var_t v = 25; int a = std::get(v); char c = std::get(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} (void)a; (void)c; } void typedefedTypedfefedVariant() { var_tt v = 25; int a = std::get(v); char c = std::get(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} (void)a; (void)c; } void typedefedGet() { std::variant v = 25; int a = std::get(v); char c = std::get(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} (void)a; (void)c; } void typedefedPack() { std::variant v = 25; int a = std::get(v); char c = std::get(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} (void)a; (void)c; } void fromVariable() { char o = 'c'; std::variant v(o); char c = std::get(v); int a = std::get(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} (void)a; (void)c; } void unknowValueButKnownType() { char o = getUnknownChar(); std::variant v(o); char c = std::get(v); int a = std::get(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} (void)a; (void)c; } void createPointer() { std::variant *v = new std::variant(15); int a = std::get(*v); char c = std::get(*v); // expected-warning {{std::variant held an 'int', not a 'char'}} (void)a; (void)c; delete v; } //----------------------------------------------------------------------------// // Passing std::variants to functions //----------------------------------------------------------------------------// // Verifying that we are not invalidating the memory region of a variant if // a non inlined or inlined function takes it as a constant reference or pointer void constNonInlineRef() { std::variant v = 'c'; cannotChangePtr(v); char c = std::get(v); int a = std::get(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} (void)a; (void)c; } void contNonInlinePtr() { std::variant v = 'c'; cannotChangePtr(&v); char c = std::get(v); int a = std::get(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} (void)a; (void)c; } void copyInAFunction() { std::variant v = 'c'; cantDo(v); char c = std::get(v); int a = std::get(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} (void)a; (void)c; } // Verifying that we can keep track of the type stored in std::variant when // it is passed to an inlined function as a reference or pointer void changeThruPointers() { std::variant v = 15; changeVariantPtr(&v); char c = std::get (v); int a = std::get (v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} (void)a; (void)c; } void functionCallWithCopyAssignment() { var_t v1 = 15; var_t v2 = 'c'; swap(v1, v2); int a = std::get (v2); (void)a; char c = std::get (v1); a = std::get (v1); // expected-warning {{std::variant 'v1' held a 'char', not an 'int'}} (void)a; (void)c; } void inlineFunctionCall() { std::variant v = 'c'; changeVariantType(v); int a = std::get (v); char c = std::get (v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} (void)a; (void)c; } // Verifying that we invalidate the mem region of std::variant when it is // passed as a non const reference or a pointer to a non inlined function. void nonInlineFunctionCall() { std::variant v = 'c'; changesToInt(v); int a = std::get (v); // no-waring char c = std::get (v); // no-warning (void)a; (void)c; } void nonInlineFunctionCallPtr() { std::variant v = 'c'; changesToInt(&v); int a = std::get (v); // no-warning char c = std::get (v); // no-warning (void)a; (void)c; }