Bug 437881 - Bogus Warning "Redundant null check"
Summary: Bogus Warning "Redundant null check"
Status: CLOSED DUPLICATE of bug 331651
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.4   Edit
Hardware: PC Windows 7
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-06-22 11:30 EDT by Clovis Seragiotto CLA
Modified: 2014-06-26 15:20 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Clovis Seragiotto CLA 2014-06-22 11:30:19 EDT
With Null analysis on, the compiler issues two bogus warnings for the following program:

@org.eclipse.jdt.annotation.NonNullByDefault
class Foo {
    static void bar(java.util.Queue<String> q) {
        if (q.poll() == null) {
            System.out.println("The queue is empty");
        }
    }
}
The warnings are "Dead code" and "Redundant null check: comparing '@NonNull String' against null.

It is true that the queue does not contain nulls, but q.poll may return null anyway (IF java.util.Queue had been annotated, the method poll would be @Nullable, but as it has not been annotated, the compiler shouldn't assume anything about this method).

Workaround: use @org.eclipse.jdt.annotation.NonNullByDefault({DefaultLocation.PARAMETER, DefaultLocation.RETURN_TYPE, DefaultLocation.FIELD})
Comment 1 Stephan Herrmann CLA 2014-06-22 12:18:40 EDT
This works exactly as designed and emphasizes the importance of bug 331651.

I think you clearly understood the underlying reasoning, but let me be explicit:

The declaration "E poll()" promises that the method will return a value of the actual type substituted for E.

By using @NonNullByDefault of o.e.j.annotation version 2.x you are declaring the variable 'q' to have type "@NonNull Queue<@NonNull String>", hence 'E' is substituted with '@NonNull String', hence poll() is declared to return '@NonNull String'. For remove() this is the correct semantics, for poll() this is wrong.

Unfortunately we don't have any hints to distinguish a situation where a type variable is intentionally used *unannotated*, to let the client's substitution take effect, from a situation that simply hasn't received the desired blessing of null annotations. Any change we would be doing here to accommodate the 'legacy' case would break the indented semantics for the other (good) case.

Unless I'm missing an option how to distinguish remove() and poll() I don't see anything we can do, other than bug 331651 (with high priority).


The workaround is fine, as it signals: we do not trust the analysis on @NonNull type arguments. 
Placing the modified NonNullByDefault on the affected method should suffice, to ensure that other methods of this type still benefit from more complete checking with @NonNull type arguments.
Comment 2 Clovis Seragiotto CLA 2014-06-23 09:06:58 EDT
An even funnier example:

import java.util.Optional;

@org.eclipse.jdt.annotation.NonNullByDefault
class Foo {
    static void bar() {
        Optional<String> o = Optional.ofNullable(foo());
        System.out.println(o.orElse("undefined"));
    }
    
    static @org.eclipse.jdt.annotation.Nullable String foo() {
        return null;
    }
}

line 6: Null type mismatch (type annotations): required '@NonNull String' but this expression has type '@Nullable String'
Comment 3 Stephan Herrmann CLA 2014-06-26 15:19:46 EDT
Here we give lead to regular type inference: with a target type of Optional<@NonNull String> we infer the <T> of 'ofNullable()' to '@NonNull String', hence the method expects a nonnull string.

Will be fixed when adding these (external) annotations to Optional:

  <T> Optional<@NonNull T> ofNullable(@Nullable T value)

eventually ...
Comment 4 Stephan Herrmann CLA 2014-06-26 15:20:37 EDT

*** This bug has been marked as a duplicate of bug 331651 ***