xref: /llvm-project/clang-tools-extra/docs/clang-tidy/checks/bugprone/unchecked-optional-access.rst (revision 6761b24ae2f34b923df46412475a9ece50542b97)
1.. title:: clang-tidy - bugprone-unchecked-optional-access
2
3bugprone-unchecked-optional-access
4==================================
5
6*Note*: This check uses a flow-sensitive static analysis to produce its
7results. Therefore, it may be more resource intensive (RAM, CPU) than the
8average clang-tidy check.
9
10This check identifies unsafe accesses to values contained in
11``std::optional<T>``, ``absl::optional<T>``, ``base::Optional<T>``,
12``folly::Optional<T>``, ``bsl::optional``, or
13``BloombergLP::bdlb::NullableValue`` objects. Below we will refer to all these
14types collectively as ``optional<T>``.
15
16An access to the value of an ``optional<T>`` occurs when one of its ``value``,
17``operator*``, or ``operator->`` member functions is invoked.  To align with
18common misconceptions, the check considers these member functions as equivalent,
19even though there are subtle differences related to exceptions versus undefined
20behavior. See *Additional notes*, below, for more information on this topic.
21
22An access to the value of an ``optional<T>`` is considered safe if and only if
23code in the local scope (for example, a function body) ensures that the
24``optional<T>`` has a value in all possible execution paths that can reach the
25access. That should happen either through an explicit check, using the
26``optional<T>::has_value`` member function, or by constructing the
27``optional<T>`` in a way that shows that it unambiguously holds a value (e.g
28using ``std::make_optional`` which always returns a populated
29``std::optional<T>``).
30
31Below we list some examples, starting with unsafe optional access patterns,
32followed by safe access patterns.
33
34Unsafe access patterns
35~~~~~~~~~~~~~~~~~~~~~~
36
37Access the value without checking if it exists
38----------------------------------------------
39
40The check flags accesses to the value that are not locally guarded by
41existence check:
42
43.. code-block:: c++
44
45   void f(std::optional<int> opt) {
46     use(*opt); // unsafe: it is unclear whether `opt` has a value.
47   }
48
49Access the value in the wrong branch
50------------------------------------
51
52The check is aware of the state of an optional object in different
53branches of the code. For example:
54
55.. code-block:: c++
56
57   void f(std::optional<int> opt) {
58     if (opt.has_value()) {
59     } else {
60       use(opt.value()); // unsafe: it is clear that `opt` does *not* have a value.
61     }
62   }
63
64Assume a function result to be stable
65-------------------------------------
66
67The check is aware that function results might not be stable. That is,
68consecutive calls to the same function might return different values.
69For example:
70
71.. code-block:: c++
72
73   void f(Foo foo) {
74     if (foo.opt().has_value()) {
75       use(*foo.opt()); // unsafe: it is unclear whether `foo.opt()` has a value.
76     }
77   }
78
79Exception: accessor methods
80```````````````````````````
81
82The check assumes *accessor* methods of a class are stable, with a heuristic to
83determine which methods are accessors. Specifically, parameter-free ``const``
84methods are treated as accessors. Note that this is not guaranteed to be safe
85-- but, it is widely used (safely) in practice, and so we have chosen to treat
86it as generally safe. Calls to non ``const`` methods are assumed to modify
87the state of the object and affect the stability of earlier accessor calls.
88
89Rely on invariants of uncommon APIs
90-----------------------------------
91
92The check is unaware of invariants of uncommon APIs. For example:
93
94.. code-block:: c++
95
96   void f(Foo foo) {
97     if (foo.HasProperty("bar")) {
98       use(*foo.GetProperty("bar")); // unsafe: it is unclear whether `foo.GetProperty("bar")` has a value.
99     }
100   }
101
102Check if a value exists, then pass the optional to another function
103-------------------------------------------------------------------
104
105The check relies on local reasoning. The check and value access must
106both happen in the same function. An access is considered unsafe even if
107the caller of the function performing the access ensures that the
108optional has a value. For example:
109
110.. code-block:: c++
111
112   void g(std::optional<int> opt) {
113     use(*opt); // unsafe: it is unclear whether `opt` has a value.
114   }
115
116   void f(std::optional<int> opt) {
117     if (opt.has_value()) {
118       g(opt);
119     }
120   }
121
122Safe access patterns
123~~~~~~~~~~~~~~~~~~~~
124
125Check if a value exists, then access the value
126----------------------------------------------
127
128The check recognizes all straightforward ways for checking if a value
129exists and accessing the value contained in an optional object. For
130example:
131
132.. code-block:: c++
133
134   void f(std::optional<int> opt) {
135     if (opt.has_value()) {
136       use(*opt);
137     }
138   }
139
140
141Check if a value exists, then access the value from a copy
142----------------------------------------------------------
143
144The criteria that the check uses is semantic, not syntactic. It
145recognizes when a copy of the optional object being accessed is known to
146have a value. For example:
147
148.. code-block:: c++
149
150   void f(std::optional<int> opt1) {
151     if (opt1.has_value()) {
152       std::optional<int> opt2 = opt1;
153       use(*opt2);
154     }
155   }
156
157
158Ensure that a value exists using common macros
159----------------------------------------------
160
161The check is aware of common macros like ``CHECK`` and ``DCHECK``. Those can be
162used to ensure that an optional object has a value. For example:
163
164.. code-block:: c++
165
166   void f(std::optional<int> opt) {
167     DCHECK(opt.has_value());
168     use(*opt);
169   }
170
171Ensure that a value exists, then access the value in a correlated branch
172------------------------------------------------------------------------
173
174The check is aware of correlated branches in the code and can figure out
175when an optional object is ensured to have a value on all execution
176paths that lead to an access. For example:
177
178.. code-block:: c++
179
180   void f(std::optional<int> opt) {
181     bool safe = false;
182     if (opt.has_value() && SomeOtherCondition()) {
183       safe = true;
184     }
185     // ... more code...
186     if (safe) {
187       use(*opt);
188     }
189   }
190
191Stabilize function results
192~~~~~~~~~~~~~~~~~~~~~~~~~~
193
194Since function results are not assumed to be stable across calls, it is best to
195store the result of the function call in a local variable and use that variable
196to access the value. For example:
197
198.. code-block:: c++
199
200   void f(Foo foo) {
201     if (const auto& foo_opt = foo.opt(); foo_opt.has_value()) {
202       use(*foo_opt);
203     }
204   }
205
206Do not rely on uncommon-API invariants
207~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
208
209When uncommon APIs guarantee that an optional has contents, do not rely on it --
210instead, check explicitly that the optional object has a value. For example:
211
212.. code-block:: c++
213
214   void f(Foo foo) {
215     if (const auto& property = foo.GetProperty("bar")) {
216       use(*property);
217     }
218   }
219
220instead of the `HasProperty`, `GetProperty` pairing we saw above.
221
222Do not rely on caller-performed checks
223~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224
225If you know that all of a function's callers have checked that an optional
226argument has a value, either change the function to take the value directly or
227check the optional again in the local scope of the callee. For example:
228
229.. code-block:: c++
230
231   void g(int val) {
232     use(val);
233   }
234
235   void f(std::optional<int> opt) {
236     if (opt.has_value()) {
237       g(*opt);
238     }
239   }
240
241and
242
243.. code-block:: c++
244
245   struct S {
246     std::optional<int> opt;
247     int x;
248   };
249
250   void g(const S &s) {
251     if (s.opt.has_value() && s.x > 10) {
252       use(*s.opt);
253   }
254
255   void f(S s) {
256     if (s.opt.has_value()) {
257       g(s);
258     }
259   }
260
261Additional notes
262~~~~~~~~~~~~~~~~
263
264Aliases created via ``using`` declarations
265------------------------------------------
266
267The check is aware of aliases of optional types that are created via
268``using`` declarations. For example:
269
270.. code-block:: c++
271
272   using OptionalInt = std::optional<int>;
273
274   void f(OptionalInt opt) {
275     use(opt.value()); // unsafe: it is unclear whether `opt` has a value.
276   }
277
278Lambdas
279-------
280
281The check does not currently report unsafe optional accesses in lambdas.
282A future version will expand the scope to lambdas, following the rules
283outlined above. It is best to follow the same principles when using
284optionals in lambdas.
285
286Access with ``operator*()`` vs. ``value()``
287-------------------------------------------
288
289Given that ``value()`` has well-defined behavior (either throwing an exception
290or terminating the program), why treat it the same as ``operator*()`` which
291causes undefined behavior (UB)? That is, why is it considered unsafe to access
292an optional with ``value()``, if it's not provably populated with a value?  For
293that matter, why is ``CHECK()`` followed by ``operator*()`` any better than
294``value()``, given that they are semantically equivalent (on configurations that
295disable exceptions)?
296
297The answer is that we assume most users do not realize the difference between
298``value()`` and ``operator*()``. Shifting to ``operator*()`` and some form of
299explicit value-presence check or explicit program termination has two
300advantages:
301
302  * Readability. The check, and any potential side effects like program
303    shutdown, are very clear in the code. Separating access from checks can
304    actually make the checks more obvious.
305
306  * Performance. A single check can cover many or even all accesses within
307    scope. This gives the user the best of both worlds -- the safety of a
308    dynamic check, but without incurring redundant costs.
309