1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
// RUN: %check_clang_tidy %s cert-oop58-cpp %t
// Example test cases from CERT rule
// https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP58-CPP.+Copy+operations+must+not+mutate+the+source+object
namespace test_mutating_noncompliant_example {
class A {
mutable int m;
public:
A() : m(0) {}
explicit A(int m) : m(m) {}
A(const A &other) : m(other.m) {
other.m = 0;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: mutating copied object
}
A &operator=(const A &other) {
if (&other != this) {
m = other.m;
other.m = 0;
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: mutating copied object
}
return *this;
}
int get_m() const { return m; }
};
} // namespace test_mutating_noncompliant_example
namespace test_mutating_compliant_example {
class B {
int m;
public:
B() : m(0) {}
explicit B(int m) : m(m) {}
B(const B &other) : m(other.m) {}
B(B &&other) : m(other.m) {
other.m = 0; //no-warning: mutation allowed in move constructor
}
B &operator=(const B &other) {
if (&other != this) {
m = other.m;
}
return *this;
}
B &operator=(B &&other) {
m = other.m;
other.m = 0; //no-warning: mutation allowed in move assignment operator
return *this;
}
int get_m() const { return m; }
};
} // namespace test_mutating_compliant_example
namespace test_mutating_pointer {
class C {
C *ptr;
int value;
C();
C(C &other) {
other = {};
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: mutating copied object
other.ptr = nullptr;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: mutating copied object
other.value = 0;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: mutating copied object
// no-warning: mutating a pointee is allowed
other.ptr->value = 0;
*other.ptr = {};
}
};
} // namespace test_mutating_pointer
namespace test_mutating_indirect_member {
struct S {
int x;
};
class D {
S s;
D(D &other) {
other.s = {};
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: mutating copied object
other.s.x = 0;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: mutating copied object
}
};
} // namespace test_mutating_indirect_member
namespace test_mutating_other_object {
class E {
E();
E(E &other) {
E tmp;
// no-warning: mutating an object that is not the source is allowed
tmp = {};
}
};
} // namespace test_mutating_other_object
namespace test_mutating_member_function {
class F {
int a;
public:
void bad_func() { a = 12; }
void fine_func() const;
void fine_func_2(int x) { x = 5; }
void questionable_func();
F(F &other) : a(other.a) {
this->bad_func(); // no-warning: mutating this is allowed
other.bad_func();
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: call mutates copied object
other.fine_func();
other.fine_func_2(42);
other.questionable_func();
}
};
} // namespace test_mutating_member_function
namespace test_mutating_function_on_nested_object {
struct S {
int x;
void mutate(int y) {
x = y;
}
};
class G {
S s;
G(G &other) {
s.mutate(0); // no-warning: mutating this is allowed
other.s.mutate(0);
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: call mutates copied object
}
};
} // namespace test_mutating_function_on_nested_object
|