xref: /llvm-project/clang/test/Analysis/std-variant-checker.cpp (revision 527fcb8e5d6b1d491b6699cde818db1127bbb12c)
1 // RUN: %clang %s -std=c++17 -Xclang -verify --analyze \
2 // RUN:   -Xclang -analyzer-checker=core \
3 // RUN:   -Xclang -analyzer-checker=debug.ExprInspection \
4 // RUN:   -Xclang -analyzer-checker=core,alpha.core.StdVariant
5 
6 #include "Inputs/system-header-simulator-cxx.h"
7 
8 class Foo{};
9 
10 void clang_analyzer_warnIfReached();
11 void clang_analyzer_eval(int);
12 
13 //helper functions
changeVariantType(std::variant<int,char> & v)14 void changeVariantType(std::variant<int, char> &v) {
15   v = 25;
16 }
17 
18 void changesToInt(std::variant<int, char> &v);
19 void changesToInt(std::variant<int, char> *v);
20 
21 void cannotChangePtr(const std::variant<int, char> &v);
22 void cannotChangePtr(const std::variant<int, char> *v);
23 
24 char getUnknownChar();
25 
swap(std::variant<int,char> & v1,std::variant<int,char> & v2)26 void swap(std::variant<int, char> &v1, std::variant<int, char> &v2) {
27   std::variant<int, char> tmp = v1;
28   v1 = v2;
29   v2 = tmp;
30 }
31 
cantDo(const std::variant<int,char> & v)32 void cantDo(const std::variant<int, char>& v) {
33   std::variant<int, char> vtmp = v;
34   vtmp = 5;
35   int a = std::get<int> (vtmp);
36   (void) a;
37 }
38 
changeVariantPtr(std::variant<int,char> * v)39 void changeVariantPtr(std::variant<int, char> *v) {
40   *v = 'c';
41 }
42 
43 using var_t = std::variant<int, char>;
44 using var_tt = var_t;
45 using int_t = int;
46 using char_t = char;
47 
48 // A quick sanity check to see that std::variant's std::get
49 // is not being confused with std::pairs std::get.
wontConfuseStdGets()50 void wontConfuseStdGets() {
51   std::pair<int, char> p{15, '1'};
52   int a = std::get<int>(p);
53   char c = std::get<char>(p);
54   (void)a;
55   (void)c;
56 }
57 
58 //----------------------------------------------------------------------------//
59 // std::get
60 //----------------------------------------------------------------------------//
stdGetType()61 void stdGetType() {
62   std::variant<int, char> v = 25;
63   int a = std::get<int>(v);
64   char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}}
65   (void)a;
66   (void)c;
67 }
68 
stdGetPointer()69 void stdGetPointer() {
70   int *p = new int;
71   std::variant<int*, char> v = p;
72   int *a = std::get<int*>(v);
73   char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int *', not a 'char'}}
74   (void)a;
75   (void)c;
76   delete p;
77 }
78 
stdGetObject()79 void stdGetObject() {
80   std::variant<int, char, Foo> v = Foo{};
81   Foo f = std::get<Foo>(v);
82   int i = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'Foo', not an 'int'}}
83   (void)i;
84 }
85 
stdGetPointerAndPointee()86 void stdGetPointerAndPointee() {
87   int a = 5;
88   std::variant<int, int*> v = &a;
89   int *b = std::get<int*>(v);
90   int c = std::get<int>(v); // expected-warning {{std::variant 'v' held an 'int *', not an 'int'}}
91   (void)c;
92   (void)b;
93 }
94 
variantHoldingVariant()95 void variantHoldingVariant() {
96   std::variant<std::variant<int, char>, std::variant<char, int>> v = std::variant<int,char>(25);
97   std::variant<int, char> v1 = std::get<std::variant<int,char>>(v);
98   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>'}}
99 }
100 
101 //----------------------------------------------------------------------------//
102 // Constructors and assignments
103 //----------------------------------------------------------------------------//
copyConstructor()104 void copyConstructor() {
105   std::variant<int, char> v = 25;
106   std::variant<int, char> t(v);
107   int a = std::get<int> (t);
108   char c = std::get<char> (t); // expected-warning {{std::variant 't' held an 'int', not a 'char'}}
109   (void)a;
110   (void)c;
111 }
112 
copyAssignmentOperator()113 void copyAssignmentOperator() {
114   std::variant<int, char> v = 25;
115   std::variant<int, char> t = 'c';
116   t = v;
117   int a = std::get<int> (t);
118   char c = std::get<char> (t); // expected-warning {{std::variant 't' held an 'int', not a 'char'}}
119   (void)a;
120   (void)c;
121 }
122 
assignmentOperator()123 void assignmentOperator() {
124   std::variant<int, char> v = 25;
125   int a = std::get<int> (v);
126   (void)a;
127   v = 'c';
128   char c = std::get<char>(v);
129   a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}}
130   (void)a;
131   (void)c;
132 }
133 
typeChangeThreeTimes()134 void typeChangeThreeTimes() {
135   std::variant<int, char, float> v = 25;
136   int a = std::get<int> (v);
137   (void)a;
138   v = 'c';
139   char c = std::get<char>(v);
140   v = 25;
141   a = std::get<int>(v);
142   (void)a;
143   v = 1.25f;
144   float f = std::get<float>(v);
145   a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'float', not an 'int'}}
146   (void)a;
147   (void)c;
148   (void)f;
149 }
150 
defaultConstructor()151 void defaultConstructor() {
152   std::variant<int, char> v;
153   int i = std::get<int>(v);
154   char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}}
155   (void)i;
156   (void)c;
157 }
158 
159 // Verify that we handle temporary objects correctly
temporaryObjectsConstructor()160 void temporaryObjectsConstructor() {
161   std::variant<int, char> v(std::variant<int, char>('c'));
162   char c = std::get<char>(v);
163   int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}}
164   (void)a;
165   (void)c;
166 }
167 
temporaryObjectsAssignment()168 void temporaryObjectsAssignment() {
169   std::variant<int, char> v = std::variant<int, char>('c');
170   char c = std::get<char>(v);
171   int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}}
172   (void)a;
173   (void)c;
174 }
175 
176 // Verify that we handle pointer types correctly
pointerTypeHeld()177 void pointerTypeHeld() {
178   int *p = new int;
179   std::variant<int*, char> v = p;
180   int *a = std::get<int*>(v);
181   char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int *', not a 'char'}}
182   (void)a;
183   (void)c;
184   delete p;
185 }
186 
187 std::variant<int, char> get_unknown_variant();
188 // Verify that the copy constructor is handles properly when the std::variant
189 // has no previously activated type and we copy an object of unknown value in it.
copyFromUnknownVariant()190 void copyFromUnknownVariant() {
191   std::variant<int, char> u = get_unknown_variant();
192   std::variant<int, char> v(u);
193   int a = std::get<int>(v); // no-waring
194   char c = std::get<char>(v); // no-warning
195   (void)a;
196   (void)c;
197 }
198 
199 // Verify that the copy constructor is handles properly when the std::variant
200 // has previously activated type and we copy an object of unknown value in it.
copyFromUnknownVariantBef()201 void copyFromUnknownVariantBef() {
202   std::variant<int, char> v = 25;
203   std::variant<int, char> u = get_unknown_variant();
204   v = u;
205   int a = std::get<int>(v); // no-waring
206   char c = std::get<char>(v); // no-warning
207   (void)a;
208   (void)c;
209 }
210 
211 //----------------------------------------------------------------------------//
212 // typedef
213 //----------------------------------------------------------------------------//
214 
typefdefedVariant()215 void typefdefedVariant() {
216   var_t v = 25;
217   int a = std::get<int>(v);
218   char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}}
219   (void)a;
220   (void)c;
221 }
222 
typedefedTypedfefedVariant()223 void typedefedTypedfefedVariant() {
224   var_tt v = 25;
225   int a = std::get<int>(v);
226   char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}}
227   (void)a;
228   (void)c;
229 }
230 
typedefedGet()231 void typedefedGet() {
232   std::variant<char, int> v = 25;
233   int a = std::get<int_t>(v);
234   char c = std::get<char_t>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}}
235   (void)a;
236   (void)c;
237 }
238 
typedefedPack()239 void typedefedPack() {
240   std::variant<int_t, char_t> v = 25;
241   int a = std::get<int>(v);
242   char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}}
243   (void)a;
244   (void)c;
245 }
246 
fromVariable()247 void fromVariable() {
248   char o = 'c';
249   std::variant<int, char> v(o);
250   char c = std::get<char>(v);
251   int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}}
252   (void)a;
253   (void)c;
254 }
255 
unknowValueButKnownType()256 void unknowValueButKnownType() {
257   char o = getUnknownChar();
258   std::variant<int, char> v(o);
259   char c = std::get<char>(v);
260   int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}}
261   (void)a;
262   (void)c;
263 }
264 
createPointer()265 void createPointer() {
266   std::variant<int, char> *v = new std::variant<int, char>(15);
267   int a = std::get<int>(*v);
268   char c = std::get<char>(*v); // expected-warning {{std::variant  held an 'int', not a 'char'}}
269   (void)a;
270   (void)c;
271   delete v;
272 }
273 
274 //----------------------------------------------------------------------------//
275 // Passing std::variants to functions
276 //----------------------------------------------------------------------------//
277 
278 // Verifying that we are not invalidating the memory region of a variant if
279 // a non inlined or inlined function takes it as a constant reference or pointer
constNonInlineRef()280 void constNonInlineRef() {
281   std::variant<int, char> v = 'c';
282   cannotChangePtr(v);
283   char c = std::get<char>(v);
284   int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}}
285   (void)a;
286   (void)c;
287 }
288 
contNonInlinePtr()289 void contNonInlinePtr() {
290   std::variant<int, char> v = 'c';
291   cannotChangePtr(&v);
292   char c = std::get<char>(v);
293   int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}}
294   (void)a;
295   (void)c;
296 }
297 
copyInAFunction()298 void copyInAFunction() {
299   std::variant<int, char> v = 'c';
300   cantDo(v);
301   char c = std::get<char>(v);
302   int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}}
303   (void)a;
304   (void)c;
305 
306 }
307 
308 // Verifying that we can keep track of the type stored in std::variant when
309 // it is passed to an inlined function as a reference or pointer
changeThruPointers()310 void changeThruPointers() {
311   std::variant<int, char> v = 15;
312   changeVariantPtr(&v);
313   char c = std::get<char> (v);
314   int a = std::get<int> (v); // expected-warning {{std::variant 'v' held a 'char', not an 'int'}}
315   (void)a;
316   (void)c;
317 }
318 
functionCallWithCopyAssignment()319 void functionCallWithCopyAssignment() {
320   var_t v1 = 15;
321   var_t v2 = 'c';
322   swap(v1, v2);
323   int a = std::get<int> (v2);
324   (void)a;
325   char c = std::get<char> (v1);
326   a = std::get<int> (v1); // expected-warning {{std::variant 'v1' held a 'char', not an 'int'}}
327   (void)a;
328   (void)c;
329 }
330 
inlineFunctionCall()331 void inlineFunctionCall() {
332   std::variant<int, char> v = 'c';
333   changeVariantType(v);
334   int a = std::get<int> (v);
335   char c = std::get<char> (v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}}
336   (void)a;
337   (void)c;
338 }
339 
340 // Verifying that we invalidate the mem region of std::variant when it is
341 // passed as a non const reference or a pointer to a non inlined function.
nonInlineFunctionCall()342 void nonInlineFunctionCall() {
343   std::variant<int, char> v = 'c';
344   changesToInt(v);
345   int a = std::get<int> (v); // no-waring
346   char c = std::get<char> (v); // no-warning
347   (void)a;
348   (void)c;
349 }
350 
nonInlineFunctionCallPtr()351 void nonInlineFunctionCallPtr() {
352   std::variant<int, char> v = 'c';
353   changesToInt(&v);
354   int a = std::get<int> (v); // no-warning
355   char c = std::get<char> (v); // no-warning
356   (void)a;
357   (void)c;
358 }