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