JDT Core/Null Analysis/Options
The analysis of possible null pointer exceptions performed by the Eclipse Java Compiler is powerful, but the full power may result in a large number of errors and warnings. Not all projects can afford addressing all these issues.
One potential solution might be to make the compiler "smarter" by add heuristics to "guess" which problems are probably uninteresting, and which ones are the real bugs.
This page describes the opposite approach: let the compiler soberly perform its analysis without any bias to any coding style. However, empower the user to stepwise enable this analysis for gradually achieving more and more safety.
The following sections each address one potential reason why null pointer problems might remain undetected, pointing at strategies for avoiding each particular kind of risk.
If you start with an existing project and only turn on annotation based null analysis, you will not notice any difference. This is because the analysis implicitly supports three kinds of reference types:
- @NonNull types
- @Nullable types and
- types with unspecified nullness (legacy types).
So before you actually add annotations to your methods and fields, all types will be considered as legacy types and so the unsafe semantics of Java apply where both assigning null and dereferencing are legal.
Adding individual annotations
This path is obvious: add just those annotations where you are certain about the design intent and after each annotation added address the problems reported by the compiler.
Making nonnull the default
Instead of going in tiny little steps you may want to do jumps of some size (cf. bug 331647):
- annotate a method as @NonNullByDefault to affect all its parameters and its return
- annotate a type as @NonNullByDefault to affect all its methods and fieds
- annotate a package as @NonNullByDefault (using package-info.java) to affect all its types
- define a global policy that all packages should be @NonNullByDefault
Unfortunately, a global default cannot directly be established but only via the indirection of package level defaults (see bug 366063 for background).
A particular problem arises when a type affected by @NonNullByDefault is a subtype of a legacy type with legacy signatures. It is illegal to override a method with legacy parameters by a method with @NonNull parameters. Specifying @Nullable for all parameters in such an overriding method may be too pessimistic, forcing the method body to do more null checks than actually useful.
For this situation a parameter has been added to the default annotation. Declaring a type that inherits from a legacy type with @NonNullByDefault(false) cancels the applicable default.
Every project depends on third party code, which it doesn't control, so adding null annotations is not directly possible.
To address this issue, the Eclipse Java Compiler should support nullity profiles, aka external annotations: separate files that capture the factual null contracts of all API methods and fields contained in a given library.
Support for this feature is planned for version 3.9, see bug 331651.
Avoiding the risk of incompleteness
In order to ensure that no unchecked legacy types are used in an application, these things must be ensured:
- @NonNullByDefault is the globally enforced default
- @NonNullByDefault(false) is never used
- all libraries come with API-complete nullity profiles
Each of these steps brings a project closer to the safety guarantees of complete analysis.
TODO: Issue a configurable warning when @NonNullByDefault(false) is used. TODO: Add a note to bug 331651 for checking complete coverage of referenced libraries by available nullity profiles.