summaryrefslogtreecommitdiff
path: root/test/Analysis/use-after-move.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/Analysis/use-after-move.cpp')
-rw-r--r--test/Analysis/use-after-move.cpp1139
1 files changed, 1139 insertions, 0 deletions
diff --git a/test/Analysis/use-after-move.cpp b/test/Analysis/use-after-move.cpp
new file mode 100644
index 0000000000000..280724512f8aa
--- /dev/null
+++ b/test/Analysis/use-after-move.cpp
@@ -0,0 +1,1139 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
+// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
+// RUN: -analyzer-config exploration_strategy=unexplored_first_queue\
+// RUN: -analyzer-checker debug.ExprInspection
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
+// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
+// RUN: -analyzer-config exploration_strategy=dfs -DDFS=1\
+// RUN: -analyzer-checker debug.ExprInspection
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
+// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
+// RUN: -analyzer-config exploration_strategy=unexplored_first_queue\
+// RUN: -analyzer-config cplusplus.Move:WarnOn=KnownsOnly -DPEACEFUL\
+// RUN: -analyzer-checker debug.ExprInspection
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
+// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
+// RUN: -analyzer-config exploration_strategy=dfs -DDFS=1\
+// RUN: -analyzer-config cplusplus.Move:WarnOn=KnownsOnly -DPEACEFUL\
+// RUN: -analyzer-checker debug.ExprInspection
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
+// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
+// RUN: -analyzer-config exploration_strategy=unexplored_first_queue\
+// RUN: -analyzer-config cplusplus.Move:WarnOn=All -DAGGRESSIVE\
+// RUN: -analyzer-checker debug.ExprInspection
+// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
+// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
+// RUN: -analyzer-config exploration_strategy=dfs -DDFS=1\
+// RUN: -analyzer-config cplusplus.Move:WarnOn=All -DAGGRESSIVE\
+// RUN: -analyzer-checker debug.ExprInspection
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void clang_analyzer_warnIfReached();
+
+class B {
+public:
+ B() = default;
+ B(const B &) = default;
+ B(B &&) = default;
+ B& operator=(const B &q) = default;
+ void operator=(B &&b) {
+ return;
+ }
+ void foo() { return; }
+};
+
+class A {
+ int i;
+ double d;
+
+public:
+ B b;
+ A(int ii = 42, double dd = 1.0) : d(dd), i(ii), b(B()) {}
+ void moveconstruct(A &&other) {
+ std::swap(b, other.b);
+ std::swap(d, other.d);
+ std::swap(i, other.i);
+ return;
+ }
+ static A get() {
+ A v(12, 13);
+ return v;
+ }
+ A(A *a) {
+ moveconstruct(std::move(*a));
+ }
+ A(const A &other) : i(other.i), d(other.d), b(other.b) {}
+ A(A &&other) : i(other.i), d(other.d), b(std::move(other.b)) {
+#ifdef AGGRESSIVE
+ // expected-note@-2{{Object 'b' is moved}}
+#endif
+ }
+ A(A &&other, char *k) {
+ moveconstruct(std::move(other));
+ }
+ void operator=(const A &other) {
+ i = other.i;
+ d = other.d;
+ b = other.b;
+ return;
+ }
+ void operator=(A &&other) {
+ moveconstruct(std::move(other));
+ return;
+ }
+ int getI() { return i; }
+ int foo() const;
+ void bar() const;
+ void reset();
+ void destroy();
+ void clear();
+ void resize(std::size_t);
+ bool empty() const;
+ bool isEmpty() const;
+ operator bool() const;
+
+ void testUpdateField() {
+ A a;
+ A b = std::move(a);
+ a.i = 1;
+ a.foo(); // no-warning
+ }
+ void testUpdateFieldDouble() {
+ A a;
+ A b = std::move(a);
+ a.d = 1.0;
+ a.foo(); // no-warning
+ }
+};
+
+int bignum();
+
+void moveInsideFunctionCall(A a) {
+ A b = std::move(a);
+}
+void leftRefCall(A &a) {
+ a.foo();
+}
+void rightRefCall(A &&a) {
+ a.foo();
+}
+void constCopyOrMoveCall(const A a) {
+ a.foo();
+}
+
+void copyOrMoveCall(A a) {
+ a.foo();
+}
+
+void simpleMoveCtorTest() {
+ {
+ A a;
+ A b = std::move(a);
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+ }
+ {
+ A a;
+ A b = std::move(a);
+ b = a;
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Moved-from object 'a' is copied}}
+ // expected-note@-4 {{Moved-from object 'a' is copied}}
+#endif
+ }
+ {
+ A a;
+ A b = std::move(a);
+ b = std::move(a);
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Moved-from object 'a' is moved}}
+ // expected-note@-4 {{Moved-from object 'a' is moved}}
+#endif
+ }
+}
+
+void simpleMoveAssignementTest() {
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+ }
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ A c(a);
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Moved-from object 'a' is copied}}
+ // expected-note@-4 {{Moved-from object 'a' is copied}}
+#endif
+ }
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ A c(std::move(a));
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Moved-from object 'a' is moved}}
+ // expected-note@-4 {{Moved-from object 'a' is moved}}
+#endif
+ }
+}
+
+void moveInInitListTest() {
+ struct S {
+ A a;
+ };
+ A a;
+ S s{std::move(a)};
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+}
+
+// Don't report a bug if the variable was assigned to in the meantime.
+void reinitializationTest(int i) {
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ a = A();
+ a.foo();
+ }
+ {
+ A a;
+ if (i == 1) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Assuming 'i' is not equal to 1}}
+ // expected-note@-3 {{Taking false branch}}
+ // And the other report:
+ // expected-note@-5 {{Assuming 'i' is not equal to 1}}
+ // expected-note@-6 {{Taking false branch}}
+#endif
+ A b;
+ b = std::move(a);
+ a = A();
+ }
+ if (i == 2) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Assuming 'i' is not equal to 2}}
+ // expected-note@-3 {{Taking false branch}}
+ // And the other report:
+ // expected-note@-5 {{Assuming 'i' is not equal to 2}}
+ // expected-note@-6 {{Taking false branch}}
+#endif
+ a.foo(); // no-warning
+ }
+ }
+ {
+ A a;
+ if (i == 1) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Taking false branch}}
+ // expected-note@-3 {{Taking false branch}}
+#endif
+ std::move(a);
+ }
+ if (i == 2) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Taking false branch}}
+ // expected-note@-3 {{Taking false branch}}
+#endif
+ a = A();
+ a.foo();
+ }
+ }
+ // The built-in assignment operator should also be recognized as a
+ // reinitialization. (std::move() may be called on built-in types in template
+ // code.)
+ {
+ int a1 = 1, a2 = 2;
+ std::swap(a1, a2);
+ }
+ // A std::move() after the assignment makes the variable invalid again.
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ a = A();
+ b = std::move(a);
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+ }
+ // If a path exist where we not reinitialize the variable we report a bug.
+ {
+ A a;
+ A b;
+ b = std::move(a);
+#ifndef PEACEFUL
+ // expected-note@-2 {{Object 'a' is moved}}
+#endif
+ if (i < 10) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Assuming 'i' is >= 10}}
+ // expected-note@-3 {{Taking false branch}}
+#endif
+ a = A();
+ }
+ if (i > 5) {
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Taking true branch}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+ }
+ }
+}
+
+// Using decltype on an expression is not a use.
+void decltypeIsNotUseTest() {
+ A a;
+ // A b(std::move(a));
+ decltype(a) other_a; // no-warning
+}
+
+void loopTest() {
+ {
+ A a;
+ for (int i = 0; i < bignum(); i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ rightRefCall(std::move(a)); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < 2; i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is true. Entering loop body}}
+ // expected-note@-3 {{Loop condition is true. Entering loop body}}
+ // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ rightRefCall(std::move(a)); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < bignum(); i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ leftRefCall(a); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < 2; i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is true. Entering loop body}}
+ // expected-note@-3 {{Loop condition is true. Entering loop body}}
+ // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ leftRefCall(a); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < bignum(); i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ constCopyOrMoveCall(a); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < 2; i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is true. Entering loop body}}
+ // expected-note@-3 {{Loop condition is true. Entering loop body}}
+ // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ constCopyOrMoveCall(a); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < bignum(); i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ moveInsideFunctionCall(a); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < 2; i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is true. Entering loop body}}
+ // expected-note@-3 {{Loop condition is true. Entering loop body}}
+ // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ moveInsideFunctionCall(a); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < bignum(); i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ copyOrMoveCall(a); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < 2; i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is true. Entering loop body}}
+ // expected-note@-3 {{Loop condition is true. Entering loop body}}
+ // expected-note@-4 {{Loop condition is false. Execution jumps to the end of the function}}
+#endif
+ copyOrMoveCall(a); // no-warning
+ }
+ }
+ {
+ A a;
+ for (int i = 0; i < bignum(); i++) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Loop condition is true. Entering loop body}}
+ // expected-note@-3 {{Loop condition is true. Entering loop body}}
+#endif
+ constCopyOrMoveCall(std::move(a));
+#ifndef PEACEFUL
+ // expected-note@-2 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Moved-from object 'a' is moved}}
+ // expected-note@-4 {{Moved-from object 'a' is moved}}
+#endif
+ }
+ }
+
+ // Don't warn if we return after the move.
+ {
+ A a;
+ for (int i = 0; i < 3; ++i) {
+ a.bar();
+ if (a.foo() > 0) {
+ A b;
+ b = std::move(a); // no-warning
+ return;
+ }
+ }
+ }
+}
+
+// Report a usage of a moved-from object only at the first use.
+void uniqueTest(bool cond) {
+ A a(42, 42.0);
+ A b;
+ b = std::move(a);
+
+ if (cond) {
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-5 {{Object 'a' is moved}}
+ // expected-note@-4 {{Assuming 'cond' is not equal to 0}}
+ // expected-note@-5 {{Taking true branch}}
+ // expected-warning@-5 {{Method called on moved-from object 'a'}}
+ // expected-note@-6 {{Method called on moved-from object 'a'}}
+#endif
+ }
+ if (cond) {
+ a.bar(); // no-warning
+ }
+
+ a.bar(); // no-warning
+}
+
+void uniqueTest2() {
+ A a;
+ A a1 = std::move(a);
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+
+ A a2 = std::move(a); // no-warning
+ a.foo(); // no-warning
+}
+
+// There are exceptions where we assume in general that the method works fine
+//even on moved-from objects.
+void moveSafeFunctionsTest() {
+ A a;
+ A b = std::move(a);
+#ifndef PEACEFUL
+ // expected-note@-2 {{Object 'a' is moved}}
+#endif
+ a.empty(); // no-warning
+ a.isEmpty(); // no-warning
+ (void)a; // no-warning
+ (bool)a; // expected-warning {{expression result unused}}
+ a.foo();
+#ifndef PEACEFUL
+ // expected-warning@-2 {{Method called on moved-from object 'a'}}
+ // expected-note@-3 {{Method called on moved-from object 'a'}}
+#endif
+}
+
+void moveStateResetFunctionsTest() {
+ {
+ A a;
+ A b = std::move(a);
+ a.reset(); // no-warning
+ a.foo(); // no-warning
+ // Test if resets the state of subregions as well.
+ a.b.foo(); // no-warning
+ }
+ {
+ A a;
+ A b = std::move(a);
+ a.destroy(); // no-warning
+ a.foo(); // no-warning
+ }
+ {
+ A a;
+ A b = std::move(a);
+ a.clear(); // no-warning
+ a.foo(); // no-warning
+ a.b.foo(); // no-warning
+ }
+ {
+ A a;
+ A b = std::move(a);
+ a.resize(0); // no-warning
+ a.foo(); // no-warning
+ a.b.foo(); // no-warning
+ }
+}
+
+// Moves or uses that occur as part of template arguments.
+template <int>
+class ClassTemplate {
+public:
+ void foo(A a);
+};
+
+template <int>
+void functionTemplate(A a);
+
+void templateArgIsNotUseTest() {
+ {
+ // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in
+ // Google Test.
+ A a;
+ ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a)); // no-warning
+ }
+ {
+ A a;
+ functionTemplate<sizeof(A(std::move(a)))>(std::move(a)); // no-warning
+ }
+}
+
+// Moves of global variables are not reported.
+A global_a;
+void globalVariablesTest() {
+ std::move(global_a);
+ global_a.foo(); // no-warning
+}
+
+// Moves of member variables.
+class memberVariablesTest {
+ A a;
+ static A static_a;
+
+ void f() {
+ A b;
+ b = std::move(a);
+ a.foo();
+#ifdef AGGRESSIVE
+ // expected-note@-3{{Object 'a' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4{{Method called on moved-from object 'a'}}
+#endif
+
+ b = std::move(static_a);
+ static_a.foo();
+#ifdef AGGRESSIVE
+ // expected-note@-3{{Object 'static_a' is moved}}
+ // expected-warning@-3{{Method called on moved-from object 'static_a'}}
+ // expected-note@-4{{Method called on moved-from object 'static_a'}}
+#endif
+ }
+};
+
+void PtrAndArrayTest() {
+ A *Ptr = new A(1, 1.5);
+ A Arr[10];
+ Arr[2] = std::move(*Ptr);
+ (*Ptr).foo();
+#ifdef AGGRESSIVE
+ // expected-note@-3{{Object is moved}}
+ // expected-warning@-3{{Method called on moved-from object}}
+ // expected-note@-4{{Method called on moved-from object}}
+#endif
+
+ Ptr = &Arr[1];
+ Arr[3] = std::move(Arr[1]);
+ Ptr->foo();
+#ifdef AGGRESSIVE
+ // expected-note@-3{{Object is moved}}
+ // expected-warning@-3{{Method called on moved-from object}}
+ // expected-note@-4{{Method called on moved-from object}}
+#endif
+
+ Arr[3] = std::move(Arr[2]);
+ Arr[2].foo();
+#ifdef AGGRESSIVE
+ // expected-note@-3{{Object is moved}}
+ // expected-warning@-3{{Method called on moved-from object}}
+ // expected-note@-4{{Method called on moved-from object}}
+#endif
+
+ Arr[2] = std::move(Arr[3]); // reinitialization
+ Arr[2].foo(); // no-warning
+}
+
+void exclusiveConditionsTest(bool cond) {
+ A a;
+ if (cond) {
+ A b;
+ b = std::move(a);
+ }
+ if (!cond) {
+ a.bar(); // no-warning
+ }
+}
+
+void differentBranchesTest(int i) {
+ // Don't warn if the use is in a different branch from the move.
+ {
+ A a;
+ if (i > 0) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Assuming 'i' is > 0}}
+ // expected-note@-3 {{Taking true branch}}
+#endif
+ A b;
+ b = std::move(a);
+ } else {
+ a.foo(); // no-warning
+ }
+ }
+ // Same thing, but with a ternary operator.
+ {
+ A a, b;
+ i > 0 ? (void)(b = std::move(a)) : a.bar(); // no-warning
+#ifndef PEACEFUL
+ // expected-note@-2 {{'?' condition is true}}
+#endif
+ }
+ // A variation on the theme above.
+ {
+ A a;
+ a.foo() > 0 ? a.foo() : A(std::move(a)).foo();
+#ifdef DFS
+ #ifndef PEACEFUL
+ // expected-note@-3 {{Assuming the condition is false}}
+ // expected-note@-4 {{'?' condition is false}}
+ #endif
+#else
+ #ifndef PEACEFUL
+ // expected-note@-8 {{Assuming the condition is true}}
+ // expected-note@-9 {{'?' condition is true}}
+ #endif
+#endif
+ }
+ // Same thing, but with a switch statement.
+ {
+ A a, b;
+ switch (i) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Control jumps to 'case 1:'}}
+#endif
+ case 1:
+ b = std::move(a); // no-warning
+ break;
+#ifndef PEACEFUL
+ // expected-note@-2 {{Execution jumps to the end of the function}}
+#endif
+ case 2:
+ a.foo(); // no-warning
+ break;
+ }
+ }
+ // However, if there's a fallthrough, we do warn.
+ {
+ A a, b;
+ switch (i) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Control jumps to 'case 1:'}}
+#endif
+ case 1:
+ b = std::move(a);
+#ifndef PEACEFUL
+ // expected-note@-2 {{Object 'a' is moved}}
+#endif
+ case 2:
+ a.foo();
+#ifndef PEACEFUL
+ // expected-warning@-2 {{Method called on moved-from object}}
+ // expected-note@-3 {{Method called on moved-from object 'a'}}
+#endif
+ break;
+ }
+ }
+}
+
+void tempTest() {
+ A a = A::get();
+ A::get().foo(); // no-warning
+ for (int i = 0; i < bignum(); i++) {
+ A::get().foo(); // no-warning
+ }
+}
+
+void interFunTest1(A &a) {
+ a.bar();
+#ifndef PEACEFUL
+ // expected-warning@-2 {{Method called on moved-from object 'a'}}
+ // expected-note@-3 {{Method called on moved-from object 'a'}}
+#endif
+}
+
+void interFunTest2() {
+ A a;
+ A b;
+ b = std::move(a);
+ interFunTest1(a);
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-note@-3 {{Calling 'interFunTest1'}}
+#endif
+}
+
+void foobar(A a, int i);
+void foobar(int i, A a);
+
+void paramEvaluateOrderTest() {
+ A a;
+ foobar(std::move(a), a.getI());
+#ifndef PEACEFUL
+ // expected-note@-2 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+
+ //FALSE NEGATIVE since parameters evaluate order is undefined
+ foobar(a.getI(), std::move(a)); //no-warning
+}
+
+void not_known_pass_by_ref(A &a);
+void not_known_pass_by_const_ref(const A &a);
+void not_known_pass_by_rvalue_ref(A &&a);
+void not_known_pass_by_ptr(A *a);
+void not_known_pass_by_const_ptr(const A *a);
+
+void regionAndPointerEscapeTest() {
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ not_known_pass_by_ref(a);
+ a.foo(); // no-warning
+ }
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ not_known_pass_by_const_ref(a);
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-4{{Object 'a' is moved}}
+ // expected-warning@-3{{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+ }
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ not_known_pass_by_rvalue_ref(std::move(a));
+ a.foo(); // no-warning
+ }
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ not_known_pass_by_ptr(&a);
+ a.foo(); // no-warning
+ }
+ {
+ A a;
+ A b;
+ b = std::move(a);
+ not_known_pass_by_const_ptr(&a);
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-4{{Object 'a' is moved}}
+ // expected-warning@-3{{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+ }
+}
+
+// A declaration statement containing multiple declarations sequences the
+// initializer expressions.
+void declarationSequenceTest() {
+ {
+ A a;
+ A a1 = a, a2 = std::move(a); // no-warning
+ }
+ {
+ A a;
+ A a1 = std::move(a), a2 = a;
+#ifndef PEACEFUL
+ // expected-note@-2 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Moved-from object 'a' is copied}}
+ // expected-note@-4 {{Moved-from object 'a' is copied}}
+#endif
+ }
+}
+
+// The logical operators && and || sequence their operands.
+void logicalOperatorsSequenceTest() {
+ {
+ A a;
+ if (a.foo() > 0 && A(std::move(a)).foo() > 0) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Assuming the condition is false}}
+ // expected-note@-3 {{Left side of '&&' is false}}
+ // expected-note@-4 {{Taking false branch}}
+ // And the other report:
+ // expected-note@-6 {{Assuming the condition is false}}
+ // expected-note@-7 {{Left side of '&&' is false}}
+ // expected-note@-8 {{Taking false branch}}
+ A().bar();
+#endif
+ }
+ }
+ // A variation: Negate the result of the && (which pushes the && further down
+ // into the AST).
+ {
+ A a;
+ if (!(a.foo() > 0 && A(std::move(a)).foo() > 0)) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Assuming the condition is false}}
+ // expected-note@-3 {{Left side of '&&' is false}}
+ // expected-note@-4 {{Taking true branch}}
+ // And the other report:
+ // expected-note@-6 {{Assuming the condition is false}}
+ // expected-note@-7 {{Left side of '&&' is false}}
+ // expected-note@-8 {{Taking true branch}}
+#endif
+ A().bar();
+ }
+ }
+ {
+ A a;
+ if (A(std::move(a)).foo() > 0 && a.foo() > 0) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Object 'a' is moved}}
+ // expected-note@-3 {{Assuming the condition is true}}
+ // expected-note@-4 {{Left side of '&&' is true}}
+ // expected-warning@-5 {{Method called on moved-from object 'a'}}
+ // expected-note@-6 {{Method called on moved-from object 'a'}}
+ // And the other report:
+ // expected-note@-8 {{Assuming the condition is false}}
+ // expected-note@-9 {{Left side of '&&' is false}}
+ // expected-note@-10{{Taking false branch}}
+#endif
+ A().bar();
+ }
+ }
+ {
+ A a;
+ if (a.foo() > 0 || A(std::move(a)).foo() > 0) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Assuming the condition is true}}
+ // expected-note@-3 {{Left side of '||' is true}}
+ // expected-note@-4 {{Taking true branch}}
+#endif
+ A().bar();
+ }
+ }
+ {
+ A a;
+ if (A(std::move(a)).foo() > 0 || a.foo() > 0) {
+#ifndef PEACEFUL
+ // expected-note@-2 {{Object 'a' is moved}}
+ // expected-note@-3 {{Assuming the condition is false}}
+ // expected-note@-4 {{Left side of '||' is false}}
+ // expected-warning@-5 {{Method called on moved-from object 'a'}}
+ // expected-note@-6 {{Method called on moved-from object 'a'}}
+#endif
+ A().bar();
+ }
+ }
+}
+
+// A range-based for sequences the loop variable declaration before the body.
+void forRangeSequencesTest() {
+ A v[2] = {A(), A()};
+ for (A &a : v) {
+ A b;
+ b = std::move(a); // no-warning
+ }
+}
+
+// If a variable is declared in an if statement, the declaration of the variable
+// (which is treated like a reinitialization by the check) is sequenced before
+// the evaluation of the condition (which constitutes a use).
+void ifStmtSequencesDeclAndConditionTest() {
+ for (int i = 0; i < 3; ++i) {
+ if (A a = A()) {
+ A b;
+ b = std::move(a); // no-warning
+ }
+ }
+}
+
+struct C : public A {
+ [[clang::reinitializes]] void reinit();
+};
+
+void subRegionMoveTest() {
+ {
+ A a;
+ B b = std::move(a.b);
+ a.b.foo();
+#ifdef AGGRESSIVE
+ // expected-note@-3{{Object 'b' is moved}}
+ // expected-warning@-3{{Method called on moved-from object 'b'}}
+ // expected-note@-4 {{Method called on moved-from object 'b'}}
+#endif
+ }
+ {
+ A a;
+ A a1 = std::move(a);
+ a.b.foo();
+#ifdef AGGRESSIVE
+ // expected-note@-3{{Calling move constructor for 'A'}}
+ // expected-note@-4{{Returning from move constructor for 'A'}}
+ // expected-warning@-4{{Method called on moved-from object 'b'}}
+ // expected-note@-5{{Method called on moved-from object 'b'}}
+#endif
+ }
+ // Don't report a misuse if any SuperRegion is already reported.
+ {
+ A a;
+ A a1 = std::move(a);
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+ a.b.foo(); // no-warning
+ }
+ {
+ C c;
+ C c1 = std::move(c);
+ c.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'c' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'c'}}
+ // expected-note@-4 {{Method called on moved-from object 'c'}}
+#endif
+ c.b.foo(); // no-warning
+ }
+}
+
+void resetSuperClass() {
+ C c;
+ C c1 = std::move(c);
+ c.clear();
+ C c2 = c; // no-warning
+}
+
+void resetSuperClass2() {
+ C c;
+ C c1 = std::move(c);
+ c.reinit();
+ C c2 = c; // no-warning
+}
+
+void reportSuperClass() {
+ C c;
+ C c1 = std::move(c);
+ c.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'c' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'c'}}
+ // expected-note@-4 {{Method called on moved-from object 'c'}}
+#endif
+ C c2 = c; // no-warning
+}
+
+struct Empty {};
+
+Empty inlinedCall() {
+ // Used to warn because region 'e' failed to be cleaned up because no symbols
+ // have ever died during the analysis and the checkDeadSymbols callback
+ // was skipped entirely.
+ Empty e{};
+ return e; // no-warning
+}
+
+void checkInlinedCallZombies() {
+ while (true)
+ inlinedCall();
+}
+
+void checkLoopZombies() {
+ while (true) {
+ Empty e{};
+ Empty f = std::move(e); // no-warning
+ }
+}
+
+void checkMoreLoopZombies1(bool flag) {
+ while (flag) {
+ Empty e{};
+ if (true)
+ e; // expected-warning {{expression result unused}}
+ Empty f = std::move(e); // no-warning
+ }
+}
+
+bool coin();
+
+void checkMoreLoopZombies2(bool flag) {
+ while (flag) {
+ Empty e{};
+ while (coin())
+ e; // expected-warning {{expression result unused}}
+ Empty f = std::move(e); // no-warning
+ }
+}
+
+void checkMoreLoopZombies3(bool flag) {
+ while (flag) {
+ Empty e{};
+ do
+ e; // expected-warning {{expression result unused}}
+ while (coin());
+ Empty f = std::move(e); // no-warning
+ }
+}
+
+void checkMoreLoopZombies4(bool flag) {
+ while (flag) {
+ Empty e{};
+ for (; coin();)
+ e; // expected-warning {{expression result unused}}
+ Empty f = std::move(e); // no-warning
+ }
+}
+
+struct MoveOnlyWithDestructor {
+ MoveOnlyWithDestructor();
+ ~MoveOnlyWithDestructor();
+ MoveOnlyWithDestructor(const MoveOnlyWithDestructor &m) = delete;
+ MoveOnlyWithDestructor(MoveOnlyWithDestructor &&m);
+};
+
+MoveOnlyWithDestructor foo() {
+ MoveOnlyWithDestructor m;
+ return m;
+}
+
+class HasSTLField {
+ std::vector<int> V;
+ void testVector() {
+ // Warn even in non-aggressive mode when it comes to STL, because
+ // in STL the object is left in "valid but unspecified state" after move.
+ 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}}
+ V.push_back(123); // expected-warning{{Method called on moved-from object 'V'}}
+ // expected-note@-1{{Method called on moved-from object 'V'}}
+ }
+
+ std::unique_ptr<int> P;
+ void testUniquePtr() {
+ // unique_ptr remains in a well-defined state after move.
+ std::unique_ptr<int> Q = std::move(P);
+ P.get();
+#ifdef AGGRESSIVE
+ // expected-warning@-2{{Method called on moved-from object 'P'}}
+ // expected-note@-4{{Object 'P' is moved}}
+ // expected-note@-4{{Method called on moved-from object 'P'}}
+#endif
+
+ // Because that well-defined state is null, dereference is still UB.
+ // Note that in aggressive mode we already warned about 'P',
+ // so no extra warning is generated.
+ *P += 1;
+#ifndef AGGRESSIVE
+ // expected-warning@-2{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
+ // expected-note@-14{{Smart pointer 'P' of type 'std::unique_ptr' is reset to null when moved from}}
+ // expected-note@-4{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
+#endif
+
+ // The program should have crashed by now.
+ clang_analyzer_warnIfReached(); // no-warning
+ }
+};
+
+void localRValueMove(A &&a) {
+ A b = std::move(a);
+ a.foo();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'a' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'a'}}
+ // expected-note@-4 {{Method called on moved-from object 'a'}}
+#endif
+}
+
+void localUniquePtr(std::unique_ptr<int> P) {
+ // Even though unique_ptr is safe to use after move,
+ // reusing a local variable this way usually indicates a bug.
+ std::unique_ptr<int> Q = std::move(P);
+ P.get();
+#ifndef PEACEFUL
+ // expected-note@-3 {{Object 'P' is moved}}
+ // expected-warning@-3 {{Method called on moved-from object 'P'}}
+ // expected-note@-4 {{Method called on moved-from object 'P'}}
+#endif
+}
+
+void localUniquePtrWithArrow(std::unique_ptr<A> P) {
+ 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}}
+ P->foo(); // expected-warning{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
+ // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
+}