1.. title:: clang-tidy - cppcoreguidelines-owning-memory 2 3cppcoreguidelines-owning-memory 4=============================== 5 6This check implements the type-based semantics of ``gsl::owner<T*>``, which allows 7static analysis on code, that uses raw pointers to handle resources like 8dynamic memory, but won't introduce RAII concepts. 9 10This check implements `I.11 11<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i11-never-transfer-ownership-by-a-raw-pointer-t-or-reference-t>`_, 12`C.33 13<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c33-if-a-class-has-an-owning-pointer-member-define-a-destructor>`_, 14`R.3 15<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r3-a-raw-pointer-a-t-is-non-owning>`_ 16and `GSL.Views 17<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#SS-views>`_ 18from the C++ Core Guidelines. 19The definition of a ``gsl::owner<T*>`` is straight forward 20 21.. code-block:: c++ 22 23 namespace gsl { template <typename T> owner = T; } 24 25It is therefore simple to introduce the owner even without using an implementation of 26the `Guideline Support Library <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-gsl>`_. 27 28All checks are purely type based and not (yet) flow sensitive. 29 30The following examples will demonstrate the correct and incorrect initializations 31of owners, assignment is handled the same way. Note that both ``new`` and 32``malloc()``-like resource functions are considered to produce resources. 33 34.. code-block:: c++ 35 36 // Creating an owner with factory functions is checked. 37 gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); } 38 39 // Dynamic memory must be assigned to an owner 40 int* Something = new int(42); // BAD, will be caught 41 gsl::owner<int*> Owner = new int(42); // Good 42 gsl::owner<int*> Owner = new int[42]; // Good as well 43 44 // Returned owner must be assigned to an owner 45 int* Something = function_that_returns_owner(); // Bad, factory function 46 gsl::owner<int*> Owner = function_that_returns_owner(); // Good, result lands in owner 47 48 // Something not a resource or owner should not be assigned to owners 49 int Stack = 42; 50 gsl::owner<int*> Owned = &Stack; // Bad, not a resource assigned 51 52In the case of dynamic memory as resource, only ``gsl::owner<T*>`` variables are allowed 53to be deleted. 54 55.. code-block:: c++ 56 57 // Example Bad, non-owner as resource handle, will be caught. 58 int* NonOwner = new int(42); // First warning here, since new must land in an owner 59 delete NonOwner; // Second warning here, since only owners are allowed to be deleted 60 61 // Example Good, Ownership correctly stated 62 gsl::owner<int*> Owner = new int(42); // Good 63 delete Owner; // Good as well, statically enforced, that only owners get deleted 64 65The check will furthermore ensure, that functions, that expect a ``gsl::owner<T*>`` as 66argument get called with either a ``gsl::owner<T*>`` or a newly created resource. 67 68.. code-block:: c++ 69 70 void expects_owner(gsl::owner<int*> o) { delete o; } 71 72 // Bad Code 73 int NonOwner = 42; 74 expects_owner(&NonOwner); // Bad, will get caught 75 76 // Good Code 77 gsl::owner<int*> Owner = new int(42); 78 expects_owner(Owner); // Good 79 expects_owner(new int(42)); // Good as well, recognized created resource 80 81 // Port legacy code for better resource-safety 82 gsl::owner<FILE*> File = fopen("my_file.txt", "rw+"); 83 FILE* BadFile = fopen("another_file.txt", "w"); // Bad, warned 84 85 // ... use the file 86 87 fclose(File); // Ok, File is annotated as 'owner<>' 88 fclose(BadFile); // BadFile is not an 'owner<>', will be warned 89 90 91Options 92------- 93 94.. option:: LegacyResourceProducers 95 96 Semicolon-separated list of fully qualified names of legacy functions that create 97 resources but cannot introduce ``gsl::owner<>``. 98 Defaults to ``::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile``. 99 100 101.. option:: LegacyResourceConsumers 102 103 Semicolon-separated list of fully qualified names of legacy functions expecting 104 resource owners as pointer arguments but cannot introduce ``gsl::owner<>``. 105 Defaults to ``::free;::realloc;::freopen;::fclose``. 106 107 108Limitations 109----------- 110 111Using ``gsl::owner<T*>`` in a typedef or alias is not handled correctly. 112 113.. code-block:: c++ 114 115 using heap_int = gsl::owner<int*>; 116 heap_int allocated = new int(42); // False positive! 117 118The ``gsl::owner<T*>`` is declared as a templated type alias. 119In template functions and classes, like in the example below, the information 120of the type aliases gets lost. Therefore using ``gsl::owner<T*>`` in a heavy templated 121code base might lead to false positives. 122 123Known code constructs that do not get diagnosed correctly are: 124 125- ``std::exchange`` 126- ``std::vector<gsl::owner<T*>>`` 127 128.. code-block:: c++ 129 130 // This template function works as expected. Type information doesn't get lost. 131 template <typename T> 132 void delete_owner(gsl::owner<T*> owned_object) { 133 delete owned_object; // Everything alright 134 } 135 136 gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); } 137 138 // Type deduction does not work for auto variables. 139 // This is caught by the check and will be noted accordingly. 140 auto OwnedObject = function_that_returns_owner(); // Type of OwnedObject will be int* 141 142 // Problematic function template that looses the typeinformation on owner 143 template <typename T> 144 void bad_template_function(T some_object) { 145 // This line will trigger the warning, that a non-owner is assigned to an owner 146 gsl::owner<T*> new_owner = some_object; 147 } 148 149 // Calling the function with an owner still yields a false positive. 150 bad_template_function(gsl::owner<int*>(new int(42))); 151 152 153 // The same issue occurs with templated classes like the following. 154 template <typename T> 155 class OwnedValue { 156 public: 157 const T getValue() const { return _val; } 158 private: 159 T _val; 160 }; 161 162 // Code, that yields a false positive. 163 OwnedValue<gsl::owner<int*>> Owner(new int(42)); // Type deduction yield T -> int * 164 // False positive, getValue returns int* and not gsl::owner<int*> 165 gsl::owner<int*> OwnedInt = Owner.getValue(); 166 167Another limitation of the current implementation is only the type based checking. 168Suppose you have code like the following: 169 170.. code-block:: c++ 171 172 // Two owners with assigned resources 173 gsl::owner<int*> Owner1 = new int(42); 174 gsl::owner<int*> Owner2 = new int(42); 175 176 Owner2 = Owner1; // Conceptual Leak of initial resource of Owner2! 177 Owner1 = nullptr; 178 179The semantic of a ``gsl::owner<T*>`` is mostly like a ``std::unique_ptr<T>``, therefore 180assignment of two ``gsl::owner<T*>`` is considered a move, which requires that the 181resource ``Owner2`` must have been released before the assignment. 182This kind of condition could be caught in later improvements of this check with 183flowsensitive analysis. Currently, the `Clang Static Analyzer` catches this bug 184for dynamic memory, but not for general types of resources. 185