Nullability annotation support - SonarSource/sonar-java GitHub Wiki

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 4 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. Return values of a strongly nullable method should always be checked for null.

  • Weak Nullable

An element that can be null or not depending on the context. Therefore, developers should themselves determine if a null value is acceptable or if it should be checked. 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 locations where an annotation can be found.
  • Variable: When an argument, field or local variable is directly annotated.
  • Method: When a method bears an annotation that applies to its arguments or return value.
  • Class: When a class bears an annotation that applies to its fields and methods.
  • Package: When a class bears an annotation that applies to its classes.

Nullability Target

This represents the different elements that can be affected by an annotation. We provide nullability information for the following elements:

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

Note that a given annotation can target only a subset of these elements.

Meta annotations

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

Priority

The nullability levels are listed by decreasing order of priority. For example, an element in a package annotated @Nonnull but that 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 written in stone, 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 affected when this annotation is used).

Specific case of "@javax.annotation.Nonnull"

@javax.annotation.Nonnull has a different meaning depending on the arguments:

  • Non-null with no arguments or when=ALWAYS
  • Strong nullable with when=NEVER
  • Nullable with when=UNKNOWN or when=MAYBE

Rules using nullability annotations

Different rules have different requirements when considering the nullability value of an element. The following table summarizes the different levels 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.
⚠️ **GitHub.com Fallback** ⚠️