187 lines · plain
1.. title:: clang-tidy - cppcoreguidelines-owning-memory2 3cppcoreguidelines-owning-memory4===============================5 6This check implements the type-based semantics of ``gsl::owner<T*>``, which7allows static analysis on code, that uses raw pointers to handle resources8like dynamic memory, but won't introduce RAII concepts.9 10This check implements `I.1111<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i11-never-transfer-ownership-by-a-raw-pointer-t-or-reference-t>`_,12`C.3313<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c33-if-a-class-has-an-owning-pointer-member-define-a-destructor>`_,14`R.315<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r3-a-raw-pointer-a-t-is-non-owning>`_16and `GSL.Views17<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-views>`_18from the C++ Core Guidelines.19The definition of a ``gsl::owner<T*>`` is straight forward20 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 of26the `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 incorrect31initializations of owners, assignment is handled the same way.32Note that both ``new`` and ``malloc()``-like resource functions are33considered to produce resources.34 35.. code-block:: c++36 37 // Creating an owner with factory functions is checked.38 gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }39 40 // Dynamic memory must be assigned to an owner41 int* Something = new int(42); // BAD, will be caught42 gsl::owner<int*> Owner = new int(42); // Good43 gsl::owner<int*> Owner = new int[42]; // Good as well44 45 // Returned owner must be assigned to an owner46 int* Something = function_that_returns_owner(); // Bad, factory function47 gsl::owner<int*> Owner = function_that_returns_owner(); // Good, result lands in owner48 49 // Something not a resource or owner should not be assigned to owners50 int Stack = 42;51 gsl::owner<int*> Owned = &Stack; // Bad, not a resource assigned52 53In the case of dynamic memory as resource, only ``gsl::owner<T*>`` variables are allowed54to be deleted.55 56.. code-block:: c++57 58 // Example Bad, non-owner as resource handle, will be caught.59 int* NonOwner = new int(42); // First warning here, since new must land in an owner60 delete NonOwner; // Second warning here, since only owners are allowed to be deleted61 62 // Example Good, Ownership correctly stated63 gsl::owner<int*> Owner = new int(42); // Good64 delete Owner; // Good as well, statically enforced, that only owners get deleted65 66The check will furthermore ensure, that functions, that expect a67``gsl::owner<T*>`` as argument get called with either a ``gsl::owner<T*>`` or68a newly created resource.69 70.. code-block:: c++71 72 void expects_owner(gsl::owner<int*> o) { delete o; }73 74 // Bad Code75 int NonOwner = 42;76 expects_owner(&NonOwner); // Bad, will get caught77 78 // Good Code79 gsl::owner<int*> Owner = new int(42);80 expects_owner(Owner); // Good81 expects_owner(new int(42)); // Good as well, recognized created resource82 83 // Port legacy code for better resource-safety84 gsl::owner<FILE*> File = fopen("my_file.txt", "rw+");85 FILE* BadFile = fopen("another_file.txt", "w"); // Bad, warned86 87 // ... use the file88 89 fclose(File); // Ok, File is annotated as 'owner<>'90 fclose(BadFile); // BadFile is not an 'owner<>', will be warned91 92 93Options94-------95 96.. option:: LegacyResourceProducers97 98 Semicolon-separated list of fully qualified names of legacy functions that create99 resources but cannot introduce ``gsl::owner<>``.100 Defaults to `::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile`.101 102 103.. option:: LegacyResourceConsumers104 105 Semicolon-separated list of fully qualified names of legacy functions expecting106 resource owners as pointer arguments but cannot introduce ``gsl::owner<>``.107 Defaults to `::free;::realloc;::freopen;::fclose`.108 109 110Limitations111-----------112 113Using ``gsl::owner<T*>`` in a typedef or alias is not handled correctly.114 115.. code-block:: c++116 117 using heap_int = gsl::owner<int*>;118 heap_int allocated = new int(42); // False positive!119 120The ``gsl::owner<T*>`` is declared as a templated type alias.121In template functions and classes, like in the example below, the information122of the type aliases gets lost. Therefore using ``gsl::owner<T*>`` in a heavy templated123code base might lead to false positives.124 125Known code constructs that do not get diagnosed correctly are:126 127- ``std::exchange``128- ``std::vector<gsl::owner<T*>>``129 130.. code-block:: c++131 132 // This template function works as expected. Type information doesn't get lost.133 template <typename T>134 void delete_owner(gsl::owner<T*> owned_object) {135 delete owned_object; // Everything alright136 }137 138 gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }139 140 // Type deduction does not work for auto variables.141 // This is caught by the check and will be noted accordingly.142 auto OwnedObject = function_that_returns_owner(); // Type of OwnedObject will be int*143 144 // Problematic function template that looses the typeinformation on owner145 template <typename T>146 void bad_template_function(T some_object) {147 // This line will trigger the warning, that a non-owner is assigned to an owner148 gsl::owner<T*> new_owner = some_object;149 }150 151 // Calling the function with an owner still yields a false positive.152 bad_template_function(gsl::owner<int*>(new int(42)));153 154 155 // The same issue occurs with templated classes like the following.156 template <typename T>157 class OwnedValue {158 public:159 const T getValue() const { return _val; }160 private:161 T _val;162 };163 164 // Code, that yields a false positive.165 OwnedValue<gsl::owner<int*>> Owner(new int(42)); // Type deduction yield T -> int *166 // False positive, getValue returns int* and not gsl::owner<int*>167 gsl::owner<int*> OwnedInt = Owner.getValue();168 169Another limitation of the current implementation is only the type based170checking. Suppose you have code like the following:171 172.. code-block:: c++173 174 // Two owners with assigned resources175 gsl::owner<int*> Owner1 = new int(42);176 gsl::owner<int*> Owner2 = new int(42);177 178 Owner2 = Owner1; // Conceptual Leak of initial resource of Owner2!179 Owner1 = nullptr;180 181The semantic of a ``gsl::owner<T*>`` is mostly like a ``std::unique_ptr<T>``,182therefore assignment of two ``gsl::owner<T*>`` is considered a move, which183requires that the resource ``Owner2`` must have been released before the184assignment. This kind of condition could be caught in later improvements of185this check with flowsensitive analysis. Currently, the `Clang Static Analyzer`186catches this bug for dynamic memory, but not for general types of resources.187