1 .. title:: clang-tidy - cppcoreguidelines-owning-memory
3 cppcoreguidelines-owning-memory
4 ===============================
6 This check implements the type-based semantics of ``gsl::owner<T*>``, which allows
7 static analysis on code, that uses raw pointers to handle resources like
8 dynamic memory, but won't introduce RAII concepts.
10 The relevant sections in the `C++ Core Guidelines <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md>`_ are I.11, C.33, R.3 and GSL.Views
11 The definition of a ``gsl::owner<T*>`` is straight forward
15 namespace gsl { template <typename T> owner = T; }
17 It is therefore simple to introduce the owner even without using an implementation of
18 the `Guideline Support Library <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gsl-guideline-support-library>`_.
20 All checks are purely type based and not (yet) flow sensitive.
22 The following examples will demonstrate the correct and incorrect initializations
23 of owners, assignment is handled the same way. Note that both ``new`` and
24 ``malloc()``-like resource functions are considered to produce resources.
28 // Creating an owner with factory functions is checked.
29 gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }
31 // Dynamic memory must be assigned to an owner
32 int* Something = new int(42); // BAD, will be caught
33 gsl::owner<int*> Owner = new int(42); // Good
34 gsl::owner<int*> Owner = new int[42]; // Good as well
36 // Returned owner must be assigned to an owner
37 int* Something = function_that_returns_owner(); // Bad, factory function
38 gsl::owner<int*> Owner = function_that_returns_owner(); // Good, result lands in owner
40 // Something not a resource or owner should not be assigned to owners
42 gsl::owner<int*> Owned = &Stack; // Bad, not a resource assigned
44 In the case of dynamic memory as resource, only ``gsl::owner<T*>`` variables are allowed
49 // Example Bad, non-owner as resource handle, will be caught.
50 int* NonOwner = new int(42); // First warning here, since new must land in an owner
51 delete NonOwner; // Second warning here, since only owners are allowed to be deleted
53 // Example Good, Ownership correctly stated
54 gsl::owner<int*> Owner = new int(42); // Good
55 delete Owner; // Good as well, statically enforced, that only owners get deleted
57 The check will furthermore ensure, that functions, that expect a ``gsl::owner<T*>`` as
58 argument get called with either a ``gsl::owner<T*>`` or a newly created resource.
62 void expects_owner(gsl::owner<int*> o) { delete o; }
66 expects_owner(&NonOwner); // Bad, will get caught
69 gsl::owner<int*> Owner = new int(42);
70 expects_owner(Owner); // Good
71 expects_owner(new int(42)); // Good as well, recognized created resource
73 // Port legacy code for better resource-safety
74 gsl::owner<FILE*> File = fopen("my_file.txt", "rw+");
75 FILE* BadFile = fopen("another_file.txt", "w"); // Bad, warned
79 fclose(File); // Ok, File is annotated as 'owner<>'
80 fclose(BadFile); // BadFile is not an 'owner<>', will be warned
86 .. option:: LegacyResourceProducers
88 Semicolon-separated list of fully qualified names of legacy functions that create
89 resources but cannot introduce ``gsl::owner<>``.
90 Defaults to ``::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile``.
93 .. option:: LegacyResourceConsumers
95 Semicolon-separated list of fully qualified names of legacy functions expecting
96 resource owners as pointer arguments but cannot introduce ``gsl::owner<>``.
97 Defaults to ``::free;::realloc;::freopen;::fclose``.
103 Using ``gsl::owner<T*>`` in a typedef or alias is not handled correctly.
107 using heap_int = gsl::owner<int*>;
108 heap_int allocated = new int(42); // False positive!
110 The ``gsl::owner<T*>`` is declared as a templated type alias.
111 In template functions and classes, like in the example below, the information
112 of the type aliases gets lost. Therefore using ``gsl::owner<T*>`` in a heavy templated
113 code base might lead to false positives.
115 Known code constructs that do not get diagnosed correctly are:
118 - ``std::vector<gsl::owner<T*>>``
122 // This template function works as expected. Type information doesn't get lost.
123 template <typename T>
124 void delete_owner(gsl::owner<T*> owned_object) {
125 delete owned_object; // Everything alright
128 gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }
130 // Type deduction does not work for auto variables.
131 // This is caught by the check and will be noted accordingly.
132 auto OwnedObject = function_that_returns_owner(); // Type of OwnedObject will be int*
134 // Problematic function template that looses the typeinformation on owner
135 template <typename T>
136 void bad_template_function(T some_object) {
137 // This line will trigger the warning, that a non-owner is assigned to an owner
138 gsl::owner<T*> new_owner = some_object;
141 // Calling the function with an owner still yields a false positive.
142 bad_template_function(gsl::owner<int*>(new int(42)));
145 // The same issue occurs with templated classes like the following.
146 template <typename T>
149 const T getValue() const { return _val; }
154 // Code, that yields a false positive.
155 OwnedValue<gsl::owner<int*>> Owner(new int(42)); // Type deduction yield T -> int *
156 // False positive, getValue returns int* and not gsl::owner<int*>
157 gsl::owner<int*> OwnedInt = Owner.getValue();
159 Another limitation of the current implementation is only the type based checking.
160 Suppose you have code like the following:
164 // Two owners with assigned resources
165 gsl::owner<int*> Owner1 = new int(42);
166 gsl::owner<int*> Owner2 = new int(42);
168 Owner2 = Owner1; // Conceptual Leak of initial resource of Owner2!
171 The semantic of a ``gsl::owner<T*>`` is mostly like a ``std::unique_ptr<T>``, therefore
172 assignment of two ``gsl::owner<T*>`` is considered a move, which requires that the
173 resource ``Owner2`` must have been released before the assignment.
174 This kind of condition could be caught in later improvements of this check with
175 flowsensitive analysis. Currently, the `Clang Static Analyzer` catches this bug
176 for dynamic memory, but not for general types of resources.