brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.8 KiB · 218de55 Raw
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