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