diff options
Diffstat (limited to 'test/Analysis/use-after-move.cpp')
-rw-r--r-- | test/Analysis/use-after-move.cpp | 1139 |
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'}} +} |