2018-12-17 14:30:39 +08:00
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
|
2018-12-04 06:32:32 +08:00
|
|
|
// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
|
2018-12-17 13:25:23 +08:00
|
|
|
// RUN: -analyzer-config exploration_strategy=unexplored_first_queue\
|
|
|
|
// RUN: -analyzer-checker debug.ExprInspection
|
2018-12-17 14:30:39 +08:00
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
|
2018-12-04 06:32:32 +08:00
|
|
|
// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
|
2018-12-17 13:25:23 +08:00
|
|
|
// RUN: -analyzer-config exploration_strategy=dfs -DDFS=1\
|
|
|
|
// RUN: -analyzer-checker debug.ExprInspection
|
2018-12-17 14:30:39 +08:00
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
|
2018-12-04 07:06:07 +08:00
|
|
|
// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
|
|
|
|
// RUN: -analyzer-config exploration_strategy=unexplored_first_queue\
|
2018-12-17 14:30:39 +08:00
|
|
|
// RUN: -analyzer-config cplusplus.Move:WarnOn=KnownsOnly -DPEACEFUL\
|
2018-12-17 13:25:23 +08:00
|
|
|
// RUN: -analyzer-checker debug.ExprInspection
|
2018-12-17 14:30:39 +08:00
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
|
2018-12-04 07:06:07 +08:00
|
|
|
// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
|
|
|
|
// RUN: -analyzer-config exploration_strategy=dfs -DDFS=1\
|
2018-12-17 14:30:39 +08:00
|
|
|
// RUN: -analyzer-config cplusplus.Move:WarnOn=KnownsOnly -DPEACEFUL\
|
2018-12-17 14:19:32 +08:00
|
|
|
// RUN: -analyzer-checker debug.ExprInspection
|
2018-12-17 14:30:39 +08:00
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
|
2018-12-17 14:19:32 +08:00
|
|
|
// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
|
|
|
|
// RUN: -analyzer-config exploration_strategy=unexplored_first_queue\
|
2018-12-17 14:30:39 +08:00
|
|
|
// RUN: -analyzer-config cplusplus.Move:WarnOn=All -DAGGRESSIVE\
|
2018-12-17 14:19:32 +08:00
|
|
|
// RUN: -analyzer-checker debug.ExprInspection
|
2018-12-17 14:30:39 +08:00
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move -verify %s\
|
2018-12-17 14:19:32 +08:00
|
|
|
// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\
|
|
|
|
// RUN: -analyzer-config exploration_strategy=dfs -DDFS=1\
|
2018-12-17 14:30:39 +08:00
|
|
|
// RUN: -analyzer-config cplusplus.Move:WarnOn=All -DAGGRESSIVE\
|
2018-12-17 13:25:23 +08:00
|
|
|
// RUN: -analyzer-checker debug.ExprInspection
|
2017-03-24 17:52:30 +08:00
|
|
|
|
2018-12-15 04:52:57 +08:00
|
|
|
#include "Inputs/system-header-simulator-cxx.h"
|
2017-03-24 17:52:30 +08:00
|
|
|
|
2018-12-17 13:25:23 +08:00
|
|
|
void clang_analyzer_warnIfReached();
|
|
|
|
|
2017-03-24 17:52:30 +08:00
|
|
|
class B {
|
|
|
|
public:
|
|
|
|
B() = default;
|
|
|
|
B(const B &) = default;
|
|
|
|
B(B &&) = default;
|
2017-10-29 07:24:00 +08:00
|
|
|
B& operator=(const B &q) = default;
|
2017-03-24 17:52:30 +08:00
|
|
|
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) {}
|
2018-12-04 07:06:07 +08:00
|
|
|
A(A &&other) : i(other.i), d(other.d), b(std::move(other.b)) {
|
|
|
|
#ifdef AGGRESSIVE
|
[analyzer] MoveChecker: Improve warning and note messages.
The warning piece traditionally describes the bug itself, i.e.
"The bug is a _____", eg. "Attempt to delete released memory",
"Resource leak", "Method call on a moved-from object".
Event pieces produced by the visitor are usually in a present tense, i.e.
"At this moment _____": "Memory is released", "File is closed",
"Object is moved".
Additionally, type information is added into the event pieces for STL objects
(in order to highlight that it is in fact an STL object), and the respective
event piece now mentions that the object is left in an unspecified state
after it was moved, which is a vital piece of information to understand the bug.
Differential Revision: https://reviews.llvm.org/D54560
llvm-svn: 348229
2018-12-04 10:00:29 +08:00
|
|
|
// expected-note@-2{{Object 'b' is moved}}
|
2018-12-04 07:06:07 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
A(A &&other, char *k) {
|
|
|
|
moveconstruct(std::move(other));
|
|
|
|
}
|
2017-10-29 07:24:00 +08:00
|
|
|
void operator=(const A &other) {
|
|
|
|
i = other.i;
|
|
|
|
d = other.d;
|
|
|
|
b = other.b;
|
|
|
|
return;
|
|
|
|
}
|
2017-03-24 17:52:30 +08:00
|
|
|
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();
|
2018-12-04 11:38:08 +08:00
|
|
|
void resize(std::size_t);
|
2017-03-24 17:52:30 +08:00
|
|
|
bool empty() const;
|
|
|
|
bool isEmpty() const;
|
|
|
|
operator bool() const;
|
2018-12-15 04:47:58 +08:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2017-03-24 17:52:30 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
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() {
|
2017-10-29 07:24:00 +08:00
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-10-29 07:24:00 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-10-29 07:24:00 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-10-29 07:24:00 +08:00
|
|
|
}
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void simpleMoveAssignementTest() {
|
2017-10-29 07:24:00 +08:00
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-10-29 07:24:00 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-10-29 07:24:00 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-10-29 07:24:00 +08:00
|
|
|
}
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void moveInInitListTest() {
|
|
|
|
struct S {
|
|
|
|
A a;
|
|
|
|
};
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
A b;
|
|
|
|
b = std::move(a);
|
|
|
|
a = A();
|
|
|
|
}
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
a.foo(); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
if (i == 1) {
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-2 {{Taking false branch}}
|
|
|
|
// expected-note@-3 {{Taking false branch}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
std::move(a);
|
|
|
|
}
|
2018-12-17 14:19:32 +08:00
|
|
|
if (i == 2) {
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-2 {{Taking false branch}}
|
|
|
|
// expected-note@-3 {{Taking false branch}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
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();
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
// If a path exist where we not reinitialize the variable we report a bug.
|
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
a = A();
|
|
|
|
}
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
rightRefCall(std::move(a)); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
rightRefCall(std::move(a)); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
leftRefCall(a); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
leftRefCall(a); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
constCopyOrMoveCall(a); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
constCopyOrMoveCall(a); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
moveInsideFunctionCall(a); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
moveInsideFunctionCall(a); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
copyOrMoveCall(a); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
copyOrMoveCall(a); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 14:19:32 +08:00
|
|
|
// Report a usage of a moved-from object only at the first use.
|
2017-03-24 17:52:30 +08:00
|
|
|
void uniqueTest(bool cond) {
|
|
|
|
A a(42, 42.0);
|
|
|
|
A b;
|
2018-12-17 14:19:32 +08:00
|
|
|
b = std::move(a);
|
2017-03-24 17:52:30 +08:00
|
|
|
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
if (cond) {
|
|
|
|
a.bar(); // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
a.bar(); // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
void uniqueTest2() {
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
|
|
|
|
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;
|
2018-12-17 14:19:32 +08:00
|
|
|
A b = std::move(a);
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-2 {{Object 'a' is moved}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
a.empty(); // no-warning
|
|
|
|
a.isEmpty(); // no-warning
|
|
|
|
(void)a; // no-warning
|
|
|
|
(bool)a; // expected-warning {{expression result unused}}
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void moveStateResetFunctionsTest() {
|
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b = std::move(a);
|
|
|
|
a.reset(); // no-warning
|
|
|
|
a.foo(); // no-warning
|
2017-10-29 07:09:37 +08:00
|
|
|
// Test if resets the state of subregions as well.
|
|
|
|
a.b.foo(); // no-warning
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
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
|
2017-10-29 07:09:37 +08:00
|
|
|
a.b.foo(); // no-warning
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
2018-12-04 11:38:08 +08:00
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b = std::move(a);
|
|
|
|
a.resize(0); // no-warning
|
|
|
|
a.foo(); // no-warning
|
|
|
|
a.b.foo(); // no-warning
|
|
|
|
}
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2018-12-04 07:06:07 +08:00
|
|
|
b = std::move(a);
|
|
|
|
a.foo();
|
|
|
|
#ifdef AGGRESSIVE
|
[analyzer] MoveChecker: Improve warning and note messages.
The warning piece traditionally describes the bug itself, i.e.
"The bug is a _____", eg. "Attempt to delete released memory",
"Resource leak", "Method call on a moved-from object".
Event pieces produced by the visitor are usually in a present tense, i.e.
"At this moment _____": "Memory is released", "File is closed",
"Object is moved".
Additionally, type information is added into the event pieces for STL objects
(in order to highlight that it is in fact an STL object), and the respective
event piece now mentions that the object is left in an unspecified state
after it was moved, which is a vital piece of information to understand the bug.
Differential Revision: https://reviews.llvm.org/D54560
llvm-svn: 348229
2018-12-04 10:00:29 +08:00
|
|
|
// 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'}}
|
2018-12-04 07:06:07 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
|
2018-12-04 07:06:07 +08:00
|
|
|
b = std::move(static_a);
|
|
|
|
static_a.foo();
|
|
|
|
#ifdef AGGRESSIVE
|
[analyzer] MoveChecker: Improve warning and note messages.
The warning piece traditionally describes the bug itself, i.e.
"The bug is a _____", eg. "Attempt to delete released memory",
"Resource leak", "Method call on a moved-from object".
Event pieces produced by the visitor are usually in a present tense, i.e.
"At this moment _____": "Memory is released", "File is closed",
"Object is moved".
Additionally, type information is added into the event pieces for STL objects
(in order to highlight that it is in fact an STL object), and the respective
event piece now mentions that the object is left in an unspecified state
after it was moved, which is a vital piece of information to understand the bug.
Differential Revision: https://reviews.llvm.org/D54560
llvm-svn: 348229
2018-12-04 10:00:29 +08:00
|
|
|
// 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'}}
|
2018-12-04 07:06:07 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void PtrAndArrayTest() {
|
|
|
|
A *Ptr = new A(1, 1.5);
|
|
|
|
A Arr[10];
|
2018-12-04 07:06:07 +08:00
|
|
|
Arr[2] = std::move(*Ptr);
|
|
|
|
(*Ptr).foo();
|
|
|
|
#ifdef AGGRESSIVE
|
[analyzer] MoveChecker: Improve warning and note messages.
The warning piece traditionally describes the bug itself, i.e.
"The bug is a _____", eg. "Attempt to delete released memory",
"Resource leak", "Method call on a moved-from object".
Event pieces produced by the visitor are usually in a present tense, i.e.
"At this moment _____": "Memory is released", "File is closed",
"Object is moved".
Additionally, type information is added into the event pieces for STL objects
(in order to highlight that it is in fact an STL object), and the respective
event piece now mentions that the object is left in an unspecified state
after it was moved, which is a vital piece of information to understand the bug.
Differential Revision: https://reviews.llvm.org/D54560
llvm-svn: 348229
2018-12-04 10:00:29 +08:00
|
|
|
// expected-note@-3{{Object is moved}}
|
|
|
|
// expected-warning@-3{{Method called on moved-from object}}
|
|
|
|
// expected-note@-4{{Method called on moved-from object}}
|
2018-12-04 07:06:07 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
|
|
|
|
Ptr = &Arr[1];
|
2018-12-04 07:06:07 +08:00
|
|
|
Arr[3] = std::move(Arr[1]);
|
|
|
|
Ptr->foo();
|
|
|
|
#ifdef AGGRESSIVE
|
[analyzer] MoveChecker: Improve warning and note messages.
The warning piece traditionally describes the bug itself, i.e.
"The bug is a _____", eg. "Attempt to delete released memory",
"Resource leak", "Method call on a moved-from object".
Event pieces produced by the visitor are usually in a present tense, i.e.
"At this moment _____": "Memory is released", "File is closed",
"Object is moved".
Additionally, type information is added into the event pieces for STL objects
(in order to highlight that it is in fact an STL object), and the respective
event piece now mentions that the object is left in an unspecified state
after it was moved, which is a vital piece of information to understand the bug.
Differential Revision: https://reviews.llvm.org/D54560
llvm-svn: 348229
2018-12-04 10:00:29 +08:00
|
|
|
// expected-note@-3{{Object is moved}}
|
|
|
|
// expected-warning@-3{{Method called on moved-from object}}
|
|
|
|
// expected-note@-4{{Method called on moved-from object}}
|
2018-12-04 07:06:07 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
|
2018-12-04 07:06:07 +08:00
|
|
|
Arr[3] = std::move(Arr[2]);
|
|
|
|
Arr[2].foo();
|
|
|
|
#ifdef AGGRESSIVE
|
[analyzer] MoveChecker: Improve warning and note messages.
The warning piece traditionally describes the bug itself, i.e.
"The bug is a _____", eg. "Attempt to delete released memory",
"Resource leak", "Method call on a moved-from object".
Event pieces produced by the visitor are usually in a present tense, i.e.
"At this moment _____": "Memory is released", "File is closed",
"Object is moved".
Additionally, type information is added into the event pieces for STL objects
(in order to highlight that it is in fact an STL object), and the respective
event piece now mentions that the object is left in an unspecified state
after it was moved, which is a vital piece of information to understand the bug.
Differential Revision: https://reviews.llvm.org/D54560
llvm-svn: 348229
2018-12-04 10:00:29 +08:00
|
|
|
// expected-note@-3{{Object is moved}}
|
|
|
|
// expected-warning@-3{{Method called on moved-from object}}
|
|
|
|
// expected-note@-4{{Method called on moved-from object}}
|
2018-12-04 07:06:07 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
|
|
|
|
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;
|
2018-12-17 14:19:32 +08:00
|
|
|
if (i > 0) {
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-2 {{Assuming 'i' is > 0}}
|
|
|
|
// expected-note@-3 {{Taking true branch}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
A b;
|
|
|
|
b = std::move(a);
|
|
|
|
} else {
|
|
|
|
a.foo(); // no-warning
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Same thing, but with a ternary operator.
|
|
|
|
{
|
|
|
|
A a, b;
|
2018-12-17 14:19:32 +08:00
|
|
|
i > 0 ? (void)(b = std::move(a)) : a.bar(); // no-warning
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-2 {{'?' condition is true}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
// A variation on the theme above.
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
a.foo() > 0 ? a.foo() : A(std::move(a)).foo();
|
2018-03-02 08:54:05 +08:00
|
|
|
#ifdef DFS
|
2018-12-17 14:19:32 +08:00
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-3 {{Assuming the condition is false}}
|
|
|
|
// expected-note@-4 {{'?' condition is false}}
|
|
|
|
#endif
|
2018-03-02 08:54:05 +08:00
|
|
|
#else
|
2018-12-17 14:19:32 +08:00
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-8 {{Assuming the condition is true}}
|
|
|
|
// expected-note@-9 {{'?' condition is true}}
|
|
|
|
#endif
|
2018-03-02 08:54:05 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
// Same thing, but with a switch statement.
|
|
|
|
{
|
|
|
|
A a, b;
|
2018-12-17 14:19:32 +08:00
|
|
|
switch (i) {
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-2 {{Control jumps to 'case 1:'}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
case 1:
|
|
|
|
b = std::move(a); // no-warning
|
2018-12-17 14:19:32 +08:00
|
|
|
break;
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-2 {{Execution jumps to the end of the function}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
case 2:
|
|
|
|
a.foo(); // no-warning
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// However, if there's a fallthrough, we do warn.
|
|
|
|
{
|
|
|
|
A a, b;
|
2018-12-17 14:19:32 +08:00
|
|
|
switch (i) {
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-2 {{Control jumps to 'case 1:'}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
case 1:
|
2018-12-17 14:19:32 +08:00
|
|
|
b = std::move(a);
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-2 {{Object 'a' is moved}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
case 2:
|
2018-12-17 14:19:32 +08:00
|
|
|
a.foo();
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-warning@-2 {{Method called on moved-from object}}
|
|
|
|
// expected-note@-3 {{Method called on moved-from object 'a'}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
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) {
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void interFunTest2() {
|
|
|
|
A a;
|
|
|
|
A b;
|
2018-12-17 14:19:32 +08:00
|
|
|
b = std::move(a);
|
|
|
|
interFunTest1(a);
|
|
|
|
#ifndef PEACEFUL
|
|
|
|
// expected-note@-3 {{Object 'a' is moved}}
|
|
|
|
// expected-note@-3 {{Calling 'interFunTest1'}}
|
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void foobar(A a, int i);
|
|
|
|
void foobar(int i, A a);
|
|
|
|
|
|
|
|
void paramEvaluateOrderTest() {
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
|
|
|
|
//FALSE NEGATIVE since parameters evaluate order is undefined
|
|
|
|
foobar(a.getI(), std::move(a)); //no-warning
|
|
|
|
}
|
|
|
|
|
2018-12-15 04:47:58 +08:00
|
|
|
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);
|
2017-03-24 17:52:30 +08:00
|
|
|
|
|
|
|
void regionAndPointerEscapeTest() {
|
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b;
|
|
|
|
b = std::move(a);
|
2018-12-15 04:47:58 +08:00
|
|
|
not_known_pass_by_ref(a);
|
|
|
|
a.foo(); // no-warning
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b;
|
2018-12-17 14:19:32 +08:00
|
|
|
b = std::move(a);
|
2018-12-15 04:47:58 +08:00
|
|
|
not_known_pass_by_const_ref(a);
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2018-12-15 04:47:58 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b;
|
|
|
|
b = std::move(a);
|
|
|
|
not_known_pass_by_rvalue_ref(std::move(a));
|
|
|
|
a.foo(); // no-warning
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b;
|
|
|
|
b = std::move(a);
|
2018-12-15 04:47:58 +08:00
|
|
|
not_known_pass_by_ptr(&a);
|
2017-03-24 17:52:30 +08:00
|
|
|
a.foo(); // no-warning
|
|
|
|
}
|
2018-12-15 04:47:58 +08:00
|
|
|
{
|
|
|
|
A a;
|
|
|
|
A b;
|
2018-12-17 14:19:32 +08:00
|
|
|
b = std::move(a);
|
2018-12-15 04:47:58 +08:00
|
|
|
not_known_pass_by_const_ptr(&a);
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2018-12-15 04:47:58 +08:00
|
|
|
}
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The logical operators && and || sequence their operands.
|
|
|
|
void logicalOperatorsSequenceTest() {
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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}}
|
2017-03-24 17:52:30 +08:00
|
|
|
A().bar();
|
2018-12-17 14:19:32 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// A variation: Negate the result of the && (which pushes the && further down
|
|
|
|
// into the AST).
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
A().bar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
A().bar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
A().bar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-09 15:28:57 +08:00
|
|
|
struct C : public A {
|
|
|
|
[[clang::reinitializes]] void reinit();
|
|
|
|
};
|
|
|
|
|
2017-03-24 17:52:30 +08:00
|
|
|
void subRegionMoveTest() {
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-04 07:06:07 +08:00
|
|
|
B b = std::move(a.b);
|
|
|
|
a.b.foo();
|
|
|
|
#ifdef AGGRESSIVE
|
[analyzer] MoveChecker: Improve warning and note messages.
The warning piece traditionally describes the bug itself, i.e.
"The bug is a _____", eg. "Attempt to delete released memory",
"Resource leak", "Method call on a moved-from object".
Event pieces produced by the visitor are usually in a present tense, i.e.
"At this moment _____": "Memory is released", "File is closed",
"Object is moved".
Additionally, type information is added into the event pieces for STL objects
(in order to highlight that it is in fact an STL object), and the respective
event piece now mentions that the object is left in an unspecified state
after it was moved, which is a vital piece of information to understand the bug.
Differential Revision: https://reviews.llvm.org/D54560
llvm-svn: 348229
2018-12-04 10:00:29 +08:00
|
|
|
// 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'}}
|
2018-12-04 07:06:07 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-04 07:06:07 +08:00
|
|
|
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'}}
|
[analyzer] MoveChecker: Improve warning and note messages.
The warning piece traditionally describes the bug itself, i.e.
"The bug is a _____", eg. "Attempt to delete released memory",
"Resource leak", "Method call on a moved-from object".
Event pieces produced by the visitor are usually in a present tense, i.e.
"At this moment _____": "Memory is released", "File is closed",
"Object is moved".
Additionally, type information is added into the event pieces for STL objects
(in order to highlight that it is in fact an STL object), and the respective
event piece now mentions that the object is left in an unspecified state
after it was moved, which is a vital piece of information to understand the bug.
Differential Revision: https://reviews.llvm.org/D54560
llvm-svn: 348229
2018-12-04 10:00:29 +08:00
|
|
|
// expected-warning@-4{{Method called on moved-from object 'b'}}
|
|
|
|
// expected-note@-5{{Method called on moved-from object 'b'}}
|
2018-12-04 07:06:07 +08:00
|
|
|
#endif
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
|
|
|
// Don't report a misuse if any SuperRegion is already reported.
|
|
|
|
{
|
|
|
|
A a;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-03-24 17:52:30 +08:00
|
|
|
a.b.foo(); // no-warning
|
|
|
|
}
|
2017-10-29 07:09:37 +08:00
|
|
|
{
|
|
|
|
C c;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-10-29 07:09:37 +08:00
|
|
|
c.b.foo(); // no-warning
|
|
|
|
}
|
2017-03-24 17:52:30 +08:00
|
|
|
}
|
2017-10-10 19:55:56 +08:00
|
|
|
|
|
|
|
void resetSuperClass() {
|
|
|
|
C c;
|
|
|
|
C c1 = std::move(c);
|
|
|
|
c.clear();
|
|
|
|
C c2 = c; // no-warning
|
|
|
|
}
|
2017-10-29 07:09:37 +08:00
|
|
|
|
2018-10-09 15:28:57 +08:00
|
|
|
void resetSuperClass2() {
|
|
|
|
C c;
|
|
|
|
C c1 = std::move(c);
|
|
|
|
c.reinit();
|
|
|
|
C c2 = c; // no-warning
|
|
|
|
}
|
|
|
|
|
2017-10-29 07:09:37 +08:00
|
|
|
void reportSuperClass() {
|
|
|
|
C c;
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2017-10-29 07:09:37 +08:00
|
|
|
C c2 = c; // no-warning
|
|
|
|
}
|
2018-11-30 11:27:50 +08:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2018-12-04 06:44:16 +08:00
|
|
|
|
2018-12-17 07:44:06 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-04 06:44:16 +08:00
|
|
|
struct MoveOnlyWithDestructor {
|
|
|
|
MoveOnlyWithDestructor();
|
|
|
|
~MoveOnlyWithDestructor();
|
|
|
|
MoveOnlyWithDestructor(const MoveOnlyWithDestructor &m) = delete;
|
|
|
|
MoveOnlyWithDestructor(MoveOnlyWithDestructor &&m);
|
|
|
|
};
|
|
|
|
|
|
|
|
MoveOnlyWithDestructor foo() {
|
|
|
|
MoveOnlyWithDestructor m;
|
|
|
|
return m;
|
|
|
|
}
|
2018-12-04 07:06:07 +08:00
|
|
|
|
|
|
|
class HasSTLField {
|
|
|
|
std::vector<int> V;
|
2018-12-15 04:52:57 +08:00
|
|
|
void testVector() {
|
2018-12-04 07:06:07 +08:00
|
|
|
// 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.
|
[analyzer] MoveChecker: Improve warning and note messages.
The warning piece traditionally describes the bug itself, i.e.
"The bug is a _____", eg. "Attempt to delete released memory",
"Resource leak", "Method call on a moved-from object".
Event pieces produced by the visitor are usually in a present tense, i.e.
"At this moment _____": "Memory is released", "File is closed",
"Object is moved".
Additionally, type information is added into the event pieces for STL objects
(in order to highlight that it is in fact an STL object), and the respective
event piece now mentions that the object is left in an unspecified state
after it was moved, which is a vital piece of information to understand the bug.
Differential Revision: https://reviews.llvm.org/D54560
llvm-svn: 348229
2018-12-04 10:00:29 +08:00
|
|
|
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'}}
|
2018-12-04 07:06:07 +08:00
|
|
|
}
|
2018-12-15 04:52:57 +08:00
|
|
|
|
|
|
|
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
|
2018-12-17 13:25:23 +08:00
|
|
|
|
|
|
|
// 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
|
2018-12-15 04:52:57 +08:00
|
|
|
}
|
2018-12-04 07:06:07 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
void localRValueMove(A &&a) {
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2018-12-04 07:06:07 +08:00
|
|
|
}
|
2018-12-15 04:52:57 +08:00
|
|
|
|
|
|
|
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.
|
2018-12-17 14:19:32 +08:00
|
|
|
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
|
2018-12-15 04:52:57 +08:00
|
|
|
}
|
2018-12-17 13:25:23 +08:00
|
|
|
|
|
|
|
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'}}
|
|
|
|
}
|