| // 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; |
| } |