blob: 645f1ab2e5343dd8b22e01410d765ac795c15716 [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
Peter Szecsi65f8f832017-10-28 23:09:37 +0000335 // Test if resets the state of subregions as well.
336 a.b.foo(); // no-warning
Artem Dergachev356151f2017-03-24 09:52:30 +0000337 }
338 {
339 A a;
340 A b = std::move(a);
341 a.destroy(); // no-warning
342 a.foo(); // no-warning
343 }
344 {
345 A a;
346 A b = std::move(a);
347 a.clear(); // no-warning
348 a.foo(); // no-warning
Peter Szecsi65f8f832017-10-28 23:09:37 +0000349 a.b.foo(); // no-warning
Artem Dergachev356151f2017-03-24 09:52:30 +0000350 }
351}
352
353// Moves or uses that occur as part of template arguments.
354template <int>
355class ClassTemplate {
356public:
357 void foo(A a);
358};
359
360template <int>
361void functionTemplate(A a);
362
363void templateArgIsNotUseTest() {
364 {
365 // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in
366 // Google Test.
367 A a;
368 ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a)); // no-warning
369 }
370 {
371 A a;
372 functionTemplate<sizeof(A(std::move(a)))>(std::move(a)); // no-warning
373 }
374}
375
376// Moves of global variables are not reported.
377A global_a;
378void globalVariablesTest() {
379 std::move(global_a);
380 global_a.foo(); // no-warning
381}
382
383// Moves of member variables.
384class memberVariablesTest {
385 A a;
386 static A static_a;
387
388 void f() {
389 A b;
390 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
391 a.foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object 'a'}}
392
393 b = std::move(static_a); // expected-note {{'static_a' became 'moved-from' here}}
394 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'}}
395 }
396};
397
398void PtrAndArrayTest() {
399 A *Ptr = new A(1, 1.5);
400 A Arr[10];
401 Arr[2] = std::move(*Ptr); // expected-note {{Became 'moved-from' here}}
402 (*Ptr).foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object}}
403
404 Ptr = &Arr[1];
405 Arr[3] = std::move(Arr[1]); // expected-note {{Became 'moved-from' here}}
406 Ptr->foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object}}
407
408 Arr[3] = std::move(Arr[2]); // expected-note {{Became 'moved-from' here}}
409 Arr[2].foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object}}
410
411 Arr[2] = std::move(Arr[3]); // reinitialization
412 Arr[2].foo(); // no-warning
413}
414
415void exclusiveConditionsTest(bool cond) {
416 A a;
417 if (cond) {
418 A b;
419 b = std::move(a);
420 }
421 if (!cond) {
422 a.bar(); // no-warning
423 }
424}
425
426void differentBranchesTest(int i) {
427 // Don't warn if the use is in a different branch from the move.
428 {
429 A a;
430 if (i > 0) { // expected-note {{Assuming 'i' is > 0}} expected-note {{Taking true branch}}
431 A b;
432 b = std::move(a);
433 } else {
434 a.foo(); // no-warning
435 }
436 }
437 // Same thing, but with a ternary operator.
438 {
439 A a, b;
440 i > 0 ? (void)(b = std::move(a)) : a.bar(); // no-warning // expected-note {{'?' condition is true}}
441 }
442 // A variation on the theme above.
443 {
444 A a;
445 a.foo() > 0 ? a.foo() : A(std::move(a)).foo(); // expected-note {{Assuming the condition is false}} expected-note {{'?' condition is false}}
446 }
447 // Same thing, but with a switch statement.
448 {
449 A a, b;
Peter Szecsi65f8f832017-10-28 23:09:37 +0000450 switch (i) { // expected-note {{Control jumps to 'case 1:' at line 451}}
Artem Dergachev356151f2017-03-24 09:52:30 +0000451 case 1:
452 b = std::move(a); // no-warning
453 break; // expected-note {{Execution jumps to the end of the function}}
454 case 2:
455 a.foo(); // no-warning
456 break;
457 }
458 }
459 // However, if there's a fallthrough, we do warn.
460 {
461 A a, b;
Peter Szecsi65f8f832017-10-28 23:09:37 +0000462 switch (i) { // expected-note {{Control jumps to 'case 1:' at line 463}}
Artem Dergachev356151f2017-03-24 09:52:30 +0000463 case 1:
464 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
465 case 2:
466 a.foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object 'a'}}
467 break;
468 }
469 }
470}
471
472void tempTest() {
473 A a = A::get();
474 A::get().foo(); // no-warning
475 for (int i = 0; i < bignum(); i++) {
476 A::get().foo(); // no-warning
477 }
478}
479
480void interFunTest1(A &a) {
481 a.bar(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
482}
483
484void interFunTest2() {
485 A a;
486 A b;
487 b = std::move(a); // expected-note {{'a' became 'moved-from' here}}
488 interFunTest1(a); // expected-note {{Calling 'interFunTest1'}}
489}
490
491void foobar(A a, int i);
492void foobar(int i, A a);
493
494void paramEvaluateOrderTest() {
495 A a;
496 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'}}
497 // expected-note@-1 {{'a' became 'moved-from' here}}
498
499 //FALSE NEGATIVE since parameters evaluate order is undefined
500 foobar(a.getI(), std::move(a)); //no-warning
501}
502
503void not_known(A &a);
504void not_known(A *a);
505
506void regionAndPointerEscapeTest() {
507 {
508 A a;
509 A b;
510 b = std::move(a);
511 not_known(a);
512 a.foo(); //no-warning
513 }
514 {
515 A a;
516 A b;
517 b = std::move(a);
518 not_known(&a);
519 a.foo(); // no-warning
520 }
521}
522
523// A declaration statement containing multiple declarations sequences the
524// initializer expressions.
525void declarationSequenceTest() {
526 {
527 A a;
528 A a1 = a, a2 = std::move(a); // no-warning
529 }
530 {
531 A a;
532 A a1 = std::move(a), a2 = a; // expected-warning {{Copying a 'moved-from' object 'a'}} expected-note {{Copying a 'moved-from' object 'a'}}
533 // expected-note@-1 {{'a' became 'moved-from' here}}
534 }
535}
536
537// The logical operators && and || sequence their operands.
538void logicalOperatorsSequenceTest() {
539 {
540 A a;
541 if (a.foo() > 0 && A(std::move(a)).foo() > 0) { // expected-note {{Assuming the condition is false}} expected-note {{Assuming the condition is false}}
542 // expected-note@-1 {{Left side of '&&' is false}} expected-note@-1 {{Left side of '&&' is false}}
543 //expected-note@-2 {{Taking false branch}} expected-note@-2 {{Taking false branch}}
544 A().bar();
545 }
546 }
547 // A variation: Negate the result of the && (which pushes the && further down
548 // into the AST).
549 {
550 A a;
551 if (!(a.foo() > 0 && A(std::move(a)).foo() > 0)) { // expected-note {{Assuming the condition is false}} expected-note {{Assuming the condition is false}}
552 // expected-note@-1 {{Left side of '&&' is false}} expected-note@-1 {{Left side of '&&' is false}}
553 // expected-note@-2 {{Taking true branch}} expected-note@-2 {{Taking true branch}}
554 A().bar();
555 }
556 }
557 {
558 A a;
559 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'}}
560 // expected-note@-1 {{'a' became 'moved-from' here}} expected-note@-1 {{Assuming the condition is true}} expected-note@-1 {{Assuming the condition is false}}
561 // expected-note@-2 {{Left side of '&&' is false}} expected-note@-2 {{Left side of '&&' is true}}
562 // expected-note@-3 {{Taking false branch}}
563 A().bar();
564 }
565 }
566 {
567 A a;
568 if (a.foo() > 0 || A(std::move(a)).foo() > 0) { // expected-note {{Assuming the condition is true}}
569 //expected-note@-1 {{Left side of '||' is true}}
570 //expected-note@-2 {{Taking true branch}}
571 A().bar();
572 }
573 }
574 {
575 A a;
576 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'}}
577 // expected-note@-1 {{'a' became 'moved-from' here}} expected-note@-1 {{Assuming the condition is false}} expected-note@-1 {{Left side of '||' is false}}
578 A().bar();
579 }
580 }
581}
582
583// A range-based for sequences the loop variable declaration before the body.
584void forRangeSequencesTest() {
585 A v[2] = {A(), A()};
586 for (A &a : v) {
587 A b;
588 b = std::move(a); // no-warning
589 }
590}
591
592// If a variable is declared in an if statement, the declaration of the variable
593// (which is treated like a reinitialization by the check) is sequenced before
594// the evaluation of the condition (which constitutes a use).
595void ifStmtSequencesDeclAndConditionTest() {
596 for (int i = 0; i < 3; ++i) {
597 if (A a = A()) {
598 A b;
599 b = std::move(a); // no-warning
600 }
601 }
602}
603
Peter Szecsi65f8f832017-10-28 23:09:37 +0000604class C : public A {};
Artem Dergachev356151f2017-03-24 09:52:30 +0000605void subRegionMoveTest() {
606 {
607 A a;
608 B b = std::move(a.b); // expected-note {{'b' became 'moved-from' here}}
609 a.b.foo(); // expected-warning {{Method call on a 'moved-from' object 'b'}} expected-note {{Method call on a 'moved-from' object 'b'}}
610 }
611 {
612 A a;
613 A a1 = std::move(a); // expected-note {{Calling move constructor for 'A'}} expected-note {{Returning from move constructor for 'A'}}
614 a.b.foo(); // expected-warning {{Method call on a 'moved-from' object 'b'}} expected-note {{Method call on a 'moved-from' object 'b'}}
615 }
616 // Don't report a misuse if any SuperRegion is already reported.
617 {
618 A a;
619 A a1 = std::move(a); // expected-note {{'a' became 'moved-from' here}}
620 a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}}
621 a.b.foo(); // no-warning
622 }
Peter Szecsi65f8f832017-10-28 23:09:37 +0000623 {
624 C c;
625 C c1 = std::move(c); // expected-note {{'c' became 'moved-from' here}}
626 c.foo(); // expected-warning {{Method call on a 'moved-from' object 'c'}} expected-note {{Method call on a 'moved-from' object 'c'}}
627 c.b.foo(); // no-warning
628 }
Artem Dergachev356151f2017-03-24 09:52:30 +0000629}
Artem Dergachev0f22a062017-10-10 11:55:56 +0000630
Artem Dergachev0f22a062017-10-10 11:55:56 +0000631void resetSuperClass() {
632 C c;
633 C c1 = std::move(c);
634 c.clear();
635 C c2 = c; // no-warning
636}
Peter Szecsi65f8f832017-10-28 23:09:37 +0000637
638void reportSuperClass() {
639 C c;
640 C c1 = std::move(c); // expected-note {{'c' became 'moved-from' here}}
641 c.foo(); // expected-warning {{Method call on a 'moved-from' object 'c'}} expected-note {{Method call on a 'moved-from' object 'c'}}
642 C c2 = c; // no-warning
643}