xref: /llvm-project/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/owning-memory.rst (revision 0f1f1d45c9f77bf5d8e5dce32551b7c78772b8a6)
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