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