xref: /llvm-project/clang/docs/analyzer/developer-docs/nullability.rst (revision 3a14993fa428c67634c979107ca6ddaafeb7037b)
11a17032bSKristof Umann==================
21a17032bSKristof UmannNullability Checks
31a17032bSKristof Umann==================
41a17032bSKristof Umann
51a17032bSKristof UmannThis document is a high level description of the nullablility checks.
61a17032bSKristof UmannThese checks intended to use the annotations that is described in this
7166f9be3SHans WennborgRFC: https://discourse.llvm.org/t/rfc-nullability-qualifiers/35672
8166f9be3SHans Wennborg(`Mailman <https://lists.llvm.org/pipermail/cfe-dev/2015-March/041779.html>`_)
91a17032bSKristof Umann
101a17032bSKristof UmannLet's consider the following 2 categories:
111a17032bSKristof Umann
121a17032bSKristof Umann**1) nullable**
131a17032bSKristof Umann
141a17032bSKristof UmannIf a pointer ``p`` has a nullable annotation and no explicit null check or assert, we should warn in the following cases:
151a17032bSKristof Umann
161a17032bSKristof Umann* ``p`` gets implicitly converted into nonnull pointer, for example, we are passing it to a function that takes a nonnull parameter.
171a17032bSKristof Umann* ``p`` gets dereferenced
181a17032bSKristof Umann
191a17032bSKristof UmannTaking a branch on nullable pointers are the same like taking branch on null unspecified pointers.
201a17032bSKristof Umann
21cc8237d9SAyushi ShuklaExplicit cast from nullable to nonnull:
221a17032bSKristof Umann
231a17032bSKristof Umann.. code-block:: cpp
241a17032bSKristof Umann
251a17032bSKristof Umann  __nullable id foo;
261a17032bSKristof Umann  id bar = foo;
271a17032bSKristof Umann  takesNonNull((_nonnull) bar); // should not warn here (backward compatibility hack)
281a17032bSKristof Umann  anotherTakesNonNull(bar); // would be great to warn here, but not necessary(*)
291a17032bSKristof Umann
301a17032bSKristof UmannBecause bar corresponds to the same symbol all the time it is not easy to implement the checker that way the cast only suppress the first call but not the second. For this reason in the first implementation after a contradictory cast happens, I will treat bar as nullable unspecified, this way all of the warnings will be suppressed. Treating the symbol as nullable unspecified also has an advantage that in case the takesNonNull function body is being inlined, the will be no warning, when the symbol is dereferenced. In case I have time after the initial version I might spend additional time to try to find a more sophisticated solution, in which we would produce the second warning (*).
311a17032bSKristof Umann
321a17032bSKristof Umann**2) nonnull**
331a17032bSKristof Umann
341a17032bSKristof Umann* Dereferencing a nonnull, or sending message to it is ok.
351a17032bSKristof Umann* Converting nonnull to nullable is Ok.
361a17032bSKristof Umann* When there is an explicit cast from nonnull to nullable I will trust the cast (it is probable there for a reason, because this cast does not suppress any warnings or errors).
371a17032bSKristof Umann* But what should we do about null checks?:
381a17032bSKristof Umann
391a17032bSKristof Umann.. code-block:: cpp
401a17032bSKristof Umann
411a17032bSKristof Umann  __nonnull id takesNonnull(__nonnull id x) {
421a17032bSKristof Umann      if (x == nil) {
431a17032bSKristof Umann          // Defensive backward compatible code:
441a17032bSKristof Umann          ....
451a17032bSKristof Umann          return nil; // Should the analyzer cover this piece of code? Should we require the cast (__nonnull)nil?
461a17032bSKristof Umann      }
471a17032bSKristof Umann      ....
481a17032bSKristof Umann  }
491a17032bSKristof Umann
501a17032bSKristof UmannThere are these directions:
511a17032bSKristof Umann
521a17032bSKristof Umann* We can either take the branch; this way the branch is analyzed
531a17032bSKristof Umann* Should we not warn about any nullability issues in that branch? Probably not, it is ok to break the nullability postconditions when the nullability preconditions are violated.
541a17032bSKristof Umann* We can assume that these pointers are not null and we lose coverage with the analyzer. (This can be implemented either in constraint solver or in the checker itself.)
551a17032bSKristof Umann
561a17032bSKristof UmannOther Issues to keep in mind/take care of:
571a17032bSKristof Umann
581a17032bSKristof Umann* Messaging:
591a17032bSKristof Umann
601a17032bSKristof Umann  * Sending a message to a nullable pointer
611a17032bSKristof Umann
621a17032bSKristof Umann    * Even though the method might return a nonnull pointer, when it was sent to a nullable pointer the return type will be nullable.
631a17032bSKristof Umann  	* The result is nullable unless the receiver is known to be non null.
641a17032bSKristof Umann
65*3a14993fSKazu Hirata  * Sending a message to an unspecified or nonnull pointer
661a17032bSKristof Umann
671a17032bSKristof Umann    * If the pointer is not assumed to be nil, we should be optimistic and use the nullability implied by the method.
681a17032bSKristof Umann
691a17032bSKristof Umann      * This will not happen automatically, since the AST will have null unspecified in this case.
701a17032bSKristof Umann
711a17032bSKristof UmannInlining
721a17032bSKristof Umann--------
731a17032bSKristof Umann
741a17032bSKristof UmannA symbol may need to be treated differently inside an inlined body. For example, consider these conversions from nonnull to nullable in presence of inlining:
751a17032bSKristof Umann
761a17032bSKristof Umann.. code-block:: cpp
771a17032bSKristof Umann
781a17032bSKristof Umann  id obj = getNonnull();
791a17032bSKristof Umann  takesNullable(obj);
801a17032bSKristof Umann  takesNonnull(obj);
811a17032bSKristof Umann
821a17032bSKristof Umann  void takesNullable(nullable id obj) {
831a17032bSKristof Umann     obj->ivar // we should assume obj is nullable and warn here
841a17032bSKristof Umann  }
851a17032bSKristof Umann
861a17032bSKristof UmannWith no special treatment, when the takesNullable is inlined the analyzer will not warn when the obj symbol is dereferenced. One solution for this is to reanalyze takesNullable as a top level function to get possible violations. The alternative method, deducing nullability information from the arguments after inlining is not robust enough (for example there might be more parameters with different nullability, but in the given path the two parameters might end up being the same symbol or there can be nested functions that take different view of the nullability of the same symbol). So the symbol will remain nonnull to avoid false positives but the functions that takes nullable parameters will be analyzed separately as well without inlining.
871a17032bSKristof Umann
881a17032bSKristof UmannAnnotations on multi level pointers
891a17032bSKristof Umann-----------------------------------
901a17032bSKristof Umann
911a17032bSKristof UmannTracking multiple levels of annotations for pointers pointing to pointers would make the checker more complicated, because this way a vector of nullability qualifiers would be needed to be tracked for each symbol. This is not a big caveat, since once the top level pointer is dereferenced, the symvol for the inner pointer will have the nullability information. The lack of multi level annotation tracking only observable, when multiple levels of pointers are passed to a function which has a parameter with multiple levels of annotations. So for now the checker support the top level nullability qualifiers only.:
921a17032bSKristof Umann
931a17032bSKristof Umann.. code-block:: cpp
941a17032bSKristof Umann
951a17032bSKristof Umann  int * __nonnull * __nullable p;
961a17032bSKristof Umann  int ** q = p;
971a17032bSKristof Umann  takesStarNullableStarNullable(q);
981a17032bSKristof Umann
991a17032bSKristof UmannImplementation notes
1001a17032bSKristof Umann--------------------
1011a17032bSKristof Umann
1021a17032bSKristof UmannWhat to track?
1031a17032bSKristof Umann
1041a17032bSKristof Umann* The checker would track memory regions, and to each relevant region a qualifier information would be attached which is either nullable, nonnull or null unspecified (or contradicted to suppress warnings for a specific region).
1051a17032bSKristof Umann* On a branch, where a nullable pointer is known to be non null, the checker treat it as a same way as a pointer annotated as nonnull.
1061a17032bSKristof Umann* When there is an explicit cast from a null unspecified to either nonnull or nullable I will trust the cast.
1071a17032bSKristof Umann* Unannotated pointers are treated the same way as pointers annotated with nullability unspecified qualifier, unless the region is wrapped in ASSUME_NONNULL macros.
1081a17032bSKristof Umann* We might want to implement a callback for entry points to top level functions, where the pointer nullability assumptions would be made.
109