Skip to content

Nullability annotation support

quentin-jaquier-sonarsource edited this page Nov 23, 2021 · 9 revisions

Context

What we call nullability annotation are the set of annotations used to describe the nullability value of elements.

@Nonnull
private String s; // "s" should never be null

@CheckForNull // The return value of "f()" should always be checked for null
String f(@Nullable Object o) {
  // You should consider the case where "o" is null
}

The Java analyzer contains rules helping users to consistently use such annotations and is also using them to improve the precision of the analysis. The current ecosystem is a bit complex, there are many different annotation providers, sometimes with a slightly different understanding of the problem. It is important for us to have a common understanding of the problem and to apply it in a consistent way.

Disclaimer: This document does not aim to show how things should work or the best way to use annotations, but rather a description of how we use nullability annotations in order to provide relevant analysis results.

Main notions

Nullability Type

In the context of the analyzer, we will use 3 different notions, representing the nullability value of an element (argument, field, return value of a method, local variable).

  • Non-null

The element is never null. It can therefore be safely dereferenced and does not need to be checked for null.

  • Strong Nullable

An element which nullability can not be statically determined, it should always be checked for null. Typical example: Null Dereference Check. A method strongly nullable should always be checked for null.

  • Weak Nullable

An element that can be null or not depending on the context. A user should therefore themself determine if a null value is acceptable and if he should check it for null. The notion of weak nullable is not directly needed in our rules, only though nullable.

  • Nullable

Combination of Weak and Strong Nullable. Everything that could be null at one point.

Nullability Level

This represents the different places where an annotation can be found.

  • Variable

When the parameter, field or local variable is directly annotated.

  • Method

When the method return value is directly annotated.

  • Class

Applies to the whole class.

  • Package

Package level annotation. The nullability value of this annotation will apply to the whole package

Nullability Target

We have seen previously that the nullability value targets multiple elements:

  • argument
  • field
  • return value of a method
  • local variable

A given annotation can target only some of these elements.

Meta annotations

When an annotation is itself annotated with a nullability annotation, we call this a meta-annotation.

Priority

The different levels represents also the priority, from variable to package. For example, something annotated @Nonnull at package level but is directly annotated with @Nullable will be nullable. Meta-annotations at a given level have lower priority than when directly annotated, but the priority of level still applies.

Supported annotations

The exact list of supported annotations is not fixed, the best way to have reliable information is to check the code itself: JSymbolMetadataNullabilityHelper.java#L67. Every annotation is then assigned a list of Levels (where can you put this annotation?) and a list of Targets (what element will be null when this annotation is used).

Rules used nullability annotations

Different rules have different requirements when considering the nullability value of an element. The following table summarizes the different Level supported in the rules relying on nullability annotations.

Rule Level Ignore meta-annotation Notes
S2789 NullShouldNotBeUsedWithOptionalCheck VARIABLE true Reporting only when directly annotated makes sense from both the implementation (reports on the annotation) and the logic of the rule (should not report any method returning Optional in a package annotated Nullable. In any case, we will still report an issue if it explicitly returns null.
S2638 ChangeMethodContractCheck PACKAGE false
S4682 PrimitivesMarkedNullableCheck VARIABLE true We want to report an issue only when the element is directly annotated.
S2637 NonNullSetToNullCheck PACKAGE false
S4454 EqualsParametersMarkedNonNullCheck VARIABLE false
S2447 BooleanMethodReturnCheck PACKAGE false We use annotations mainly to kill the noise.
S1168 ReturnEmptyArrayNotNullCheck PACKAGE false Same as S2447.
S4449 ParameterNullnessCheck (SE) PACKAGE false
S2259 NullDereferenceCheck (SE) PACKAGE false
Exploded Graph Walker PACKAGE false The Exploded graph walker is not a rule, but is used during symbolic execution to get the nullability value of elements.
Clone this wiki locally