1 // RUN: %check_clang_tidy %s cppcoreguidelines-owning-memory %t
2
3 namespace gsl {
4 template <class T>
5 using owner = T;
6 } // namespace gsl
7
8 template <typename T>
9 class unique_ptr {
10 public:
unique_ptr(gsl::owner<T> resource)11 unique_ptr(gsl::owner<T> resource) : memory(resource) {}
12 unique_ptr(const unique_ptr<T> &) = default;
13
~unique_ptr()14 ~unique_ptr() { delete memory; }
15
16 private:
17 gsl::owner<T> memory;
18 };
19
takes_owner(gsl::owner<int * > owned_int)20 void takes_owner(gsl::owner<int *> owned_int) {
21 }
22
takes_pointer(int * unowned_int)23 void takes_pointer(int *unowned_int) {
24 }
25
takes_owner_and_more(int some_int,gsl::owner<int * > owned_int,float f)26 void takes_owner_and_more(int some_int, gsl::owner<int *> owned_int, float f) {
27 }
28
29 template <typename T>
takes_templated_owner(gsl::owner<T> owned_T)30 void takes_templated_owner(gsl::owner<T> owned_T) {
31 }
32
returns_owner1()33 gsl::owner<int *> returns_owner1() { return gsl::owner<int *>(new int(42)); } // Ok
returns_owner2()34 gsl::owner<int *> returns_owner2() { return new int(42); } // Ok
35
returns_no_owner1()36 int *returns_no_owner1() { return nullptr; }
returns_no_owner2()37 int *returns_no_owner2() {
38 return new int(42);
39 // CHECK-NOTES: [[@LINE-1]]:3: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
40 }
returns_no_owner3()41 int *returns_no_owner3() {
42 int *should_be_owner = new int(42);
43 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
44 return should_be_owner;
45 }
returns_no_owner4()46 int *returns_no_owner4() {
47 gsl::owner<int *> owner = new int(42);
48 return owner;
49 // CHECK-NOTES: [[@LINE-1]]:3: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
50 }
51
returns_no_owner5()52 unique_ptr<int *> returns_no_owner5() {
53 return unique_ptr<int *>(new int(42)); // Ok
54 }
55
56 /// FIXME: CSA finds it, but the report is misleading. Ownersemantics can catch this
57 /// by flow analysis similar to bugprone-use-after-move.
csa_not_finding_leak()58 void csa_not_finding_leak() {
59 gsl::owner<int *> o1 = new int(42); // Ok
60
61 gsl::owner<int *> o2 = o1; // Ok
62 o2 = new int(45); // conceptual leak, the memory from o1 is now leaked, since its considered moved in the guidelines
63
64 delete o2;
65 // actual leak occurs here, its found, but mixed
66 delete o1;
67 }
68
test_assignment_and_initialization()69 void test_assignment_and_initialization() {
70 int stack_int1 = 15;
71 int stack_int2;
72
73 gsl::owner<int *> owned_int1 = &stack_int1; // BAD
74 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
75
76 gsl::owner<int *> owned_int2;
77 owned_int2 = &stack_int2; // BAD since no owner, bad since uninitialized
78 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *'
79
80 gsl::owner<int *> owned_int3 = new int(42); // Good
81 owned_int3 = nullptr; // Good
82
83 gsl::owner<int *> owned_int4(nullptr); // Ok
84 owned_int4 = new int(42); // Good
85
86 gsl::owner<int *> owned_int5 = owned_int3; // Good
87
88 gsl::owner<int *> owned_int6{nullptr}; // Ok
89 owned_int6 = owned_int4; // Good
90
91 // FIXME:, flow analysis for the case of reassignment. Value must be released before
92 owned_int6 = owned_int3; // BAD, because reassignment without resource release
93
94 auto owned_int7 = returns_owner1(); // Bad, since type deduction eliminates the owner wrapper
95 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
96 // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner
97
98 const auto owned_int8 = returns_owner2(); // Bad, since type deduction eliminates the owner wrapper
99 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *const' with a newly created 'gsl::owner<>'
100 // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner
101
102 gsl::owner<int *> owned_int9 = returns_owner1(); // Ok
103 int *unowned_int3 = returns_owner1(); // Bad
104 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
105
106 gsl::owner<int *> owned_int10;
107 owned_int10 = returns_owner1(); // Ok
108
109 int *unowned_int4;
110 unowned_int4 = returns_owner1(); // Bad
111 // CHECK-NOTES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *'
112
113 gsl::owner<int *> owned_int11 = returns_no_owner1(); // Bad since no owner
114 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
115
116 gsl::owner<int *> owned_int12;
117 owned_int12 = returns_no_owner1(); // Bad since no owner
118 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *'
119
120 int *unowned_int5 = returns_no_owner1(); // Ok
121 int *unowned_int6;
122 unowned_int6 = returns_no_owner1(); // Ok
123
124 int *unowned_int7 = new int(42); // Bad, since resource not assigned to an owner
125 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
126
127 int *unowned_int8;
128 unowned_int8 = new int(42);
129 // CHECK-NOTES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *'
130
131 gsl::owner<int *> owned_int13 = nullptr; // Ok
132 }
133
test_deletion()134 void test_deletion() {
135 gsl::owner<int *> owned_int1 = new int(42);
136 delete owned_int1; // Good
137
138 gsl::owner<int *> owned_int2 = new int[42];
139 delete[] owned_int2; // Good
140
141 int *unowned_int1 = new int(42); // BAD, since new creates and owner
142 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
143 delete unowned_int1; // BAD, since no owner
144 // CHECK-NOTES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead
145 // CHECK-NOTES: [[@LINE-4]]:3: note: variable declared here
146
147 int *unowned_int2 = new int[42]; // BAD, since new creates and owner
148 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
149 delete[] unowned_int2; // BAD since no owner
150 // CHECK-NOTES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead
151 // CHECK-NOTES: [[@LINE-4]]:3: note: variable declared here
152
153 delete new int(42); // Technically ok, but stupid
154 delete[] new int[42]; // Technically ok, but stupid
155 }
156
test_owner_function_calls()157 void test_owner_function_calls() {
158 int stack_int = 42;
159 int *unowned_int1 = &stack_int;
160 takes_owner(&stack_int); // BAD
161 // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *'
162 takes_owner(unowned_int1); // BAD
163 // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *'
164
165 gsl::owner<int *> owned_int1 = new int(42);
166 takes_owner(owned_int1); // Ok
167
168 takes_owner_and_more(42, &stack_int, 42.0f); // BAD
169 // CHECK-NOTES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *'
170 takes_owner_and_more(42, unowned_int1, 42.0f); // BAD
171 // CHECK-NOTES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *'
172
173 takes_owner_and_more(42, new int(42), 42.0f); // Ok, since new is consumed by owner
174 takes_owner_and_more(42, owned_int1, 42.0f); // Ok, since owner as argument
175
176 takes_templated_owner(owned_int1); // Ok
177 takes_templated_owner(new int(42)); // Ok
178 takes_templated_owner(unowned_int1); // Bad
179 // CHECK-NOTES: [[@LINE-1]]:25: warning: expected argument of type 'gsl::owner<>'; got 'int *'
180
181 takes_owner(returns_owner1()); // Ok
182 takes_owner(returns_no_owner1()); // BAD
183 // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *'
184 }
185
test_unowned_function_calls()186 void test_unowned_function_calls() {
187 int stack_int = 42;
188 int *unowned_int1 = &stack_int;
189 gsl::owner<int *> owned_int1 = new int(42);
190
191 takes_pointer(&stack_int); // Ok
192 takes_pointer(unowned_int1); // Ok
193 takes_pointer(owned_int1); // Ok
194 takes_pointer(new int(42)); // Bad, since new creates and owner
195 // CHECK-NOTES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>'
196
197 takes_pointer(returns_owner1()); // Bad
198 // CHECK-NOTES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>'
199
200 takes_pointer(returns_no_owner1()); // Ok
201 }
202
203 // FIXME: Typedefing owner<> to something else does not work.
204 // This might be necessary for code already having a similar typedef like owner<> and
205 // replacing it with owner<>. This might be the same problem as with templates.
206 // The canonical type will ignore the owner<> alias, since its a typedef as well.
207 //
208 // Check, if owners hidden by typedef are handled the same as 'obvious' owners.
209 #if 0
210 using heap_int = gsl::owner<int *>;
211 typedef gsl::owner<float *> heap_float;
212
213 // This tests only a subset, assuming that the check will either see through the
214 // typedef or not (it doesn't!).
215 void test_typedefed_values() {
216 // Modern typedef.
217 int StackInt1 = 42;
218 heap_int HeapInt1 = &StackInt1;
219 // CHECK MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *'
220
221 //FIXME: Typedef not considered correctly here.
222 // heap_int HeapInt2 = new int(42); // Ok
223 takes_pointer(HeapInt1); // Ok
224 takes_owner(HeapInt1); // Ok
225
226 // Traditional typedef.
227 float StackFloat1 = 42.0f;
228 heap_float HeapFloat1 = &StackFloat1;
229 // CHECK MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'float *'
230
231 //FIXME: Typedef not considered correctly here.
232 // heap_float HeapFloat2 = new float(42.0f);
233 HeapFloat2 = HeapFloat1; // Ok
234 }
235 #endif
236
237 struct ArbitraryClass {};
238 struct ClassWithOwner { // Does not define destructor, necessary with owner
ClassWithOwnerClassWithOwner239 ClassWithOwner() : owner_var(nullptr) {} // Ok
240
ClassWithOwnerClassWithOwner241 ClassWithOwner(ArbitraryClass &other) : owner_var(&other) {}
242 // CHECK-NOTES: [[@LINE-1]]:43: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'ArbitraryClass *'
243
ClassWithOwnerClassWithOwner244 ClassWithOwner(gsl::owner<ArbitraryClass *> other) : owner_var(other) {} // Ok
245
ClassWithOwnerClassWithOwner246 ClassWithOwner(gsl::owner<ArbitraryClass *> data, int /* unused */) { // Ok
247 owner_var = data; // Ok
248 }
249
ClassWithOwnerClassWithOwner250 ClassWithOwner(ArbitraryClass *bad_data, int /* unused */, int /* unused */) {
251 owner_var = bad_data;
252 // CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *'
253 }
254
ClassWithOwnerClassWithOwner255 ClassWithOwner(ClassWithOwner &&other) : owner_var{other.owner_var} {} // Ok
256
operator =ClassWithOwner257 ClassWithOwner &operator=(ClassWithOwner &&other) {
258 owner_var = other.owner_var; // Ok
259 return *this;
260 }
261
262 // Returning means, that the owner is "moved", so the class should not access this
263 // variable anymore after this method gets called.
buggy_but_returns_ownerClassWithOwner264 gsl::owner<ArbitraryClass *> buggy_but_returns_owner() { return owner_var; }
265
266 gsl::owner<ArbitraryClass *> owner_var;
267 // CHECK-NOTES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'ClassWithOwner' to implement a destructor to release the owned resource
268 };
269
270 class DefaultedDestructor { // Bad since default constructor with owner
271 ~DefaultedDestructor() = default; // Bad, since will not destroy the owner
272 gsl::owner<int *> Owner;
273 // CHECK-NOTES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'DefaultedDestructor' to implement a destructor to release the owned resource
274 };
275
276 struct DeletedDestructor {
277 ~DeletedDestructor() = delete;
278 gsl::owner<int *> Owner;
279 // CHECK-NOTES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'DeletedDestructor' to implement a destructor to release the owned resource
280 };
281
test_class_with_owner()282 void test_class_with_owner() {
283 ArbitraryClass A;
284 ClassWithOwner C1; // Ok
285 ClassWithOwner C2{A}; // Bad, since the owner would be initialized with an non-owner, but catched in the class
286 ClassWithOwner C3{gsl::owner<ArbitraryClass *>(new ArbitraryClass)}; // Ok
287
288 const auto Owner1 = C3.buggy_but_returns_owner(); // BAD, deduces Owner1 to ArbitraryClass *const
289 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitraryClass *const' with a newly created 'gsl::owner<>'
290 // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner
291
292 auto Owner2 = C2.buggy_but_returns_owner(); // BAD, deduces Owner2 to ArbitraryClass *
293 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitraryClass *' with a newly created 'gsl::owner<>'
294 // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner
295
296 Owner2 = &A; // Ok, since type deduction did NOT result in owner<int*>
297
298 gsl::owner<ArbitraryClass *> Owner3 = C1.buggy_but_returns_owner(); // Ok, still an owner
299 Owner3 = &A; // Bad, since assignment of non-owner to owner
300 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *'
301 }
302
303 template <typename T>
304 struct HeapArray { // Ok, since destructor with owner
HeapArrayHeapArray305 HeapArray() : _data(nullptr), size(0) {} // Ok
HeapArrayHeapArray306 HeapArray(int size) : _data(new int[size]), size(size) {} // Ok
HeapArrayHeapArray307 HeapArray(int size, T val) {
308 _data = new int[size]; // Ok
309 size = size;
310 for (auto i = 0u; i < size; ++i)
311 _data[i] = val; // Ok
312 }
HeapArrayHeapArray313 HeapArray(int size, T val, int *problematic) : _data{problematic}, size(size) {} // Bad
314 // CHECK-NOTES: [[@LINE-1]]:50: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'void'
315 // FIXME: void is incorrect type, probably wrong thing matched
316
HeapArrayHeapArray317 HeapArray(HeapArray &&other) : _data(other._data), size(other.size) { // Ok
318 other._data = nullptr; // Ok
319 other.size = 0;
320 }
321
operator =HeapArray322 HeapArray<T> &operator=(HeapArray<T> &&other) {
323 _data = other._data; // Ok, NOLINT warning here about bad types, why?
324 size = other.size;
325 return *this;
326 }
327
~HeapArrayHeapArray328 ~HeapArray() { delete[] _data; } // Ok
329
dataHeapArray330 T *data() { return _data; } // Ok NOLINT, because it "looks" like a factory
331
332 gsl::owner<T *> _data;
333 unsigned int size;
334 };
335
test_inner_template()336 void test_inner_template() {
337 HeapArray<int> Array1;
338 HeapArray<int> Array2(100);
339 HeapArray<int> Array3(100, 0);
340 HeapArray<int> Array4(100, 0, nullptr);
341
342 Array1 = static_cast<HeapArray<int> &&>(Array2);
343 HeapArray<int> Array5(static_cast<HeapArray<int> &&>(Array3));
344
345 int *NonOwningPtr = Array1.data(); // Ok
346 gsl::owner<int *> OwningPtr = Array1.data(); // Bad, since it does not return the owner
347 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
348 }
349
350 // FIXME: Typededuction removes the owner - wrapper, therefore gsl::owner can not be used
351 // with Template classes like this. Is there a walkaround?
352 template <typename T>
353 struct TemplateValue {
354 TemplateValue() = default;
TemplateValueTemplateValue355 TemplateValue(T t) : val{t} {}
356
setValTemplateValue357 void setVal(const T &t) { val = t; }
getValTemplateValue358 const T getVal() const { return val; }
359
360 T val;
361 };
362
363 // FIXME: Same typededcution problems
364 template <typename T>
template_function(T t)365 void template_function(T t) {
366 gsl::owner<int *> owner_t = t; // Probably bad, since type deduction still wrong
367 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'T'
368 // CHECK-NOTES: [[@LINE-2]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
369 }
370
371 // FIXME: Same typededcution problems
test_templates()372 void test_templates() {
373 int stack_int = 42;
374 int *stack_ptr1 = &stack_int;
375
376 TemplateValue<gsl::owner<int *>> Owner0; // Ok, T should be owner, but is int*
377
378 TemplateValue<gsl::owner<int *>> Owner1(new int(42)); // Ok, T should be owner, but is int*
379 Owner1.setVal(&stack_int); // Bad since non-owner assignment
380 Owner1.setVal(stack_ptr1); // Bad since non-owner assignment
381 //Owner1.setVal(new int(42)); // Ok, but since type deduction is wrong, this one is considered harmful
382
383 int *stack_ptr2 = Owner1.getVal(); // Bad, initializing non-owner with owner
384
385 TemplateValue<int *> NonOwner1(new int(42)); // Bad, T is int *, hence dynamic memory to non-owner
386 gsl::owner<int *> IntOwner1 = NonOwner1.getVal(); // Bad, since owner initialized with non-owner
387 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
388
389 template_function(IntOwner1); // Ok, but not actually ok, since type deduction removes owner
390 template_function(stack_ptr1); // Bad, but type deduction gets it wrong
391 }
392