forked from OSchip/llvm-project
[analyzer] MoveChecker: Add an option to suppress warnings on locals.
Re-using a moved-from local variable is most likely a bug because there's rarely a good motivation for not introducing a separate variable instead. We plan to keep emitting such warnings by default. Introduce a flag that allows disabling warnings on local variables that are not of a known move-unsafe type. If it doesn't work out as we expected, we'll just flip the flag. We still warn on move-unsafe objects and unsafe operations on known move-safe objects. Differential Revision: https://reviews.llvm.org/D55730 llvm-svn: 349327
This commit is contained in:
parent
69909540a7
commit
2b500cbdf1
clang
|
@ -26,7 +26,6 @@ using namespace clang;
|
|||
using namespace ento;
|
||||
|
||||
namespace {
|
||||
|
||||
struct RegionState {
|
||||
private:
|
||||
enum Kind { Moved, Reported } K;
|
||||
|
@ -42,7 +41,9 @@ public:
|
|||
bool operator==(const RegionState &X) const { return K == X.K; }
|
||||
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
|
||||
};
|
||||
} // end of anonymous namespace
|
||||
|
||||
namespace {
|
||||
class MoveChecker
|
||||
: public Checker<check::PreCall, check::PostCall,
|
||||
check::DeadSymbols, check::RegionChanges> {
|
||||
|
@ -62,8 +63,18 @@ public:
|
|||
|
||||
private:
|
||||
enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
|
||||
// This needs to be unsigned in order to avoid undefined behavior
|
||||
// when putting it into a tight bitfield.
|
||||
enum StdObjectKind : unsigned { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
|
||||
|
||||
enum AggressivenessKind { // In any case, don't warn after a reset.
|
||||
AK_Invalid = -1,
|
||||
AK_KnownsOnly = 0, // Warn only about known move-unsafe classes.
|
||||
AK_KnownsAndLocals = 1, // Also warn about all local objects.
|
||||
AK_All = 2, // Warn on any use-after-move.
|
||||
AK_NumKinds = AK_All
|
||||
};
|
||||
|
||||
static bool misuseCausesCrash(MisuseKind MK) {
|
||||
return MK == MK_Dereference;
|
||||
}
|
||||
|
@ -117,8 +128,9 @@ private:
|
|||
// In aggressive mode, warn on any use-after-move because the user has
|
||||
// intentionally asked us to completely eliminate use-after-move
|
||||
// in his code.
|
||||
return IsAggressive || OK.IsLocal
|
||||
|| OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
|
||||
return (Aggressiveness == AK_All) ||
|
||||
(Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
|
||||
OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
|
||||
}
|
||||
|
||||
// Some objects only suffer from some kinds of misuses, but we need to track
|
||||
|
@ -127,8 +139,9 @@ private:
|
|||
// Additionally, only warn on smart pointers when they are dereferenced (or
|
||||
// local or we are aggressive).
|
||||
return shouldBeTracked(OK) &&
|
||||
(IsAggressive || OK.IsLocal
|
||||
|| OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
|
||||
((Aggressiveness == AK_All) ||
|
||||
(Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
|
||||
OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
|
||||
}
|
||||
|
||||
// Obtains ObjectKind of an object. Because class declaration cannot always
|
||||
|
@ -173,10 +186,17 @@ private:
|
|||
bool Found;
|
||||
};
|
||||
|
||||
bool IsAggressive = false;
|
||||
AggressivenessKind Aggressiveness;
|
||||
|
||||
public:
|
||||
void setAggressiveness(bool Aggressive) { IsAggressive = Aggressive; }
|
||||
void setAggressiveness(StringRef Str) {
|
||||
Aggressiveness =
|
||||
llvm::StringSwitch<AggressivenessKind>(Str)
|
||||
.Case("KnownsOnly", AK_KnownsOnly)
|
||||
.Case("KnownsAndLocals", AK_KnownsAndLocals)
|
||||
.Case("All", AK_All)
|
||||
.Default(AK_KnownsAndLocals); // A sane default.
|
||||
};
|
||||
|
||||
private:
|
||||
mutable std::unique_ptr<BugType> BT;
|
||||
|
@ -717,6 +737,6 @@ void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State,
|
|||
}
|
||||
void ento::registerMoveChecker(CheckerManager &mgr) {
|
||||
MoveChecker *chk = mgr.registerChecker<MoveChecker>();
|
||||
chk->setAggressiveness(mgr.getAnalyzerOptions().getCheckerBooleanOption(
|
||||
"Aggressive", false, chk));
|
||||
chk->setAggressiveness(
|
||||
mgr.getAnalyzerOptions().getCheckerStringOption("WarnOn", "", chk));
|
||||
}
|
||||
|
|
|
@ -9,12 +9,22 @@
|
|||
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.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 alpha.cplusplus.Move:Aggressive=true -DAGGRESSIVE\
|
||||
// RUN: -analyzer-config alpha.cplusplus.Move:WarnOn=KnownsOnly -DPEACEFUL\
|
||||
// RUN: -analyzer-checker debug.ExprInspection
|
||||
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.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 alpha.cplusplus.Move:Aggressive=true -DAGGRESSIVE\
|
||||
// RUN: -analyzer-config alpha.cplusplus.Move:WarnOn=KnownsOnly -DPEACEFUL\
|
||||
// RUN: -analyzer-checker debug.ExprInspection
|
||||
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.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 alpha.cplusplus.Move:WarnOn=All -DAGGRESSIVE\
|
||||
// RUN: -analyzer-checker debug.ExprInspection
|
||||
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.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 alpha.cplusplus.Move:WarnOn=All -DAGGRESSIVE\
|
||||
// RUN: -analyzer-checker debug.ExprInspection
|
||||
|
||||
#include "Inputs/system-header-simulator-cxx.h"
|
||||
|
@ -119,18 +129,33 @@ void copyOrMoveCall(A a) {
|
|||
void simpleMoveCtorTest() {
|
||||
{
|
||||
A a;
|
||||
A b = std::move(a); // expected-note {{Object 'a' is moved}}
|
||||
a.foo(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object '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); // expected-note {{Object 'a' is moved}}
|
||||
b = a; // expected-warning {{Moved-from object 'a' is copied}} expected-note {{Moved-from object 'a' is copied}}
|
||||
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); // expected-note {{Object 'a' is moved}}
|
||||
b = std::move(a); // expected-warning {{Moved-from object 'a' is moved}} expected-note {{Moved-from object 'a' is moved}}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,20 +163,35 @@ void simpleMoveAssignementTest() {
|
|||
{
|
||||
A a;
|
||||
A b;
|
||||
b = std::move(a); // expected-note {{Object 'a' is moved}}
|
||||
a.foo(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object '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;
|
||||
b = std::move(a); // expected-note {{Object 'a' is moved}}
|
||||
A c(a); // expected-warning {{Moved-from object 'a' is copied}} expected-note {{Moved-from object 'a' is copied}}
|
||||
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); // expected-note {{Object 'a' is moved}}
|
||||
A c(std::move(a)); // expected-warning {{Moved-from object 'a' is moved}} expected-note {{Moved-from object 'a' is moved}}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,8 +200,13 @@ void moveInInitListTest() {
|
|||
A a;
|
||||
};
|
||||
A a;
|
||||
S s{std::move(a)}; // expected-note {{Object 'a' is moved}}
|
||||
a.foo(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object '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.
|
||||
|
@ -175,23 +220,43 @@ void reinitializationTest(int i) {
|
|||
}
|
||||
{
|
||||
A a;
|
||||
if (i == 1) { // expected-note {{Assuming 'i' is not equal to 1}} expected-note {{Taking false branch}}
|
||||
// expected-note@-1 {{Assuming 'i' is not equal to 1}} expected-note@-1 {{Taking false branch}}
|
||||
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) { // expected-note {{Assuming 'i' is not equal to 2}} expected-note {{Taking false branch}}
|
||||
//expected-note@-1 {{Assuming 'i' is not equal to 2}} expected-note@-1 {{Taking false branch}}
|
||||
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) { // expected-note {{Taking false branch}} expected-note {{Taking false branch}}
|
||||
if (i == 1) {
|
||||
#ifndef PEACEFUL
|
||||
// expected-note@-2 {{Taking false branch}}
|
||||
// expected-note@-3 {{Taking false branch}}
|
||||
#endif
|
||||
std::move(a);
|
||||
}
|
||||
if (i == 2) { // expected-note {{Taking false branch}} expected-note {{Taking false branch}}
|
||||
if (i == 2) {
|
||||
#ifndef PEACEFUL
|
||||
// expected-note@-2 {{Taking false branch}}
|
||||
// expected-note@-3 {{Taking false branch}}
|
||||
#endif
|
||||
a = A();
|
||||
a.foo();
|
||||
}
|
||||
|
@ -209,19 +274,36 @@ void reinitializationTest(int i) {
|
|||
A b;
|
||||
b = std::move(a);
|
||||
a = A();
|
||||
b = std::move(a); // expected-note {{Object 'a' is moved}}
|
||||
a.foo(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object '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); // expected-note {{Object 'a' is moved}}
|
||||
if (i < 10) { // expected-note {{Assuming 'i' is >= 10}} expected-note {{Taking false branch}}
|
||||
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) { // expected-note {{Taking true branch}}
|
||||
a.foo(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object '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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,79 +318,117 @@ void decltypeIsNotUseTest() {
|
|||
void loopTest() {
|
||||
{
|
||||
A a;
|
||||
for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is true. Entering loop body}}
|
||||
//expected-note@-1 {{Loop condition is true. Entering loop body}}
|
||||
//expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is true. Entering loop body}}
|
||||
//expected-note@-1 {{Loop condition is true. Entering loop body}}
|
||||
//expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is true. Entering loop body}}
|
||||
//expected-note@-1 {{Loop condition is true. Entering loop body}}
|
||||
//expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is true. Entering loop body}}
|
||||
//expected-note@-1 {{Loop condition is true. Entering loop body}}
|
||||
//expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is true.}}
|
||||
//expected-note@-1 {{Loop condition is true. Entering loop body}}
|
||||
//expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}}
|
||||
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++) { // expected-note {{Loop condition is true. Entering loop body}} expected-note {{Loop condition is true. Entering loop body}}
|
||||
constCopyOrMoveCall(std::move(a)); // expected-warning {{Moved-from object 'a' is moved}} expected-note {{Moved-from object 'a' is moved}}
|
||||
// expected-note@-1 {{Object 'a' is moved}}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,14 +446,21 @@ void loopTest() {
|
|||
}
|
||||
}
|
||||
|
||||
//report a usage of a moved-from object only at the first use
|
||||
// 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); // expected-note {{Object 'a' is moved}}
|
||||
b = std::move(a);
|
||||
|
||||
if (cond) { // expected-note {{Assuming 'cond' is not equal to 0}} expected-note {{Taking true branch}}
|
||||
a.foo(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object '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
|
||||
|
@ -344,8 +471,13 @@ void uniqueTest(bool cond) {
|
|||
|
||||
void uniqueTest2() {
|
||||
A a;
|
||||
A a1 = std::move(a); // expected-note {{Object 'a' is moved}}
|
||||
a.foo(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object '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
|
||||
|
@ -355,12 +487,19 @@ void uniqueTest2() {
|
|||
//even on moved-from objects.
|
||||
void moveSafeFunctionsTest() {
|
||||
A a;
|
||||
A b = std::move(a); // expected-note {{Object 'a' is moved}}
|
||||
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(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object 'a'}}
|
||||
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() {
|
||||
|
@ -496,7 +635,11 @@ void differentBranchesTest(int i) {
|
|||
// Don't warn if the use is in a different branch from the move.
|
||||
{
|
||||
A a;
|
||||
if (i > 0) { // expected-note {{Assuming 'i' is > 0}} expected-note {{Taking true branch}}
|
||||
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 {
|
||||
|
@ -506,24 +649,40 @@ void differentBranchesTest(int i) {
|
|||
// Same thing, but with a ternary operator.
|
||||
{
|
||||
A a, b;
|
||||
i > 0 ? (void)(b = std::move(a)) : a.bar(); // no-warning // expected-note {{'?' condition is true}}
|
||||
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
|
||||
a.foo() > 0 ? a.foo() : A(std::move(a)).foo(); // expected-note {{Assuming the condition is false}} expected-note {{'?' condition is false}}
|
||||
#ifndef PEACEFUL
|
||||
// expected-note@-3 {{Assuming the condition is false}}
|
||||
// expected-note@-4 {{'?' condition is false}}
|
||||
#endif
|
||||
#else
|
||||
a.foo() > 0 ? a.foo() : A(std::move(a)).foo(); // expected-note {{Assuming the condition is true}} expected-note {{'?' condition is true}}
|
||||
#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) { // expected-note {{Control jumps to 'case 1:'}}
|
||||
switch (i) {
|
||||
#ifndef PEACEFUL
|
||||
// expected-note@-2 {{Control jumps to 'case 1:'}}
|
||||
#endif
|
||||
case 1:
|
||||
b = std::move(a); // no-warning
|
||||
break; // expected-note {{Execution jumps to the end of the function}}
|
||||
break;
|
||||
#ifndef PEACEFUL
|
||||
// expected-note@-2 {{Execution jumps to the end of the function}}
|
||||
#endif
|
||||
case 2:
|
||||
a.foo(); // no-warning
|
||||
break;
|
||||
|
@ -532,11 +691,21 @@ void differentBranchesTest(int i) {
|
|||
// However, if there's a fallthrough, we do warn.
|
||||
{
|
||||
A a, b;
|
||||
switch (i) { // expected-note {{Control jumps to 'case 1:'}}
|
||||
switch (i) {
|
||||
#ifndef PEACEFUL
|
||||
// expected-note@-2 {{Control jumps to 'case 1:'}}
|
||||
#endif
|
||||
case 1:
|
||||
b = std::move(a); // expected-note {{Object 'a' is moved}}
|
||||
b = std::move(a);
|
||||
#ifndef PEACEFUL
|
||||
// expected-note@-2 {{Object 'a' is moved}}
|
||||
#endif
|
||||
case 2:
|
||||
a.foo(); // expected-warning {{Method called on moved-from object}} expected-note {{Method called on moved-from object 'a'}}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -551,14 +720,22 @@ void tempTest() {
|
|||
}
|
||||
|
||||
void interFunTest1(A &a) {
|
||||
a.bar(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object '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); // expected-note {{Object 'a' is moved}}
|
||||
interFunTest1(a); // expected-note {{Calling 'interFunTest1'}}
|
||||
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);
|
||||
|
@ -566,8 +743,12 @@ void foobar(int i, A a);
|
|||
|
||||
void paramEvaluateOrderTest() {
|
||||
A a;
|
||||
foobar(std::move(a), a.getI()); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object 'a'}}
|
||||
// expected-note@-1 {{Object 'a' is moved}}
|
||||
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
|
||||
|
@ -590,10 +771,14 @@ void regionAndPointerEscapeTest() {
|
|||
{
|
||||
A a;
|
||||
A b;
|
||||
b = std::move(a); // expected-note{{Object 'a' is moved}}
|
||||
b = std::move(a);
|
||||
not_known_pass_by_const_ref(a);
|
||||
a.foo(); // expected-warning{{Method called on moved-from object 'a'}}
|
||||
// expected-note@-1{{Method called on moved-from object '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;
|
||||
|
@ -612,10 +797,14 @@ void regionAndPointerEscapeTest() {
|
|||
{
|
||||
A a;
|
||||
A b;
|
||||
b = std::move(a); // expected-note{{Object 'a' is moved}}
|
||||
b = std::move(a);
|
||||
not_known_pass_by_const_ptr(&a);
|
||||
a.foo(); // expected-warning{{Method called on moved-from object 'a'}}
|
||||
// expected-note@-1{{Method called on moved-from object '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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -628,8 +817,12 @@ void declarationSequenceTest() {
|
|||
}
|
||||
{
|
||||
A a;
|
||||
A a1 = std::move(a), a2 = a; // expected-warning {{Moved-from object 'a' is copied}} expected-note {{Moved-from object 'a' is copied}}
|
||||
// expected-note@-1 {{Object 'a' is moved}}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,43 +830,74 @@ void declarationSequenceTest() {
|
|||
void logicalOperatorsSequenceTest() {
|
||||
{
|
||||
A a;
|
||||
if (a.foo() > 0 && A(std::move(a)).foo() > 0) { // expected-note {{Assuming the condition is false}} expected-note {{Assuming the condition is false}}
|
||||
// expected-note@-1 {{Left side of '&&' is false}} expected-note@-1 {{Left side of '&&' is false}}
|
||||
//expected-note@-2 {{Taking false branch}} expected-note@-2 {{Taking false branch}}
|
||||
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)) { // expected-note {{Assuming the condition is false}} expected-note {{Assuming the condition is false}}
|
||||
// expected-note@-1 {{Left side of '&&' is false}} expected-note@-1 {{Left side of '&&' is false}}
|
||||
// expected-note@-2 {{Taking true branch}} expected-note@-2 {{Taking true branch}}
|
||||
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) { // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object 'a'}}
|
||||
// expected-note@-1 {{Object 'a' is moved}} expected-note@-1 {{Assuming the condition is true}} expected-note@-1 {{Assuming the condition is false}}
|
||||
// expected-note@-2 {{Left side of '&&' is false}} expected-note@-2 {{Left side of '&&' is true}}
|
||||
// expected-note@-3 {{Taking false branch}}
|
||||
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) { // expected-note {{Assuming the condition is true}}
|
||||
//expected-note@-1 {{Left side of '||' is true}}
|
||||
//expected-note@-2 {{Taking true branch}}
|
||||
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) { // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object 'a'}}
|
||||
// expected-note@-1 {{Object 'a' is moved}} expected-note@-1 {{Assuming the condition is false}} expected-note@-1 {{Left side of '||' is false}}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -729,14 +953,24 @@ void subRegionMoveTest() {
|
|||
// Don't report a misuse if any SuperRegion is already reported.
|
||||
{
|
||||
A a;
|
||||
A a1 = std::move(a); // expected-note {{Object 'a' is moved}}
|
||||
a.foo(); // expected-warning {{Method called on moved-from object 'a'}} expected-note {{Method called on moved-from object '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); // expected-note {{Object 'c' is moved}}
|
||||
c.foo(); // expected-warning {{Method called on moved-from object 'c'}} expected-note {{Method called on moved-from object '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
|
||||
}
|
||||
}
|
||||
|
@ -757,8 +991,13 @@ void resetSuperClass2() {
|
|||
|
||||
void reportSuperClass() {
|
||||
C c;
|
||||
C c1 = std::move(c); // expected-note {{Object 'c' is moved}}
|
||||
c.foo(); // expected-warning {{Method called on moved-from object 'c'}} expected-note {{Method called on moved-from object '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
|
||||
}
|
||||
|
||||
|
@ -872,17 +1111,25 @@ class HasSTLField {
|
|||
};
|
||||
|
||||
void localRValueMove(A &&a) {
|
||||
A b = std::move(a); // expected-note{{Object 'a' is moved}}
|
||||
a.foo(); // expected-warning{{Method called on moved-from object 'a'}}
|
||||
// expected-note@-1{{Method called on moved-from object '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); // expected-note{{Object 'P' is moved}}
|
||||
P.get(); // expected-warning{{Method called on moved-from object 'P'}}
|
||||
// expected-note@-1{{Method called on moved-from object 'P'}}
|
||||
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) {
|
||||
|
|
Loading…
Reference in New Issue