Nullability annotation support - SonarSource/sonar-java GitHub Wiki
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.
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.
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.
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.
When an annotation is itself annotated with a nullability annotation, we call this a meta-annotation.
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.
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).
@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
orwhen=MAYBE
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. |