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