blob: 44e055fd407b3bb213ed4854450311ff27ee6021 [file] [log] [blame]
Artem Dergachev356151f2017-03-24 09:52:30 +00001// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.cplusplus.MisusedMovedObject -std=c++11 -verify -analyzer-output=text %s
2
3namespace std {
4
5template <typename>
6struct remove_reference;
7
8template <typename _Tp>
9struct remove_reference { typedef _Tp type; };
10
11template <typename _Tp>
12struct remove_reference<_Tp &> { typedef _Tp type; };
13
14template <typename _Tp>
15struct remove_reference<_Tp &&> { typedef _Tp type; };
16
17template <typename _Tp>
18typename remove_reference<_Tp>::type &&move(_Tp &&__t) {
19 return static_cast<typename remove_reference<_Tp>::type &&>(__t);
20}
21
22template <typename _Tp>
23_Tp &&forward(typename remove_reference<_Tp>::type &__t) noexcept {
24 return static_cast<_Tp &&>(__t);
25}
26
27template <class T>
28void swap(T &a, T &b) {
29 T c(std::move(a));
30 a = std::move(b);
31 b = std::move(c);
32}
33
34} // namespace std
35
36class B {
37public:
38 B() = default;
39 B(const B &) = default;
40 B(B &&) = default;
41 void operator=(B &&b) {
42 return;
43 }
44 void foo() { return; }
45};
46
47class A {
48 int i;
49 double d;
50
51public:
52 B b;
53 A(int ii = 42, double dd = 1.0) : d(dd), i(ii), b(B()) {}
54 void moveconstruct(A &&other) {
55 std::swap(b, other.b);
56 std::swap(d, other.d);
57 std::swap(i, other.i);
58 return;
59 }
60 static A get() {
61 A v(12, 13);
62 return v;
63 }
64 A(A *a) {
65 moveconstruct(std::move(*a));
66 }
67 A(const A &other) : i(other.i), d(other.d), b(other.b) {}
68 A(A &&other) : i(other.i), d(other.d), b(std::move(other.b)) { // expected-note {{'b' became 'moved-from' here}}
69 }
70 A(A &&other, char *k) {
71 moveconstruct(std::move(other));
72 }
73 void operator=(A &&other) {
74 moveconstruct(std::move(other));
75 return;
76 }
77 int getI() { return i; }
78 int foo() const;
79 void bar() const;
80 void reset();
81 void destroy();
82 void clear();
83 bool empty() const;
84 bool isEmpty() const;
85 operator bool() const;
86};
87
88int bignum();
89
90void moveInsideFunctionCall(A a) {
91 A b = std::move(a);
92}
93void leftRefCall(A &a) {
94 a.foo();
95}
96void rightRefCall(A &&a) {
97 a.foo();
98}
99void constCopyOrMoveCall(const A a) {
100 a.foo();
101}
102
103void copyOrMoveCall(A a) {
104 a.foo();
105}
106
107void simpleMoveCtorTest() {
108 A a;
109 A b;
110 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
111 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
112}
113
114void simpleMoveAssignementTest() {
115 A a;
116 A b;
117 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
118 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
119}
120
121void moveInInitListTest() {
122 struct S {
123 A a;
124 };
125 A a;
126 S s{std::move(a)}; // expected-note {{'a' became 'moved-from' here}}
127 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
128}
129
130// Don't report a bug if the variable was assigned to in the meantime.
131void reinitializationTest(int i) {
132 {
133 A a;
134 A b;
135 b = std::move(a);
136 a = A();
137 a.foo();
138 }
139 {
140 A a;
141 if (i == 1) { // expected-note {{Assuming 'i' is not equal to 1}} expected-note {{Taking false branch}}
142 // expected-note@-1 {{Assuming 'i' is not equal to 1}} expected-note@-1 {{Taking false branch}}
143 A b;
144 b = std::move(a);
145 a = A();
146 }
147 if (i == 2) { // expected-note {{Assuming 'i' is not equal to 2}} expected-note {{Taking false branch}}
148 //expected-note@-1 {{Assuming 'i' is not equal to 2}} expected-note@-1 {{Taking false branch}}
149 a.foo(); // no-warning
150 }
151 }
152 {
153 A a;
154 if (i == 1) { // expected-note {{Taking false branch}} expected-note {{Taking false branch}}
155 std::move(a);
156 }
157 if (i == 2) { // expected-note {{Taking false branch}} expected-note {{Taking false branch}}
158 a = A();
159 a.foo();
160 }
161 }
162 // The built-in assignment operator should also be recognized as a
163 // reinitialization. (std::move() may be called on built-in types in template
164 // code.)
165 {
166 int a1 = 1, a2 = 2;
167 std::swap(a1, a2);
168 }
169 // A std::move() after the assignment makes the variable invalid again.
170 {
171 A a;
172 A b;
173 b = std::move(a);
174 a = A();
175 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
176 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
177 }
178 // If a path exist where we not reinitialize the variable we report a bug.
179 {
180 A a;
181 A b;
182 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
183 if (i < 10) { // expected-note {{Assuming 'i' is >= 10}} expected-note {{Taking false branch}}
184 a = A();
185 }
186 if (i > 5) { // expected-note {{Taking true branch}}
187 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
188 }
189 }
190}
191
192// Using decltype on an expression is not a use.
193void decltypeIsNotUseTest() {
194 A a;
195 // A b(std::move(a));
196 decltype(a) other_a; // no-warning
197}
198
199void loopTest() {
200 {
201 A a;
202 for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
203 rightRefCall(std::move(a)); // no-warning
204 }
205 }
206 {
207 A a;
208 for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true. Entering loop body}}
209 //expected-note@-1 {{Loop condition is true. Entering loop body}}
210 //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
211 rightRefCall(std::move(a)); // no-warning
212 }
213 }
214 {
215 A a;
216 for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
217 leftRefCall(a); // no-warning
218 }
219 }
220 {
221 A a;
222 for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true. Entering loop body}}
223 //expected-note@-1 {{Loop condition is true. Entering loop body}}
224 //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
225 leftRefCall(a); // no-warning
226 }
227 }
228 {
229 A a;
230 for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
231 constCopyOrMoveCall(a); // no-warning
232 }
233 }
234 {
235 A a;
236 for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true. Entering loop body}}
237 //expected-note@-1 {{Loop condition is true. Entering loop body}}
238 //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
239 constCopyOrMoveCall(a); // no-warning
240 }
241 }
242 {
243 A a;
244 for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
245 moveInsideFunctionCall(a); // no-warning
246 }
247 }
248 {
249 A a;
250 for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true. Entering loop body}}
251 //expected-note@-1 {{Loop condition is true. Entering loop body}}
252 //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
253 moveInsideFunctionCall(a); // no-warning
254 }
255 }
256 {
257 A a;
258 for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
259 copyOrMoveCall(a); // no-warning
260 }
261 }
262 {
263 A a;
264 for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true.}}
265 //expected-note@-1 {{Loop condition is true. Entering loop body}}
266 //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
267 copyOrMoveCall(a); // no-warning
268 }
269 }
270 {
271 A a;
272 for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is true. Entering loop body}} expected-note {{Loop condition is true. Entering loop body}}
273 constCopyOrMoveCall(std::move(a)); // expected-warning {{Copying a 'moved-from' object 'a'}} expected-note {{Copying a 'moved-from' object 'a'}}
274 // expected-note@-1 {{'a' became 'moved-from' here}}
275 }
276 }
277
278 // Don't warn if we return after the move.
279 {
280 A a;
281 for (int i = 0; i < 3; ++i) {
282 a.bar();
283 if (a.foo() > 0) {
284 A b;
285 b = std::move(a); // no-warning
286 return;
287 }
288 }
289 }
290}
291
292//report a usage of a moved-from object only at the first use
293void uniqueTest(bool cond) {
294 A a(42, 42.0);
295 A b;
296 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
297
298 if (cond) { // expected-note {{Assuming 'cond' is not equal to 0}} expected-note {{Taking true branch}}
299 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
300 }
301 if (cond) {
302 a.bar(); // no-warning
303 }
304
305 a.bar(); // no-warning
306}
307
308void uniqueTest2() {
309 A a;
310 A a1 = std::move(a); // expected-note {{'a' became 'moved-from' here}}
311 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
312
313 A a2 = std::move(a); // no-warning
314 a.foo(); // no-warning
315}
316
317// There are exceptions where we assume in general that the method works fine
318//even on moved-from objects.
319void moveSafeFunctionsTest() {
320 A a;
321 A b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
322 a.empty(); // no-warning
323 a.isEmpty(); // no-warning
324 (void)a; // no-warning
325 (bool)a; // expected-warning {{expression result unused}}
326 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
327}
328
329void moveStateResetFunctionsTest() {
330 {
331 A a;
332 A b = std::move(a);
333 a.reset(); // no-warning
334 a.foo(); // no-warning
335 }
336 {
337 A a;
338 A b = std::move(a);
339 a.destroy(); // no-warning
340 a.foo(); // no-warning
341 }
342 {
343 A a;
344 A b = std::move(a);
345 a.clear(); // no-warning
346 a.foo(); // no-warning
347 }
348}
349
350// Moves or uses that occur as part of template arguments.
351template <int>
352class ClassTemplate {
353public:
354 void foo(A a);
355};
356
357template <int>
358void functionTemplate(A a);
359
360void templateArgIsNotUseTest() {
361 {
362 // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in
363 // Google Test.
364 A a;
365 ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a)); // no-warning
366 }
367 {
368 A a;
369 functionTemplate<sizeof(A(std::move(a)))>(std::move(a)); // no-warning
370 }
371}
372
373// Moves of global variables are not reported.
374A global_a;
375void globalVariablesTest() {
376 std::move(global_a);
377 global_a.foo(); // no-warning
378}
379
380// Moves of member variables.
381class memberVariablesTest {
382 A a;
383 static A static_a;
384
385 void f() {
386 A b;
387 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
388 a.foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object 'a'}}
389
390 b = std::move(static_a); // expected-note {{'static_a' became 'moved-from' here}}
391 static_a.foo(); // expected-warning {{Method call on a 'moved-from' object 'static_a'}} expected-note {{Method call on a 'moved-from' object 'static_a'}}
392 }
393};
394
395void PtrAndArrayTest() {
396 A *Ptr = new A(1, 1.5);
397 A Arr[10];
398 Arr[2] = std::move(*Ptr); // expected-note {{Became 'moved-from' here}}
399 (*Ptr).foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object}}
400
401 Ptr = &Arr[1];
402 Arr[3] = std::move(Arr[1]); // expected-note {{Became 'moved-from' here}}
403 Ptr->foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object}}
404
405 Arr[3] = std::move(Arr[2]); // expected-note {{Became 'moved-from' here}}
406 Arr[2].foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object}}
407
408 Arr[2] = std::move(Arr[3]); // reinitialization
409 Arr[2].foo(); // no-warning
410}
411
412void exclusiveConditionsTest(bool cond) {
413 A a;
414 if (cond) {
415 A b;
416 b = std::move(a);
417 }
418 if (!cond) {
419 a.bar(); // no-warning
420 }
421}
422
423void differentBranchesTest(int i) {
424 // Don't warn if the use is in a different branch from the move.
425 {
426 A a;
427 if (i > 0) { // expected-note {{Assuming 'i' is > 0}} expected-note {{Taking true branch}}
428 A b;
429 b = std::move(a);
430 } else {
431 a.foo(); // no-warning
432 }
433 }
434 // Same thing, but with a ternary operator.
435 {
436 A a, b;
437 i > 0 ? (void)(b = std::move(a)) : a.bar(); // no-warning // expected-note {{'?' condition is true}}
438 }
439 // A variation on the theme above.
440 {
441 A a;
442 a.foo() > 0 ? a.foo() : A(std::move(a)).foo(); // expected-note {{Assuming the condition is false}} expected-note {{'?' condition is false}}
443 }
444 // Same thing, but with a switch statement.
445 {
446 A a, b;
447 switch (i) { // expected-note {{Control jumps to 'case 1:' at line 448}}
448 case 1:
449 b = std::move(a); // no-warning
450 break; // expected-note {{Execution jumps to the end of the function}}
451 case 2:
452 a.foo(); // no-warning
453 break;
454 }
455 }
456 // However, if there's a fallthrough, we do warn.
457 {
458 A a, b;
459 switch (i) { // expected-note {{Control jumps to 'case 1:' at line 460}}
460 case 1:
461 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
462 case 2:
463 a.foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object 'a'}}
464 break;
465 }
466 }
467}
468
469void tempTest() {
470 A a = A::get();
471 A::get().foo(); // no-warning
472 for (int i = 0; i < bignum(); i++) {
473 A::get().foo(); // no-warning
474 }
475}
476
477void interFunTest1(A &a) {
478 a.bar(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
479}
480
481void interFunTest2() {
482 A a;
483 A b;
484 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
485 interFunTest1(a); // expected-note {{Calling 'interFunTest1'}}
486}
487
488void foobar(A a, int i);
489void foobar(int i, A a);
490
491void paramEvaluateOrderTest() {
492 A a;
493 foobar(std::move(a), a.getI()); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
494 // expected-note@-1 {{'a' became 'moved-from' here}}
495
496 //FALSE NEGATIVE since parameters evaluate order is undefined
497 foobar(a.getI(), std::move(a)); //no-warning
498}
499
500void not_known(A &a);
501void not_known(A *a);
502
503void regionAndPointerEscapeTest() {
504 {
505 A a;
506 A b;
507 b = std::move(a);
508 not_known(a);
509 a.foo(); //no-warning
510 }
511 {
512 A a;
513 A b;
514 b = std::move(a);
515 not_known(&a);
516 a.foo(); // no-warning
517 }
518}
519
520// A declaration statement containing multiple declarations sequences the
521// initializer expressions.
522void declarationSequenceTest() {
523 {
524 A a;
525 A a1 = a, a2 = std::move(a); // no-warning
526 }
527 {
528 A a;
529 A a1 = std::move(a), a2 = a; // expected-warning {{Copying a 'moved-from' object 'a'}} expected-note {{Copying a 'moved-from' object 'a'}}
530 // expected-note@-1 {{'a' became 'moved-from' here}}
531 }
532}
533
534// The logical operators && and || sequence their operands.
535void logicalOperatorsSequenceTest() {
536 {
537 A a;
538 if (a.foo() > 0 && A(std::move(a)).foo() > 0) { // expected-note {{Assuming the condition is false}} expected-note {{Assuming the condition is false}}
539 // expected-note@-1 {{Left side of '&&' is false}} expected-note@-1 {{Left side of '&&' is false}}
540 //expected-note@-2 {{Taking false branch}} expected-note@-2 {{Taking false branch}}
541 A().bar();
542 }
543 }
544 // A variation: Negate the result of the && (which pushes the && further down
545 // into the AST).
546 {
547 A a;
548 if (!(a.foo() > 0 && A(std::move(a)).foo() > 0)) { // expected-note {{Assuming the condition is false}} expected-note {{Assuming the condition is false}}
549 // expected-note@-1 {{Left side of '&&' is false}} expected-note@-1 {{Left side of '&&' is false}}
550 // expected-note@-2 {{Taking true branch}} expected-note@-2 {{Taking true branch}}
551 A().bar();
552 }
553 }
554 {
555 A a;
556 if (A(std::move(a)).foo() > 0 && a.foo() > 0) { // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
557 // expected-note@-1 {{'a' became 'moved-from' here}} expected-note@-1 {{Assuming the condition is true}} expected-note@-1 {{Assuming the condition is false}}
558 // expected-note@-2 {{Left side of '&&' is false}} expected-note@-2 {{Left side of '&&' is true}}
559 // expected-note@-3 {{Taking false branch}}
560 A().bar();
561 }
562 }
563 {
564 A a;
565 if (a.foo() > 0 || A(std::move(a)).foo() > 0) { // expected-note {{Assuming the condition is true}}
566 //expected-note@-1 {{Left side of '||' is true}}
567 //expected-note@-2 {{Taking true branch}}
568 A().bar();
569 }
570 }
571 {
572 A a;
573 if (A(std::move(a)).foo() > 0 || a.foo() > 0) { // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
574 // expected-note@-1 {{'a' became 'moved-from' here}} expected-note@-1 {{Assuming the condition is false}} expected-note@-1 {{Left side of '||' is false}}
575 A().bar();
576 }
577 }
578}
579
580// A range-based for sequences the loop variable declaration before the body.
581void forRangeSequencesTest() {
582 A v[2] = {A(), A()};
583 for (A &a : v) {
584 A b;
585 b = std::move(a); // no-warning
586 }
587}
588
589// If a variable is declared in an if statement, the declaration of the variable
590// (which is treated like a reinitialization by the check) is sequenced before
591// the evaluation of the condition (which constitutes a use).
592void ifStmtSequencesDeclAndConditionTest() {
593 for (int i = 0; i < 3; ++i) {
594 if (A a = A()) {
595 A b;
596 b = std::move(a); // no-warning
597 }
598 }
599}
600
601void subRegionMoveTest() {
602 {
603 A a;
604 B b = std::move(a.b); // expected-note {{'b' became 'moved-from' here}}
605 a.b.foo(); // expected-warning {{Method call on a 'moved-from' object 'b'}} expected-note {{Method call on a 'moved-from' object 'b'}}
606 }
607 {
608 A a;
609 A a1 = std::move(a); // expected-note {{Calling move constructor for 'A'}} expected-note {{Returning from move constructor for 'A'}}
610 a.b.foo(); // expected-warning {{Method call on a 'moved-from' object 'b'}} expected-note {{Method call on a 'moved-from' object 'b'}}
611 }
612 // Don't report a misuse if any SuperRegion is already reported.
613 {
614 A a;
615 A a1 = std::move(a); // expected-note {{'a' became 'moved-from' here}}
616 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
617 a.b.foo(); // no-warning
618 }
619}