1// RUN: %check_clang_tidy %s cppcoreguidelines-owning-memory %t
2
3namespace gsl {
4template <class T>
5using owner = T;
6} // namespace gsl
7
8template <typename T>
9class unique_ptr {
10public:
11 unique_ptr(gsl::owner<T> resource) : memory(resource) {}
12 unique_ptr(const unique_ptr<T> &) = default;
13
14 ~unique_ptr() { delete memory; }
15
16private:
17 gsl::owner<T> memory;
18};
19
20void takes_owner(gsl::owner<int *> owned_int) {
21}
22
23void takes_pointer(int *unowned_int) {
24}
25
26void takes_owner_and_more(int some_int, gsl::owner<int *> owned_int, float f) {
27}
28
29template <typename T>
30void takes_templated_owner(gsl::owner<T> owned_T) {
31}
32
33gsl::owner<int *> returns_owner1() { return gsl::owner<int *>(new int(42)); } // Ok
34gsl::owner<int *> returns_owner2() { return new int(42); } // Ok
35
36int *returns_no_owner1() { return nullptr; }
37int *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}
41int *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}
46int *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
52unique_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.
58void 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
69void 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(); // Ok, since type deduction does not eliminate the owner wrapper
95
96 const auto owned_int8 = returns_owner2(); // Ok, since type deduction does not eliminate the owner wrapper
97
98 gsl::owner<int *> owned_int9 = returns_owner1(); // Ok
99 int *unowned_int3 = returns_owner1(); // Bad
100 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
101
102 gsl::owner<int *> owned_int10;
103 owned_int10 = returns_owner1(); // Ok
104
105 int *unowned_int4;
106 unowned_int4 = returns_owner1(); // Bad
107 // CHECK-NOTES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *'
108
109 gsl::owner<int *> owned_int11 = returns_no_owner1(); // Bad since no owner
110 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
111
112 gsl::owner<int *> owned_int12;
113 owned_int12 = returns_no_owner1(); // Bad since no owner
114 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *'
115
116 int *unowned_int5 = returns_no_owner1(); // Ok
117 int *unowned_int6;
118 unowned_int6 = returns_no_owner1(); // Ok
119
120 int *unowned_int7 = new int(42); // Bad, since resource not assigned to an owner
121 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
122
123 int *unowned_int8;
124 unowned_int8 = new int(42);
125 // CHECK-NOTES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *'
126
127 gsl::owner<int *> owned_int13 = nullptr; // Ok
128}
129
130void test_deletion() {
131 gsl::owner<int *> owned_int1 = new int(42);
132 delete owned_int1; // Good
133
134 gsl::owner<int *> owned_int2 = new int[42];
135 delete[] owned_int2; // Good
136
137 int *unowned_int1 = new int(42); // BAD, since new creates and owner
138 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
139 delete unowned_int1; // BAD, since no owner
140 // CHECK-NOTES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead
141 // CHECK-NOTES: [[@LINE-4]]:3: note: variable declared here
142
143 int *unowned_int2 = new int[42]; // BAD, since new creates and owner
144 // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>'
145 delete[] unowned_int2; // BAD since no owner
146 // CHECK-NOTES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead
147 // CHECK-NOTES: [[@LINE-4]]:3: note: variable declared here
148
149 delete new int(42); // Technically ok, but stupid
150 delete[] new int[42]; // Technically ok, but stupid
151}
152
153void test_owner_function_calls() {
154 int stack_int = 42;
155 int *unowned_int1 = &stack_int;
156 takes_owner(owned_int: &stack_int); // BAD
157 // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *'
158 takes_owner(owned_int: unowned_int1); // BAD
159 // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *'
160
161 gsl::owner<int *> owned_int1 = new int(42);
162 takes_owner(owned_int: owned_int1); // Ok
163
164 takes_owner_and_more(some_int: 42, owned_int: &stack_int, f: 42.0f); // BAD
165 // CHECK-NOTES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *'
166 takes_owner_and_more(some_int: 42, owned_int: unowned_int1, f: 42.0f); // BAD
167 // CHECK-NOTES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *'
168
169 takes_owner_and_more(some_int: 42, owned_int: new int(42), f: 42.0f); // Ok, since new is consumed by owner
170 takes_owner_and_more(some_int: 42, owned_int: owned_int1, f: 42.0f); // Ok, since owner as argument
171
172 takes_templated_owner(owned_T: owned_int1); // Ok
173 takes_templated_owner(owned_T: new int(42)); // Ok
174 takes_templated_owner(owned_T: unowned_int1); // Bad
175 // CHECK-NOTES: [[@LINE-1]]:25: warning: expected argument of type 'gsl::owner<>'; got 'int *'
176
177 takes_owner(owned_int: returns_owner1()); // Ok
178 takes_owner(owned_int: returns_no_owner1()); // BAD
179 // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *'
180}
181
182void test_unowned_function_calls() {
183 int stack_int = 42;
184 int *unowned_int1 = &stack_int;
185 gsl::owner<int *> owned_int1 = new int(42);
186
187 takes_pointer(unowned_int: &stack_int); // Ok
188 takes_pointer(unowned_int: unowned_int1); // Ok
189 takes_pointer(unowned_int: owned_int1); // Ok
190 takes_pointer(unowned_int: new int(42)); // Bad, since new creates and owner
191 // CHECK-NOTES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>'
192
193 takes_pointer(unowned_int: returns_owner1()); // Bad
194 // CHECK-NOTES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>'
195
196 takes_pointer(unowned_int: returns_no_owner1()); // Ok
197}
198
199// FIXME: Typedefing owner<> to something else does not work.
200// This might be necessary for code already having a similar typedef like owner<> and
201// replacing it with owner<>. This might be the same problem as with templates.
202// The canonical type will ignore the owner<> alias, since its a typedef as well.
203//
204// Check, if owners hidden by typedef are handled the same as 'obvious' owners.
205#if 0
206using heap_int = gsl::owner<int *>;
207typedef gsl::owner<float *> heap_float;
208
209// This tests only a subset, assuming that the check will either see through the
210// typedef or not (it doesn't!).
211void test_typedefed_values() {
212 // Modern typedef.
213 int StackInt1 = 42;
214 heap_int HeapInt1 = &StackInt1;
215 // CHECK MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *'
216
217 //FIXME: Typedef not considered correctly here.
218 // heap_int HeapInt2 = new int(42); // Ok
219 takes_pointer(HeapInt1); // Ok
220 takes_owner(HeapInt1); // Ok
221
222 // Traditional typedef.
223 float StackFloat1 = 42.0f;
224 heap_float HeapFloat1 = &StackFloat1;
225 // CHECK MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'float *'
226
227 //FIXME: Typedef not considered correctly here.
228 // heap_float HeapFloat2 = new float(42.0f);
229 HeapFloat2 = HeapFloat1; // Ok
230}
231#endif
232
233struct ArbitraryClass {};
234struct ClassWithOwner { // Does not define destructor, necessary with owner
235 ClassWithOwner() : owner_var(nullptr) {} // Ok
236
237 ClassWithOwner(ArbitraryClass &other) : owner_var(&other) {}
238 // CHECK-NOTES: [[@LINE-1]]:43: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'ArbitraryClass *'
239
240 ClassWithOwner(gsl::owner<ArbitraryClass *> other) : owner_var(other) {} // Ok
241
242 ClassWithOwner(gsl::owner<ArbitraryClass *> data, int /* unused */) { // Ok
243 owner_var = data; // Ok
244 }
245
246 ClassWithOwner(ArbitraryClass *bad_data, int /* unused */, int /* unused */) {
247 owner_var = bad_data;
248 // CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *'
249 }
250
251 ClassWithOwner(ClassWithOwner &&other) : owner_var{other.owner_var} {} // Ok
252
253 ClassWithOwner &operator=(ClassWithOwner &&other) {
254 owner_var = other.owner_var; // Ok
255 return *this;
256 }
257
258 // Returning means, that the owner is "moved", so the class should not access this
259 // variable anymore after this method gets called.
260 gsl::owner<ArbitraryClass *> buggy_but_returns_owner() { return owner_var; }
261
262 gsl::owner<ArbitraryClass *> owner_var;
263 // 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
264};
265
266class DefaultedDestructor { // Bad since default constructor with owner
267 ~DefaultedDestructor() = default; // Bad, since will not destroy the owner
268 gsl::owner<int *> Owner;
269 // 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
270};
271
272struct DeletedDestructor {
273 ~DeletedDestructor() = delete;
274 gsl::owner<int *> Owner;
275 // 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
276};
277
278void test_class_with_owner() {
279 ArbitraryClass A;
280 ClassWithOwner C1; // Ok
281 ClassWithOwner C2{A}; // Bad, since the owner would be initialized with an non-owner, but catched in the class
282 ClassWithOwner C3{gsl::owner<ArbitraryClass *>(new ArbitraryClass)}; // Ok
283
284 const auto Owner1 = C3.buggy_but_returns_owner(); // Ok, deduces Owner1 to owner<ArbitraryClass *> const
285
286 auto Owner2 = C2.buggy_but_returns_owner(); // Ok, deduces Owner2 to owner<ArbitraryClass *>
287
288 Owner2 = &A; // BAD, since type deduction resulted in owner<ArbitraryClass *>
289 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *'
290
291 gsl::owner<ArbitraryClass *> Owner3 = C1.buggy_but_returns_owner(); // Ok, still an owner
292 Owner3 = &A; // Bad, since assignment of non-owner to owner
293 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *'
294}
295
296template <typename T>
297struct HeapArray { // Ok, since destructor with owner
298 HeapArray() : _data(nullptr), size(0) {} // Ok
299 HeapArray(int size) : _data(new int[size]), size(size) {} // Ok
300 HeapArray(int size, T val) {
301 _data = new int[size]; // Ok
302 size = size;
303 for (auto i = 0u; i < size; ++i)
304 _data[i] = val; // Ok
305 }
306 HeapArray(int size, T val, int *problematic) : _data{problematic}, size(size) {} // Bad
307 // CHECK-NOTES: [[@LINE-1]]:50: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'void'
308 // FIXME: void is incorrect type, probably wrong thing matched
309
310 HeapArray(HeapArray &&other) : _data(other._data), size(other.size) { // Ok
311 other._data = nullptr; // Ok
312 // CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'std::nullptr_t'
313 // FIXME: This warning is emitted because an ImplicitCastExpr for the NullToPointer conversion isn't created for dependent types.
314 other.size = 0;
315 }
316
317 HeapArray<T> &operator=(HeapArray<T> &&other) {
318 _data = other._data; // Ok, NOLINT warning here about bad types, why?
319 size = other.size;
320 return *this;
321 }
322
323 ~HeapArray() { delete[] _data; } // Ok
324
325 T *data() { return _data; } // Ok NOLINT, because it "looks" like a factory
326
327 gsl::owner<T *> _data;
328 unsigned int size;
329};
330
331void test_inner_template() {
332 HeapArray<int> Array1;
333 HeapArray<int> Array2(100);
334 HeapArray<int> Array3(100, 0);
335 HeapArray<int> Array4(100, 0, nullptr);
336
337 Array1 = static_cast<HeapArray<int> &&>(Array2);
338 HeapArray<int> Array5(static_cast<HeapArray<int> &&>(Array3));
339
340 int *NonOwningPtr = Array1.data(); // Ok
341 gsl::owner<int *> OwningPtr = Array1.data(); // Bad, since it does not return the owner
342 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
343}
344
345// FIXME: Typededuction removes the owner - wrapper, therefore gsl::owner can not be used
346// with Template classes like this. Is there a walkaround?
347template <typename T>
348struct TemplateValue {
349 TemplateValue() = default;
350 TemplateValue(T t) : val{t} {}
351
352 void setVal(const T &t) { val = t; }
353 const T getVal() const { return val; }
354
355 T val;
356};
357
358// FIXME: Same typededcution problems
359template <typename T>
360void template_function(T t) {
361 gsl::owner<int *> owner_t = t; // Probably bad, since type deduction still wrong
362 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'T'
363 // CHECK-NOTES: [[@LINE-2]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
364}
365
366// FIXME: Same typededcution problems
367void test_templates() {
368 int stack_int = 42;
369 int *stack_ptr1 = &stack_int;
370
371 TemplateValue<gsl::owner<int *>> Owner0; // Ok, T should be owner, but is int*
372
373 TemplateValue<gsl::owner<int *>> Owner1(new int(42)); // Ok, T should be owner, but is int*
374 Owner1.setVal(&stack_int); // Bad since non-owner assignment
375 Owner1.setVal(stack_ptr1); // Bad since non-owner assignment
376 //Owner1.setVal(new int(42)); // Ok, but since type deduction is wrong, this one is considered harmful
377
378 int *stack_ptr2 = Owner1.getVal(); // Bad, initializing non-owner with owner
379
380 TemplateValue<int *> NonOwner1(new int(42)); // Bad, T is int *, hence dynamic memory to non-owner
381 gsl::owner<int *> IntOwner1 = NonOwner1.getVal(); // Bad, since owner initialized with non-owner
382 // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *'
383
384 template_function(t: IntOwner1); // Ok, but not actually ok, since type deduction removes owner
385 template_function(t: stack_ptr1); // Bad, but type deduction gets it wrong
386}
387
388namespace PR63994 {
389 struct A {
390 virtual ~A() {}
391 };
392
393 struct B : public A {};
394
395 A* foo(int x) {
396 return new B;
397 // CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'A *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
398 }
399}
400
401namespace PR59389 {
402 struct S {
403 S();
404 S(int);
405
406 int value = 1;
407 };
408
409 void testLambdaInFunctionNegative() {
410 const auto MakeS = []() -> ::gsl::owner<S*> {
411 return ::gsl::owner<S*>{new S{}};
412 };
413 }
414
415 void testLambdaInFunctionPositive() {
416 const auto MakeS = []() -> S* {
417 return ::gsl::owner<S*>{new S{}};
418 // CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
419 };
420 }
421
422 void testFunctionInFunctionNegative() {
423 struct C {
424 ::gsl::owner<S*> test() {
425 return ::gsl::owner<S*>{new S{}};
426 }
427 };
428 }
429
430 void testFunctionInFunctionPositive() {
431 struct C {
432 S* test() {
433 return ::gsl::owner<S*>{new S{}};
434 // CHECK-NOTES: [[@LINE-1]]:9: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
435 }
436 };
437 }
438
439 ::gsl::owner<S*> testReverseLambdaNegative() {
440 const auto MakeI = [] -> int { return 5; };
441 return ::gsl::owner<S*>{new S(MakeI())};
442 }
443
444 S* testReverseLambdaPositive() {
445 const auto MakeI = [] -> int { return 5; };
446 return ::gsl::owner<S*>{new S(MakeI())};
447 // CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
448 }
449
450 ::gsl::owner<S*> testReverseFunctionNegative() {
451 struct C {
452 int test() { return 5; }
453 };
454 return ::gsl::owner<S*>{new S(C().test())};
455 }
456
457 S* testReverseFunctionPositive() {
458 struct C {
459 int test() { return 5; }
460 };
461 return ::gsl::owner<S*>{new S(C().test())};
462 // CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
463 }
464
465 void testLambdaInLambdaNegative() {
466 const auto MakeS = []() -> ::gsl::owner<S*> {
467 const auto MakeI = []() -> int { return 5; };
468 return ::gsl::owner<S*>{new S(MakeI())};
469 };
470 }
471
472 void testLambdaInLambdaPositive() {
473 const auto MakeS = []() -> S* {
474 const auto MakeI = []() -> int { return 5; };
475 return ::gsl::owner<S*>{new S(MakeI())};
476 // CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
477 };
478 }
479
480 void testLambdaInLambdaWithDoubleReturns() {
481 const auto MakeS = []() -> S* {
482 const auto MakeS2 = []() -> S* {
483 return ::gsl::owner<S*>{new S(1)};
484 // CHECK-NOTES: [[@LINE-1]]:9: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>' [cppcoreguidelines-owning-memory]
485 };
486 return ::gsl::owner<S*>{new S(2)};
487 // CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
488 };
489 }
490
491 void testReverseLambdaInLambdaNegative() {
492 const auto MakeI = []() -> int {
493 const auto MakeS = []() -> ::gsl::owner<S*> { return new S(); };
494 return 5;
495 };
496 }
497
498 void testReverseLambdaInLambdaPositive() {
499 const auto MakeI = []() -> int {
500 const auto MakeS = []() -> S* { return new S(); };
501 // CHECK-NOTES: [[@LINE-1]]:39: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
502 return 5;
503 };
504 }
505}
506

source code of clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp