Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
Difference between revisions of "JDT Core/Null Analysis"
(Updated section Usage for JDT 3.8 release) |
(udpated quick fixes for JDT 3.8.0) |
||
Line 92: | Line 92: | ||
====Cleaning up==== | ====Cleaning up==== | ||
− | + | When applying the new analysis to a big existing project, the sheer number of new problems may look intimidating but that's where quick fixes will come to the rescue. | |
− | + | ||
− | When applying the new analysis to a big existing project, the sheer number of new problems may look intimidating but that's where | + | |
Currently the following problems offer a quickfix: | Currently the following problems offer a quickfix: | ||
− | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] | + | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Null type mismatch: required '@NonNull Foo' but the provided value is null</div></div> |
− | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] | + | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Null type mismatch: required '@NonNull Foo' but the provided value is specified as @Nullable</div></div> ''not in JDT 3.8.0 - see {{bug|337977#c19}} |
− | * <div class="b"><div class="hover">[[Image: | + | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Null type mismatch: required '@NonNull Foo' but the provided value is inferred as @Nullable</div></div> |
+ | * <div class="b"><div class="hover">[[Image:Quickfix_warning_obj.gif]] Null type safety: The expression of type Foo needs unchecked conversion to conform to '@NonNull Foo'</div></div> | ||
*: '''Fixable for these locations: return statements''': | *: '''Fixable for these locations: return statements''': | ||
*: Note that the mentioned @NonNull declaration may be implicit via an applicable default | *: Note that the mentioned @NonNull declaration may be implicit via an applicable default | ||
− | *: In cases | + | *: In cases 3) and 4) use only with care: the compiler has no clear indication if @Nullable was actually intended or not |
*: The fix is: | *: The fix is: | ||
*: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Declare method return as @Nullable</div></div> | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Declare method return as @Nullable</div></div> | ||
− | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] | + | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Null comparison always yields false: The variable x is specified as @NonNull</div></div> |
− | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] | + | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Redundant null check: The variable x is specified as @NonNull</div></div> |
− | *: '''Fixable for these locations: | + | *: '''Fixable for these locations: null check for a method parameter''' |
*: The fix is: | *: The fix is: | ||
*: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Declare method parameter as @Nullable</div></div> | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Declare method parameter as @Nullable</div></div> | ||
Line 115: | Line 114: | ||
*: Note again that the mentioned @NonNull declaration may be due to a default. | *: Note again that the mentioned @NonNull declaration may be due to a default. | ||
*: Possible fixes are: | *: Possible fixes are: | ||
− | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] | + | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Change return type of foo(..) to '@NonNull'</div></div> |
− | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] | + | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Change return type of overridden foo(..) to '@Nullable'</div></div> |
* <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Illegal redefinition of parameter a, inherited method from SuperFoo declares this parameter as @Nullable</div></div> | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Illegal redefinition of parameter a, inherited method from SuperFoo declares this parameter as @Nullable</div></div> | ||
* <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Illegal redefinition of parameter a, inherited method from SuperFoo does not constrain this parameter.</div></div> | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Illegal redefinition of parameter a, inherited method from SuperFoo does not constrain this parameter.</div></div> | ||
Line 122: | Line 121: | ||
*: The second form occurs when no null default applies at the scope of the super method. | *: The second form occurs when no null default applies at the scope of the super method. | ||
*: Possible fixes are: | *: Possible fixes are: | ||
− | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] | + | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Change parameter type to '@Nullable'</div></div> |
− | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] | + | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Change parameter type in overridden 'foo(..)' to '@NonNull'</div></div> |
− | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Missing null annotation: inherited method from | + | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Missing non-null annotation: inherited method from SuperClass declares this parameter as @NonNull</div></div> |
− | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Missing | + | * <div class="b"><div class="hover">[[Image:Quickfix_error_obj.gif]] Missing nullable annotation: inherited method from SuperClass declares this parameter as @Nullable</div></div> |
*: '''Location: Parameter declaration of an overriding method''' | *: '''Location: Parameter declaration of an overriding method''' | ||
− | *: | + | *: Quick fix is either of: |
− | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] | + | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Change parameter type to @NonNull</div></div> |
− | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] | + | *: <div class="b"><div class="fix">[[Image:Correction_change.gif]] Change parameter type to @Nullable</div></div> |
− | These | + | These quick fixes can be applied... |
* individually (Ctrl-1) | * individually (Ctrl-1) | ||
* all occurrences per file (via the hover) | * all occurrences per file (via the hover) | ||
* all occurrences (via context menu in the Problems view) | * all occurrences (via context menu in the Problems view) | ||
− | Note, that some | + | Note, that some quick fixes require to modify another compilation unit (file) than the one |
where the problem was observed. For these quickfixes the current implementation doesn't | where the problem was observed. For these quickfixes the current implementation doesn't | ||
support fixing several equal issues in bulk (for the technical background see | support fixing several equal issues in bulk (for the technical background see |
Revision as of 10:34, 21 June 2012
This page discusses current work on improving the static null analysis of the JDT compiler.
The initial master bug for this work was bug 186342.
Contents
Introduction
The static analysis of the JDT compiler detects many potential programming problems related to the null-ness of variables: dereferencing a null value (-> NPE), redundant null checks etc.
However, the analysis in JDT ≤ 3.7 is restricted to flow analysis within one method. No assumptions can be made about
- arguments flowing into a method
- return values from method calls and
- field reads.
In order to include these elements in the analysis one could either
- use whole program analysis (very expensive - not feasible for a (incremental) compiler)
- explicit contracts via an extended type system or annotations
The second option is well explored in research and some existing tools (like the Checker Framework, JML, FindBugs) already introduce specific annotations to this end.
One could argue that advanced analysis should be left to specialized tools but having something like this in the JDT compiler should show two benefits:
- feedback is more immediate and it is available for all JDT users without installing more software
- analysis might be more precise than some existing tools provide, because the actual flow analysis in the JDT compiler is already pretty strong (unproven claim).
A preparatory discussion of the design space can be found here: /Brainstorming.
See also the recording of this ECE 2011 session: Bye, bye, NPE
Actual Strategy in the JDT
By default the JDT does not support inter-procedural null analysis, however, starting with 3.8 the JDT can be configured to use annotations for extended null checking.
Specifying nullness
Null annotations in method signatures can be interpreted as null contracts, however, a more general approach considers null annotations as an extension of the type system. Eventually - that is once JSR 308 can be used - all type references should either include or exclude null, which allows for complete checking of any possible dereferencing of null. In other words, a fully annotated program which passes the type checker will never raise an NPE at runtime.
To achieve this guarantee two annotations are used. The specific annotations types can be selected as a preference, but the following defaults are provided (see #Compiler configuration explained):
For any variable who's type is annotated with @NonNull (or the configured equivalent) the following rules apply:
- It is illegal to bind null or a value that can be null to the variable. (For fields and local variables this applies to initialization and assignments, for method argument binding a value means to pass an actual argument in a method call).
- It is legal and safe to dereference such a variable for accessing a field or a method of the bound object.
For any variable who's type is annotated with @Nullable (or the configured equivalent) the following rules apply:
- It is legal to bind null or a value that can be null to the variable (see details above).
- It is illegal to dereference such a variable for either field or method access.
The above rules imply that the value from a @NonNull variable can be bound to a variable annotated with @Nullable, but the opposite direction is generally illegal. Only after an explicit null check can a @Nullable variable be treated as being @NonNull for the sake of binding to another @NonNull variable or for dereferencing.
For interaction with inheritance see Null Contract Inheritance.
Usage
In order to try the new analysis against any existing Java project the following steps should help:
- Open the compiler preferences for your project:
- Ensure compliance is 1.5 or higher
- Find the section Null analysis and select Enable annotation-based null analysis
- You will be prompted to update the severity of some null-related problems, this is recommended.
- Apply any of the annotations
@NonNull
,@Nullable
or@NonNullByDefault
in your code.- The annotation will be unresolvable at first, but a quick fix is offered to update the project setup:
- (see also: Help: buildpath setup)
- Copy library with default annotations to build path (plain Java projects), or:
- Add library with default annotations to build path (Plug-in projects).
- Define
@NonNull
as the default at the granularity of your choice (package/type):- package: add a file
package-info.java
with contents like this:-
@NonNullByDefault package org.my.pack.age;
-
- type: add
@NonNullByDefault
to the type declaration.
- package: add a file
- At this point you should see plenty of new errors and warnings
Cleaning up
When applying the new analysis to a big existing project, the sheer number of new problems may look intimidating but that's where quick fixes will come to the rescue. Currently the following problems offer a quickfix:
- not in JDT 3.8.0 - see bug 337977#c19
-
- Fixable for these locations: return statements:
- Note that the mentioned @NonNull declaration may be implicit via an applicable default
- In cases 3) and 4) use only with care: the compiler has no clear indication if @Nullable was actually intended or not
- The fix is:
-
- Fixable for these locations: null check for a method parameter
- The fix is:
- Otherwise a null check may indeed be unnecessary and should be deleted.
-
- Location: declaration of an overriding method
- Note again that the mentioned @NonNull declaration may be due to a default.
- Possible fixes are:
-
- Location: Parameter declaration of an overriding method
- The second form occurs when no null default applies at the scope of the super method.
- Possible fixes are:
-
- Location: Parameter declaration of an overriding method
- Quick fix is either of:
These quick fixes can be applied...
- individually (Ctrl-1)
- all occurrences per file (via the hover)
- all occurrences (via context menu in the Problems view)
Note, that some quick fixes require to modify another compilation unit (file) than the one where the problem was observed. For these quickfixes the current implementation doesn't support fixing several equal issues in bulk (for the technical background see bug 337977).
Compiler configuration explained
By default the JDT does not recognize any null annotations but it can be configured to do so.
Up-to-date documentation for the new configuration options can be found in the online help (Eclipse 3.8 M4 and greater):
- Java development user guide > Reference > Preferences > Java > Compiler > Errors/Warnings
- scroll down to Null analysis
See also Options (work in progress).
Defaults at different levels
If no null annotations are used, the compiler uses the original Java semantics, where the following is legal for all variables of reference types:
- assign
null
, and - dereference without check.
The preference "Use non-null as workspace-wide default" allows to globally change this so that any declaration (currently: method parameters and method return) to which no null annotation applies will be considered as nonnull.
For more fine-grained control an additional annotation can be used. The qualified type name of this annotation can be configured using the preference "'NonNullByDefault' annotation".
The built-in value for these preference is org.eclipse.jdt.annotation.NonNullByDefault
.
- This annotation takes an optional boolean parameter; when set to false this causes the annotation to cancel a default that may possible apply at the current location. This is useful when, e.g., sub-classing a legacy class without null annotation, where the sub-class sits in a place that would otherwise apply non-null as the default, which would make all overrides incompatible with inherited methods.
This annotation can be applied to any package, Java type or method and affects all method returns and parameters with undefined null status within their scope. (More locations will be supported in the future, but local variables are intentially unaffected by any default).
Done
At the current point the following bugs are resolved:
- bug 186342.- [compiler][null] Using annotations for null checking
- bug 334455 - UI for new preferences regarding null annotations (plus a dup: bug 364815).
- bug 334457 - [compiler][null] check compatibility of inherited null contracts
- bug 331647 - [compiler][null] support flexible default mechanism for null-annotations
- bug 365208 - [compiler][batch] command line options for annotation based null analysis
Future
The following bugzillas address future improvements of the above strategy:
- bug 337977 - [quick fix] Add quickfixes for null annotations - planned for 3.8 M6
- bug 331649 - [compiler][null] consider null annotations for fields
- bug 331651 - [compiler][null] Support nullity profiles for libraries