1 // RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\
2 // RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
3 // RUN: -analyzer-config exploration_strategy=unexplored_first_queue\
4 // RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\
5 // RUN: -verify=expected,peaceful,non-aggressive
6 // RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\
7 // RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
8 // RUN: -analyzer-config exploration_strategy=dfs -DDFS\
9 // RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\
10 // RUN: -verify=expected,peaceful,non-aggressive
11 // RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\
12 // RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
13 // RUN: -analyzer-config exploration_strategy=unexplored_first_queue\
14 // RUN: -analyzer-config cplusplus.Move:WarnOn=KnownsOnly\
15 // RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\
16 // RUN: -verify=expected,non-aggressive
17 // RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
18 // RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
19 // RUN: -analyzer-config exploration_strategy=dfs -DDFS\
20 // RUN: -analyzer-config cplusplus.Move:WarnOn=KnownsOnly\
21 // RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\
22 // RUN: -verify=expected,non-aggressive
23 // RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\
24 // RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
25 // RUN: -analyzer-config exploration_strategy=unexplored_first_queue\
26 // RUN: -analyzer-config cplusplus.Move:WarnOn=All\
27 // RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\
28 // RUN: -verify=expected,peaceful,aggressive
29 // RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\
30 // RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
31 // RUN: -analyzer-config exploration_strategy=dfs -DDFS\
32 // RUN: -analyzer-config cplusplus.Move:WarnOn=All\
33 // RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\
34 // RUN: -verify=expected,peaceful,aggressive
35
36 // RUN: not %clang_analyze_cc1 -verify %s \
37 // RUN: -analyzer-checker=core \
38 // RUN: -analyzer-checker=cplusplus.Move \
39 // RUN: -analyzer-config cplusplus.Move:WarnOn="a bunch of things" \
40 // RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-MOVE-INVALID-VALUE
41
42 // CHECK-MOVE-INVALID-VALUE: (frontend): invalid input for checker option
43 // CHECK-MOVE-INVALID-VALUE-SAME: 'cplusplus.Move:WarnOn', that expects either
44 // CHECK-MOVE-INVALID-VALUE-SAME: "KnownsOnly", "KnownsAndLocals" or "All"
45 // CHECK-MOVE-INVALID-VALUE-SAME: string value
46
47 // Tests checker-messages printing.
48 // RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\
49 // RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
50 // RUN: -analyzer-config exploration_strategy=dfs -DDFS\
51 // RUN: -analyzer-config cplusplus.Move:WarnOn=All -DAGGRESSIVE_DFS\
52 // RUN: -analyzer-checker core,cplusplus.SmartPtrModeling,debug.ExprInspection\
53 // RUN: -verify=expected,peaceful,aggressive %s 2>&1 | FileCheck %s
54
55 #include "Inputs/system-header-simulator-cxx.h"
56
57 void clang_analyzer_warnIfReached();
58 void clang_analyzer_printState();
59
60 class B {
61 public:
62 B() = default;
63 B(const B &) = default;
64 B(B &&) = default;
65 B& operator=(const B &q) = default;
operator =(B && b)66 void operator=(B &&b) {
67 return;
68 }
foo()69 void foo() { return; }
70 };
71
72 class A {
73 int i;
74 double d;
75
76 public:
77 B b;
A(int ii=42,double dd=1.0)78 A(int ii = 42, double dd = 1.0) : d(dd), i(ii), b(B()) {}
moveconstruct(A && other)79 void moveconstruct(A &&other) {
80 std::swap(b, other.b);
81 std::swap(d, other.d);
82 std::swap(i, other.i);
83 return;
84 }
get()85 static A get() {
86 A v(12, 13);
87 return v;
88 }
A(A * a)89 A(A *a) {
90 moveconstruct(std::move(*a));
91 }
A(const A & other)92 A(const A &other) : i(other.i), d(other.d), b(other.b) {}
A(A && other)93 A(A &&other) : i(other.i), d(other.d), b(std::move(other.b)) { // aggressive-note{{Object 'b' is moved}}
94 }
A(A && other,char * k)95 A(A &&other, char *k) {
96 moveconstruct(std::move(other));
97 }
operator =(const A & other)98 void operator=(const A &other) {
99 i = other.i;
100 d = other.d;
101 b = other.b;
102 return;
103 }
operator =(A && other)104 void operator=(A &&other) {
105 moveconstruct(std::move(other));
106 return;
107 }
getI()108 int getI() { return i; }
109 int foo() const;
110 void bar() const;
111 void reset();
112 void destroy();
113 void clear();
114 void resize(std::size_t);
115 void assign(const A &);
116 bool empty() const;
117 bool isEmpty() const;
118 operator bool() const;
119
testUpdateField()120 void testUpdateField() {
121 A a;
122 A b = std::move(a);
123 a.i = 1;
124 a.foo(); // no-warning
125 }
testUpdateFieldDouble()126 void testUpdateFieldDouble() {
127 A a;
128 A b = std::move(a);
129 a.d = 1.0;
130 a.foo(); // no-warning
131 }
132 };
133
134 int bignum();
135
moveInsideFunctionCall(A a)136 void moveInsideFunctionCall(A a) {
137 A b = std::move(a);
138 }
leftRefCall(A & a)139 void leftRefCall(A &a) {
140 a.foo();
141 }
rightRefCall(A && a)142 void rightRefCall(A &&a) {
143 a.foo();
144 }
constCopyOrMoveCall(const A a)145 void constCopyOrMoveCall(const A a) {
146 a.foo();
147 }
148
copyOrMoveCall(A a)149 void copyOrMoveCall(A a) {
150 a.foo();
151 }
152
simpleMoveCtorTest()153 void simpleMoveCtorTest() {
154 {
155 A a;
156 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}
157
158 #ifdef AGGRESSIVE_DFS
159 clang_analyzer_printState();
160
161 // CHECK: "checker_messages": [
162 // CHECK-NEXT: { "checker": "cplusplus.Move", "messages": [
163 // CHECK-NEXT: "Moved-from objects :",
164 // CHECK: "a: moved",
165 // CHECK: ""
166 // CHECK-NEXT: ]}
167 // CHECK-NEXT: ]
168 #endif
169
170 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
171 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
172 }
173 {
174 A a;
175 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}
176 b = a; // peaceful-warning {{Moved-from object 'a' is copied}}
177 // peaceful-note@-1 {{Moved-from object 'a' is copied}}
178 }
179 {
180 A a;
181 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}
182 b = std::move(a); // peaceful-warning {{Moved-from object 'a' is moved}}
183 // peaceful-note@-1 {{Moved-from object 'a' is moved}}
184 }
185 }
186
simpleMoveAssignementTest()187 void simpleMoveAssignementTest() {
188 {
189 A a;
190 A b;
191 b = std::move(a); // peaceful-note {{Object 'a' is moved}}
192 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
193 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
194 }
195 {
196 A a;
197 A b;
198 b = std::move(a); // peaceful-note {{Object 'a' is moved}}
199 A c(a); // peaceful-warning {{Moved-from object 'a' is copied}}
200 // peaceful-note@-1 {{Moved-from object 'a' is copied}}
201 }
202 {
203 A a;
204 A b;
205 b = std::move(a); // peaceful-note {{Object 'a' is moved}}
206 A c(std::move(a)); // peaceful-warning {{Moved-from object 'a' is moved}}
207 // peaceful-note@-1 {{Moved-from object 'a' is moved}}
208 }
209 }
210
moveInInitListTest()211 void moveInInitListTest() {
212 struct S {
213 A a;
214 };
215 A a;
216 S s{std::move(a)}; // peaceful-note {{Object 'a' is moved}}
217 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
218 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
219 }
220
221 // Don't report a bug if the variable was assigned to in the meantime.
reinitializationTest(int i)222 void reinitializationTest(int i) {
223 {
224 A a;
225 A b;
226 b = std::move(a);
227 a = A();
228 a.foo();
229 }
230 {
231 A a;
232 if (i == 1) { // peaceful-note 2 {{Assuming 'i' is not equal to 1}}
233 // peaceful-note@-1 2 {{Taking false branch}}
234 A b;
235 b = std::move(a);
236 a = A();
237 }
238 if (i == 2) { // peaceful-note 2 {{Assuming 'i' is not equal to 2}}
239 // peaceful-note@-1 2 {{Taking false branch}}
240 a.foo(); // no-warning
241 }
242 }
243 {
244 A a;
245 if (i == 1) { // peaceful-note 2 {{'i' is not equal to 1}}
246 // peaceful-note@-1 2 {{Taking false branch}}
247 std::move(a);
248 }
249 if (i == 2) { // peaceful-note 2 {{'i' is not equal to 2}}
250 // peaceful-note@-1 2 {{Taking false branch}}
251 a = A();
252 a.foo();
253 }
254 }
255 // The built-in assignment operator should also be recognized as a
256 // reinitialization. (std::move() may be called on built-in types in template
257 // code.)
258 {
259 int a1 = 1, a2 = 2;
260 std::swap(a1, a2);
261 }
262 // A std::move() after the assignment makes the variable invalid again.
263 {
264 A a;
265 A b;
266 b = std::move(a);
267 a = A();
268 b = std::move(a); // peaceful-note {{Object 'a' is moved}}
269 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
270 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
271 }
272 // If a path exist where we not reinitialize the variable we report a bug.
273 {
274 A a;
275 A b;
276 b = std::move(a); // peaceful-note {{Object 'a' is moved}}
277 if (i < 10) { // peaceful-note {{Assuming 'i' is >= 10}}
278 // peaceful-note@-1 {{Taking false branch}}
279 a = A();
280 }
281 if (i > 5) { // peaceful-note {{'i' is > 5}}
282 // peaceful-note@-1 {{Taking true branch}}
283 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
284 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
285 }
286 }
287 }
288
289 // Using decltype on an expression is not a use.
decltypeIsNotUseTest()290 void decltypeIsNotUseTest() {
291 A a;
292 // A b(std::move(a));
293 decltype(a) other_a; // no-warning
294 }
295
loopTest()296 void loopTest() {
297 {
298 A a;
299 // FIXME: Execution doesn't jump to the end of the function yet.
300 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}
301 rightRefCall(std::move(a)); // no-warning
302 }
303 }
304 {
305 A a;
306 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}
307 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}
308 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
309 rightRefCall(std::move(a)); // no-warning
310 }
311 }
312 {
313 A a;
314 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}
315 leftRefCall(a); // no-warning
316 }
317 }
318 {
319 A a;
320 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}
321 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}
322 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
323 leftRefCall(a); // no-warning
324 }
325 }
326 {
327 A a;
328 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}
329 constCopyOrMoveCall(a); // no-warning
330 }
331 }
332 {
333 A a;
334 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}
335 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}
336 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
337 constCopyOrMoveCall(a); // no-warning
338 }
339 }
340 {
341 A a;
342 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}
343 moveInsideFunctionCall(a); // no-warning
344 }
345 }
346 {
347 A a;
348 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}
349 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}
350 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
351 moveInsideFunctionCall(a); // no-warning
352 }
353 }
354 {
355 A a;
356 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is false. Execution jumps to the end of the function}}
357 copyOrMoveCall(a); // no-warning
358 }
359 }
360 {
361 A a;
362 for (int i = 0; i < 2; i++) { // peaceful-note {{Loop condition is true. Entering loop body}}
363 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}
364 // peaceful-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
365 copyOrMoveCall(a); // no-warning
366 }
367 }
368 {
369 A a;
370 for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is true. Entering loop body}}
371 // peaceful-note@-1 {{Loop condition is true. Entering loop body}}
372 constCopyOrMoveCall(std::move(a)); // peaceful-note {{Object 'a' is moved}}
373 // peaceful-warning@-1 {{Moved-from object 'a' is moved}}
374 // peaceful-note@-2 {{Moved-from object 'a' is moved}}
375 }
376 }
377
378 // Don't warn if we return after the move.
379 {
380 A a;
381 for (int i = 0; i < 3; ++i) {
382 a.bar();
383 if (a.foo() > 0) {
384 A b;
385 b = std::move(a); // no-warning
386 return;
387 }
388 }
389 }
390 }
391
392 // Report a usage of a moved-from object only at the first use.
uniqueTest(bool cond)393 void uniqueTest(bool cond) {
394 A a(42, 42.0);
395 A b;
396 b = std::move(a); // peaceful-note {{Object 'a' is moved}}
397
398 if (cond) { // peaceful-note {{Assuming 'cond' is true}}
399 // peaceful-note@-1 {{Taking true branch}}
400 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
401 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
402 }
403 if (cond) {
404 a.bar(); // no-warning
405 }
406
407 a.bar(); // no-warning
408 }
409
uniqueTest2()410 void uniqueTest2() {
411 A a;
412 A a1 = std::move(a); // peaceful-note {{Object 'a' is moved}}
413 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
414 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
415
416 A a2 = std::move(a); // no-warning
417 a.foo(); // no-warning
418 }
419
420 // There are exceptions where we assume in general that the method works fine
421 //even on moved-from objects.
moveSafeFunctionsTest()422 void moveSafeFunctionsTest() {
423 A a;
424 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}
425 a.empty(); // no-warning
426 a.isEmpty(); // no-warning
427 (void)a; // no-warning
428 (bool)a; // expected-warning {{expression result unused}}
429 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
430 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
431 }
432
moveStateResetFunctionsTest()433 void moveStateResetFunctionsTest() {
434 {
435 A a;
436 A b = std::move(a);
437 a.reset(); // no-warning
438 a.foo(); // no-warning
439 // Test if resets the state of subregions as well.
440 a.b.foo(); // no-warning
441 }
442 {
443 A a;
444 A b = std::move(a);
445 a.destroy(); // no-warning
446 a.foo(); // no-warning
447 }
448 {
449 A a;
450 A b = std::move(a);
451 a.clear(); // no-warning
452 a.foo(); // no-warning
453 a.b.foo(); // no-warning
454 }
455 {
456 A a;
457 A b = std::move(a);
458 a.resize(0); // no-warning
459 a.foo(); // no-warning
460 a.b.foo(); // no-warning
461 }
462 {
463 A a;
464 A b = std::move(a);
465 a.assign(A()); // no-warning
466 a.foo(); // no-warning
467 a.b.foo(); // no-warning
468 }
469 }
470
471 // Moves or uses that occur as part of template arguments.
472 template <int>
473 class ClassTemplate {
474 public:
475 void foo(A a);
476 };
477
478 template <int>
479 void functionTemplate(A a);
480
templateArgIsNotUseTest()481 void templateArgIsNotUseTest() {
482 {
483 // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in
484 // Google Test.
485 A a;
486 ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a)); // no-warning
487 }
488 {
489 A a;
490 functionTemplate<sizeof(A(std::move(a)))>(std::move(a)); // no-warning
491 }
492 }
493
494 // Moves of global variables are not reported.
495 A global_a;
globalVariablesTest()496 void globalVariablesTest() {
497 std::move(global_a);
498 global_a.foo(); // no-warning
499 }
500
501 // Moves of member variables.
502 class memberVariablesTest {
503 A a;
504 static A static_a;
505
f()506 void f() {
507 A b;
508 b = std::move(a); // aggressive-note {{Object 'a' is moved}}
509
510 a.foo(); // aggressive-warning {{Method called on moved-from object 'a'}}
511 // aggressive-note@-1 {{Method called on moved-from object 'a'}}
512
513 b = std::move(static_a); // aggressive-note {{Object 'static_a' is moved}}
514 static_a.foo(); // aggressive-warning {{Method called on moved-from object 'static_a'}}
515 // aggressive-note@-1 {{Method called on moved-from object 'static_a'}}
516 }
517 };
518
PtrAndArrayTest()519 void PtrAndArrayTest() {
520 A *Ptr = new A(1, 1.5);
521 A Arr[10];
522 Arr[2] = std::move(*Ptr); // aggressive-note{{Object is moved}}
523 (*Ptr).foo(); // aggressive-warning{{Method called on moved-from object}}
524 // aggressive-note@-1{{Method called on moved-from object}}
525
526 Ptr = &Arr[1];
527 Arr[3] = std::move(Arr[1]); // aggressive-note {{Object is moved}}
528 Ptr->foo(); // aggressive-warning {{Method called on moved-from object}}
529 // aggressive-note@-1 {{Method called on moved-from object}}
530
531 Arr[3] = std::move(Arr[2]); // aggressive-note{{Object is moved}}
532 Arr[2].foo(); // aggressive-warning{{Method called on moved-from object}}
533 // aggressive-note@-1{{Method called on moved-from object}}
534
535 Arr[2] = std::move(Arr[3]); // reinitialization
536 Arr[2].foo(); // no-warning
537 }
538
exclusiveConditionsTest(bool cond)539 void exclusiveConditionsTest(bool cond) {
540 A a;
541 if (cond) {
542 A b;
543 b = std::move(a);
544 }
545 if (!cond) {
546 a.bar(); // no-warning
547 }
548 }
549
differentBranchesTest(int i)550 void differentBranchesTest(int i) {
551 // Don't warn if the use is in a different branch from the move.
552 {
553 A a;
554 if (i > 0) { // peaceful-note {{Assuming 'i' is > 0}}
555 // peaceful-note@-1 {{Taking true branch}}
556 A b;
557 b = std::move(a);
558 } else {
559 a.foo(); // no-warning
560 }
561 }
562 // Same thing, but with a ternary operator.
563 {
564 A a, b;
565 i > 0 ? (void)(b = std::move(a)) : a.bar(); // no-warning
566 // peaceful-note@-1 {{'i' is > 0}}
567 // peaceful-note@-2 {{'?' condition is true}}
568 }
569 // A variation on the theme above.
570 {
571 A a;
572 a.foo() > 0 ? a.foo() : A(std::move(a)).foo();
573 #ifdef DFS
574 // peaceful-note@-2 {{Assuming the condition is false}}
575 // peaceful-note@-3 {{'?' condition is false}}
576 #else
577 // peaceful-note@-5 {{Assuming the condition is true}}
578 // peaceful-note@-6 {{'?' condition is true}}
579 #endif
580 }
581 // Same thing, but with a switch statement.
582 {
583 A a, b;
584 switch (i) { // peaceful-note {{Control jumps to 'case 1:'}}
585 case 1:
586 b = std::move(a); // no-warning
587 // FIXME: Execution doesn't jump to the end of the function yet.
588 break; // peaceful-note {{Execution jumps to the end of the function}}
589 case 2:
590 a.foo(); // no-warning
591 break;
592 }
593 }
594 // However, if there's a fallthrough, we do warn.
595 {
596 A a, b;
597 switch (i) { // peaceful-note {{Control jumps to 'case 1:'}}
598 case 1:
599 b = std::move(a); // peaceful-note {{Object 'a' is moved}}
600 case 2:
601 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
602 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
603 break;
604 }
605 }
606 }
607
tempTest()608 void tempTest() {
609 A a = A::get();
610 A::get().foo(); // no-warning
611 for (int i = 0; i < bignum(); i++) {
612 A::get().foo(); // no-warning
613 }
614 }
615
interFunTest1(A & a)616 void interFunTest1(A &a) {
617 a.bar(); // peaceful-warning {{Method called on moved-from object 'a'}}
618 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
619 }
620
interFunTest2()621 void interFunTest2() {
622 A a;
623 A b;
624 b = std::move(a); // peaceful-note {{Object 'a' is moved}}
625 interFunTest1(a); // peaceful-note {{Calling 'interFunTest1'}}
626 }
627
628 void foobar(A a, int i);
629 void foobar(int i, A a);
630
paramEvaluateOrderTest()631 void paramEvaluateOrderTest() {
632 A a;
633 foobar(std::move(a), a.getI()); // peaceful-note {{Object 'a' is moved}}
634 // peaceful-warning@-1 {{Method called on moved-from object 'a'}}
635 // peaceful-note@-2 {{Method called on moved-from object 'a'}}
636
637 //FALSE NEGATIVE since parameters evaluate order is undefined
638 foobar(a.getI(), std::move(a)); //no-warning
639 }
640
641 void not_known_pass_by_ref(A &a);
642 void not_known_pass_by_const_ref(const A &a);
643 void not_known_pass_by_rvalue_ref(A &&a);
644 void not_known_pass_by_ptr(A *a);
645 void not_known_pass_by_const_ptr(const A *a);
646
regionAndPointerEscapeTest()647 void regionAndPointerEscapeTest() {
648 {
649 A a;
650 A b;
651 b = std::move(a);
652 not_known_pass_by_ref(a);
653 a.foo(); // no-warning
654 }
655 {
656 A a;
657 A b;
658 b = std::move(a); // peaceful-note{{Object 'a' is moved}}
659 not_known_pass_by_const_ref(a);
660 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
661 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
662 }
663 {
664 A a;
665 A b;
666 b = std::move(a);
667 not_known_pass_by_rvalue_ref(std::move(a));
668 a.foo(); // no-warning
669 }
670 {
671 A a;
672 A b;
673 b = std::move(a);
674 not_known_pass_by_ptr(&a);
675 a.foo(); // no-warning
676 }
677 {
678 A a;
679 A b;
680 b = std::move(a); // peaceful-note {{Object 'a' is moved}}
681 not_known_pass_by_const_ptr(&a);
682 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
683 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
684 }
685 }
686
687 // A declaration statement containing multiple declarations sequences the
688 // initializer expressions.
declarationSequenceTest()689 void declarationSequenceTest() {
690 {
691 A a;
692 A a1 = a, a2 = std::move(a); // no-warning
693 }
694 {
695 A a;
696 A a1 = std::move(a), a2 = a; // peaceful-note {{Object 'a' is moved}}
697 // peaceful-warning@-1 {{Moved-from object 'a' is copied}}
698 // peaceful-note@-2 {{Moved-from object 'a' is copied}}
699 }
700 }
701
702 // The logical operators && and || sequence their operands.
logicalOperatorsSequenceTest()703 void logicalOperatorsSequenceTest() {
704 {
705 A a;
706 if (a.foo() > 0 && A(std::move(a)).foo() > 0) { // peaceful-note {{Assuming the condition is false}}
707 // peaceful-note@-1 {{Left side of '&&' is false}}
708 // peaceful-note@-2 {{Taking false branch}}
709 // And the other report:
710 // peaceful-note@-4 {{Assuming the condition is false}}
711 // peaceful-note@-5 {{Left side of '&&' is false}}
712 // peaceful-note@-6 {{Taking false branch}}
713 A().bar();
714 }
715 }
716 // A variation: Negate the result of the && (which pushes the && further down
717 // into the AST).
718 {
719 A a;
720 if (!(a.foo() > 0 && A(std::move(a)).foo() > 0)) { // peaceful-note {{Assuming the condition is false}}
721 // peaceful-note@-1 {{Left side of '&&' is false}}
722 // peaceful-note@-2 {{Taking true branch}}
723 // And the other report:
724 // peaceful-note@-4 {{Assuming the condition is false}}
725 // peaceful-note@-5 {{Left side of '&&' is false}}
726 // peaceful-note@-6 {{Taking true branch}}
727 A().bar();
728 }
729 }
730 {
731 A a;
732 if (A(std::move(a)).foo() > 0 && a.foo() > 0) { // peaceful-note {{Object 'a' is moved}}
733 // peaceful-note@-1 {{Assuming the condition is true}}
734 // peaceful-note@-2 {{Left side of '&&' is true}}
735 // peaceful-warning@-3 {{Method called on moved-from object 'a'}}
736 // peaceful-note@-4 {{Method called on moved-from object 'a'}}
737 // And the other report:
738 // peaceful-note@-6 {{Assuming the condition is false}}
739 // peaceful-note@-7 {{Left side of '&&' is false}}
740 // peaceful-note@-8 {{Taking false branch}}
741 A().bar();
742 }
743 }
744 {
745 A a;
746 if (a.foo() > 0 || A(std::move(a)).foo() > 0) { // peaceful-note {{Assuming the condition is true}}
747 // peaceful-note@-1 {{Left side of '||' is true}}
748 // peaceful-note@-2 {{Taking true branch}}
749 A().bar();
750 }
751 }
752 {
753 A a;
754 if (A(std::move(a)).foo() > 0 || a.foo() > 0) { // peaceful-note {{Object 'a' is moved}}
755 // peaceful-note@-1 {{Assuming the condition is false}}
756 // peaceful-note@-2 {{Left side of '||' is false}}
757 // peaceful-warning@-3 {{Method called on moved-from object 'a'}}
758 // peaceful-note@-4 {{Method called on moved-from object 'a'}}
759 A().bar();
760 }
761 }
762 }
763
764 // A range-based for sequences the loop variable declaration before the body.
forRangeSequencesTest()765 void forRangeSequencesTest() {
766 A v[2] = {A(), A()};
767 for (A &a : v) {
768 A b;
769 b = std::move(a); // no-warning
770 }
771 }
772
773 // If a variable is declared in an if statement, the declaration of the variable
774 // (which is treated like a reinitialization by the check) is sequenced before
775 // the evaluation of the condition (which constitutes a use).
ifStmtSequencesDeclAndConditionTest()776 void ifStmtSequencesDeclAndConditionTest() {
777 for (int i = 0; i < 3; ++i) {
778 if (A a = A()) {
779 A b;
780 b = std::move(a); // no-warning
781 }
782 }
783 }
784
785 struct C : public A {
786 [[clang::reinitializes]] void reinit();
787 };
788
subRegionMoveTest()789 void subRegionMoveTest() {
790 {
791 A a;
792 B b = std::move(a.b); // aggressive-note {{Object 'b' is moved}}
793 a.b.foo(); // aggressive-warning {{Method called on moved-from object 'b'}}
794 // aggressive-note@-1 {{Method called on moved-from object 'b'}}
795 }
796 {
797 A a;
798 A a1 = std::move(a); // aggressive-note {{Calling move constructor for 'A'}}
799 // aggressive-note@-1 {{Returning from move constructor for 'A'}}
800 a.b.foo(); // aggressive-warning{{Method called on moved-from object 'b'}}
801 // aggressive-note@-1{{Method called on moved-from object 'b'}}
802 }
803 // Don't report a misuse if any SuperRegion is already reported.
804 {
805 A a;
806 A a1 = std::move(a); // peaceful-note {{Object 'a' is moved}}
807 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
808 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
809 a.b.foo(); // no-warning
810 }
811 {
812 C c;
813 C c1 = std::move(c); // peaceful-note {{Object 'c' is moved}}
814 c.foo(); // peaceful-warning {{Method called on moved-from object 'c'}}
815 // peaceful-note@-1 {{Method called on moved-from object 'c'}}
816 c.b.foo(); // no-warning
817 }
818 }
819
resetSuperClass()820 void resetSuperClass() {
821 C c;
822 C c1 = std::move(c);
823 c.clear();
824 C c2 = c; // no-warning
825 }
826
resetSuperClass2()827 void resetSuperClass2() {
828 C c;
829 C c1 = std::move(c);
830 c.reinit();
831 C c2 = c; // no-warning
832 }
833
reportSuperClass()834 void reportSuperClass() {
835 C c;
836 C c1 = std::move(c); // peaceful-note {{Object 'c' is moved}}
837 c.foo(); // peaceful-warning {{Method called on moved-from object 'c'}}
838 // peaceful-note@-1 {{Method called on moved-from object 'c'}}
839 C c2 = c; // no-warning
840 }
841
842 struct Empty {};
843
inlinedCall()844 Empty inlinedCall() {
845 // Used to warn because region 'e' failed to be cleaned up because no symbols
846 // have ever died during the analysis and the checkDeadSymbols callback
847 // was skipped entirely.
848 Empty e{};
849 return e; // no-warning
850 }
851
checkInlinedCallZombies()852 void checkInlinedCallZombies() {
853 while (true)
854 inlinedCall();
855 }
856
checkLoopZombies()857 void checkLoopZombies() {
858 while (true) {
859 Empty e{};
860 Empty f = std::move(e); // no-warning
861 }
862 }
863
checkMoreLoopZombies1(bool flag)864 void checkMoreLoopZombies1(bool flag) {
865 while (flag) {
866 Empty e{};
867 if (true)
868 e; // expected-warning {{expression result unused}}
869 Empty f = std::move(e); // no-warning
870 }
871 }
872
873 bool coin();
874
checkMoreLoopZombies2(bool flag)875 void checkMoreLoopZombies2(bool flag) {
876 while (flag) {
877 Empty e{};
878 while (coin())
879 e; // expected-warning {{expression result unused}}
880 Empty f = std::move(e); // no-warning
881 }
882 }
883
checkMoreLoopZombies3(bool flag)884 void checkMoreLoopZombies3(bool flag) {
885 while (flag) {
886 Empty e{};
887 do
888 e; // expected-warning {{expression result unused}}
889 while (coin());
890 Empty f = std::move(e); // no-warning
891 }
892 }
893
checkMoreLoopZombies4(bool flag)894 void checkMoreLoopZombies4(bool flag) {
895 while (flag) {
896 Empty e{};
897 for (; coin();)
898 e; // expected-warning {{expression result unused}}
899 Empty f = std::move(e); // no-warning
900 }
901 }
902
903 struct MoveOnlyWithDestructor {
904 MoveOnlyWithDestructor();
905 ~MoveOnlyWithDestructor();
906 MoveOnlyWithDestructor(const MoveOnlyWithDestructor &m) = delete;
907 MoveOnlyWithDestructor(MoveOnlyWithDestructor &&m);
908 };
909
foo()910 MoveOnlyWithDestructor foo() {
911 MoveOnlyWithDestructor m;
912 return m;
913 }
914
915 class HasSTLField {
916 std::vector<int> V;
testVector()917 void testVector() {
918 // Warn even in non-aggressive mode when it comes to STL, because
919 // in STL the object is left in "valid but unspecified state" after move.
920 std::vector<int> W = std::move(V); // expected-note {{Object 'V' of type 'std::vector' is left in a valid but unspecified state after move}}
921 V.push_back(123); // expected-warning {{Method called on moved-from object 'V'}}
922 // expected-note@-1 {{Method called on moved-from object 'V'}}
923 }
924
925 std::unique_ptr<int> P;
testUniquePtr()926 void testUniquePtr() {
927 // unique_ptr remains in a well-defined state after move.
928 std::unique_ptr<int> Q = std::move(P); // aggressive-note {{Object 'P' is moved}}
929 // non-aggressive-note@-1 {{Smart pointer 'P' of type 'std::unique_ptr' is reset to null when moved from}}
930 P.get(); // aggressive-warning{{Method called on moved-from object 'P'}}
931 // aggressive-note@-1{{Method called on moved-from object 'P'}}
932
933 // Because that well-defined state is null, dereference is still UB.
934 // Note that in aggressive mode we already warned about 'P',
935 // so no extra warning is generated.
936 *P += 1; // non-aggressive-warning{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
937 // non-aggressive-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
938
939 // The program should have crashed by now.
940 clang_analyzer_warnIfReached(); // no-warning
941 }
942 };
943
localRValueMove(A && a)944 void localRValueMove(A &&a) {
945 A b = std::move(a); // peaceful-note {{Object 'a' is moved}}
946 a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}}
947 // peaceful-note@-1 {{Method called on moved-from object 'a'}}
948 }
949
localUniquePtr(std::unique_ptr<int> P)950 void localUniquePtr(std::unique_ptr<int> P) {
951 // Even though unique_ptr is safe to use after move,
952 // reusing a local variable this way usually indicates a bug.
953 std::unique_ptr<int> Q = std::move(P); // peaceful-note {{Object 'P' is moved}}
954 P.get(); // peaceful-warning {{Method called on moved-from object 'P'}}
955 // peaceful-note@-1 {{Method called on moved-from object 'P'}}
956 }
957
localUniquePtrWithArrow(std::unique_ptr<A> P)958 void localUniquePtrWithArrow(std::unique_ptr<A> P) {
959 std::unique_ptr<A> Q = std::move(P); // expected-note{{Smart pointer 'P' of type 'std::unique_ptr' is reset to null when moved from}}
960 P->foo(); // expected-warning{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
961 // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
962 }
963
getAfterMove(std::unique_ptr<A> P)964 void getAfterMove(std::unique_ptr<A> P) {
965 std::unique_ptr<A> Q = std::move(P); // peaceful-note {{Object 'P' is moved}}
966
967 // TODO: Explain why (bool)P is false.
968 if (P) // peaceful-note{{Taking false branch}}
969 clang_analyzer_warnIfReached(); // no-warning
970
971 A *a = P.get(); // peaceful-warning {{Method called on moved-from object 'P'}}
972 // peaceful-note@-1 {{Method called on moved-from object 'P'}}
973
974 // TODO: Warn on a null dereference here.
975 a->foo();
976 }
977
978 struct OtherMoveSafeClasses {
979 std::packaged_task<int(void)> Task;
980
testOtherMoveSafeClasses981 void test() {
982 // Test the suppression caused by use-after-move semantics of
983 // std::package_task being different from other standard classes.
984 // Only warn in aggressive mode. Don't say that the object
985 // is left in unspecified state after move.
986 std::packaged_task<int(void)> Task2 = std::move(Task);
987 // aggressive-note@-1 {{Object 'Task' is moved}}
988 std::packaged_task<int(void)> Task3 = std::move(Task);
989 // aggressive-warning@-1{{Moved-from object 'Task' is moved}}
990 // aggressive-note@-2 {{Moved-from object 'Task' is moved}}
991 }
992 };
993