358 lines
10 KiB
C++
358 lines
10 KiB
C++
|
// 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;
|
||
|
}
|