| // 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<int, char> &v) { | |
| v = 25; | |
| } | |
| void changesToInt(std::variant<int, char> &v); | |
| void changesToInt(std::variant<int, char> *v); | |
| void cannotChangePtr(const std::variant<int, char> &v); | |
| void cannotChangePtr(const std::variant<int, char> *v); | |
| char getUnknownChar(); | |
| void swap(std::variant<int, char> &v1, std::variant<int, char> &v2) { | |
| std::variant<int, char> tmp = v1; | |
| v1 = v2; | |
| v2 = tmp; | |
| } | |
| void cantDo(const std::variant<int, char>& v) { | |
| std::variant<int, char> vtmp = v; | |
| vtmp = 5; | |
| int a = std::get<int> (vtmp); | |
| (void) a; | |
| } | |
| void changeVariantPtr(std::variant<int, char> *v) { | |
| *v = 'c'; | |
| } | |
| using var_t = std::variant<int, char>; | |
| 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<int, char> p{15, '1'}; | |
| int a = std::get<int>(p); | |
| char c = std::get<char>(p); | |
| (void)a; | |
| (void)c; | |
| } | |
| //----------------------------------------------------------------------------// | |
| // std::get | |
| //----------------------------------------------------------------------------// | |
| void stdGetType() { | |
| std::variant<int, char> v = 25; | |
| int a = std::get<int>(v); | |
| char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void stdGetPointer() { | |
| int *p = new int; | |
| std::variant<int*, char> v = p; | |
| int *a = std::get<int*>(v); | |
| char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int *', not a 'char'}} | |
| (void)a; | |
| (void)c; | |
| delete p; | |
| } | |
| void stdGetObject() { | |
| std::variant<int, char, Foo> v = Foo{}; | |
| Foo f = std::get<Foo>(v); | |
| int i = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'Foo', not an 'int'}} | |
| (void)i; | |
| } | |
| void stdGetPointerAndPointee() { | |
| int a = 5; | |
| std::variant<int, int*> v = &a; | |
| int *b = std::get<int*>(v); | |
| int c = std::get<int>(v); // expected-warning {{std::variant 'v' held an 'int *', not an 'int'}} | |
| (void)c; | |
| (void)b; | |
| } | |
| void variantHoldingVariant() { | |
| std::variant<std::variant<int, char>, std::variant<char, int>> v = std::variant<int,char>(25); | |
| std::variant<int, char> v1 = std::get<std::variant<int,char>>(v); | |
| std::variant<char, int> v2 = std::get<std::variant<char,int>>(v); // expected-warning {{std::variant 'v' held a 'std::variant<int, char>', not a 'class std::variant<char, int>'}} | |
| } | |
| //----------------------------------------------------------------------------// | |
| // Constructors and assignments | |
| //----------------------------------------------------------------------------// | |
| void copyConstructor() { | |
| std::variant<int, char> v = 25; | |
| std::variant<int, char> t(v); | |
| int a = std::get<int> (t); | |
| char c = std::get<char> (t); // expected-warning {{std::variant 't' held an 'int', not a 'char'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void copyAssignmentOperator() { | |
| std::variant<int, char> v = 25; | |
| std::variant<int, char> t = 'c'; | |
| t = v; | |
| int a = std::get<int> (t); | |
| char c = std::get<char> (t); // expected-warning {{std::variant 't' held an 'int', not a 'char'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void assignmentOperator() { | |
| std::variant<int, char> v = 25; | |
| int a = std::get<int> (v); | |
| (void)a; | |
| v = 'c'; | |
| char c = std::get<char>(v); | |
| a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void typeChangeThreeTimes() { | |
| std::variant<int, char, float> v = 25; | |
| int a = std::get<int> (v); | |
| (void)a; | |
| v = 'c'; | |
| char c = std::get<char>(v); | |
| v = 25; | |
| a = std::get<int>(v); | |
| (void)a; | |
| v = 1.25f; | |
| float f = std::get<float>(v); | |
| a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'float', not an 'int'}} | |
| (void)a; | |
| (void)c; | |
| (void)f; | |
| } | |
| void defaultConstructor() { | |
| std::variant<int, char> v; | |
| int i = std::get<int>(v); | |
| char c = std::get<char>(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<int, char> v(std::variant<int, char>('c')); | |
| char c = std::get<char>(v); | |
| int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void temporaryObjectsAssignment() { | |
| std::variant<int, char> v = std::variant<int, char>('c'); | |
| char c = std::get<char>(v); | |
| int a = std::get<int>(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<int*, char> v = p; | |
| int *a = std::get<int*>(v); | |
| char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int *', not a 'char'}} | |
| (void)a; | |
| (void)c; | |
| delete p; | |
| } | |
| std::variant<int, char> 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<int, char> u = get_unknown_variant(); | |
| std::variant<int, char> v(u); | |
| int a = std::get<int>(v); // no-waring | |
| char c = std::get<char>(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<int, char> v = 25; | |
| std::variant<int, char> u = get_unknown_variant(); | |
| v = u; | |
| int a = std::get<int>(v); // no-waring | |
| char c = std::get<char>(v); // no-warning | |
| (void)a; | |
| (void)c; | |
| } | |
| //----------------------------------------------------------------------------// | |
| // typedef | |
| //----------------------------------------------------------------------------// | |
| void typefdefedVariant() { | |
| var_t v = 25; | |
| int a = std::get<int>(v); | |
| char c = std::get<char>(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<int>(v); | |
| char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void typedefedGet() { | |
| std::variant<char, int> v = 25; | |
| int a = std::get<int_t>(v); | |
| char c = std::get<char_t>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void typedefedPack() { | |
| std::variant<int_t, char_t> v = 25; | |
| int a = std::get<int>(v); | |
| char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void fromVariable() { | |
| char o = 'c'; | |
| std::variant<int, char> v(o); | |
| char c = std::get<char>(v); | |
| int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void unknowValueButKnownType() { | |
| char o = getUnknownChar(); | |
| std::variant<int, char> v(o); | |
| char c = std::get<char>(v); | |
| int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void createPointer() { | |
| std::variant<int, char> *v = new std::variant<int, char>(15); | |
| int a = std::get<int>(*v); | |
| char c = std::get<char>(*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<int, char> v = 'c'; | |
| cannotChangePtr(v); | |
| char c = std::get<char>(v); | |
| int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void contNonInlinePtr() { | |
| std::variant<int, char> v = 'c'; | |
| cannotChangePtr(&v); | |
| char c = std::get<char>(v); | |
| int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void copyInAFunction() { | |
| std::variant<int, char> v = 'c'; | |
| cantDo(v); | |
| char c = std::get<char>(v); | |
| int a = std::get<int>(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<int, char> v = 15; | |
| changeVariantPtr(&v); | |
| char c = std::get<char> (v); | |
| int a = std::get<int> (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<int> (v2); | |
| (void)a; | |
| char c = std::get<char> (v1); | |
| a = std::get<int> (v1); // expected-warning {{std::variant 'v1' held a 'char', not an 'int'}} | |
| (void)a; | |
| (void)c; | |
| } | |
| void inlineFunctionCall() { | |
| std::variant<int, char> v = 'c'; | |
| changeVariantType(v); | |
| int a = std::get<int> (v); | |
| char c = std::get<char> (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<int, char> v = 'c'; | |
| changesToInt(v); | |
| int a = std::get<int> (v); // no-waring | |
| char c = std::get<char> (v); // no-warning | |
| (void)a; | |
| (void)c; | |
| } | |
| void nonInlineFunctionCallPtr() { | |
| std::variant<int, char> v = 'c'; | |
| changesToInt(&v); | |
| int a = std::get<int> (v); // no-warning | |
| char c = std::get<char> (v); // no-warning | |
| (void)a; | |
| (void)c; | |
| } | |
| // ----------------------------------------------------------------------------// | |
| // Misc | |
| // ----------------------------------------------------------------------------// | |
| void unknownVal() { | |
| // force the argument to be UnknownVal | |
| (void)std::get<int>(*(std::variant<int, float>*)(int)3.14f); // no crash | |
| } | |
| template <typename T> | |
| using MyVariant = std::variant<int, float>; | |
| void typeAlias() { | |
| MyVariant<bool> v; | |
| (void)std::get<int>(v); // no-warning | |
| } | |
| template <template<typename> typename Container> | |
| using MySpecialVariant = std::variant<int, float>; | |
| void complexTypeAlias() { | |
| MySpecialVariant<std::vector> v; | |
| (void)std::get<int>(v); // no crash | |
| } |