Bug 373032 - [compiler][null][loop] wrong warning in while/for: "Redundant null check: The variable b can only be null at this location"
Summary: [compiler][null][loop] wrong warning in while/for: "Redundant null check: The...
Status: CLOSED WONTFIX
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.8   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact: Stephan Herrmann CLA
URL:
Whiteboard: stalebug
Keywords: helpwanted
Depends on:
Blocks:
 
Reported: 2012-03-01 19:08 EST by Julian Ladisch CLA
Modified: 2020-02-04 16:59 EST (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Julian Ladisch CLA 2012-03-01 19:08:02 EST
Build Identifier: 3.7.3.v20120119-1537

public class X {
  void foo() {
    Object a = null, b = null;
    while (a == null || b == null) { // must not complain here
      b = a;
      a = new Object();
    }
  }
}

public class Y {
  void foo() {
    Object a = null, b = null;
    do {
      b = a;
      a = new Object();
    } while (a == null || b == null); // must not complain here
  }
}

public class Z {
  void foo() {
    Object a = null, b = null;
    for (; a == null || b == null; ) { // must not complain here
      b = a;
      a = new Object();
    }
  }
}

Each example results in "Redundant null check: The variable b can only be null at this location".

Reproducible: Always
Comment 1 Srikanth Sankaran CLA 2012-03-01 20:48:11 EST
Stephan, please follow up, thanks.
Comment 2 Stephan Herrmann CLA 2012-03-02 08:20:33 EST
Wow, fixed number of passes during loop analysis is hitting the cealing.
We need to do s.t. about this.
Comment 3 Srikanth Sankaran CLA 2012-03-02 23:43:04 EST
(In reply to comment #2)
> Wow, fixed number of passes during loop analysis is hitting the cealing.
> We need to do s.t. about this.

Sorry, this is unclear. Could you elaborate upon this Stephan ?
Comment 4 Ayushman Jain CLA 2012-03-03 01:22:37 EST
(In reply to comment #3)
> (In reply to comment #2)
> > Wow, fixed number of passes during loop analysis is hitting the cealing.
> > We need to do s.t. about this.
> 
> Sorry, this is unclear. Could you elaborate upon this Stephan ?

We only analyse the loop statements one time, and record every comparison against null for deferred checking. Then we make another pass on only the deferred variables and see how their nullness was affected by all statements in the loop. But that doesn't help in all cases. Eg, here in the first pass, b's nullness changed only once, at b = a. But in first iteration a was only null at that point. So, when we come back to b for the deferred check, we see (1) it was originally null, and (2) it was assigned null in the loop. So we complain. If we'd made a second pass, we'd have seen an assignment to a non-null value as well. :)
Comment 5 Srikanth Sankaran CLA 2012-03-05 23:28:10 EST
(In reply to comment #4)
> (In reply to comment #3)
> > (In reply to comment #2)
> > > Wow, fixed number of passes during loop analysis is hitting the cealing.
> > > We need to do s.t. about this.
> > 
> > Sorry, this is unclear. Could you elaborate upon this Stephan ?
> 
> We only analyse the loop statements one time, and record every comparison
> against null for deferred checking. Then we make another pass on only the
> deferred variables and see how their nullness was affected by all statements in
> the loop. But that doesn't help in all cases.

Thanks for the explanation, Ayush. That tells me that this issue has existed
from day one and is not a recent regression. Can you confirm ? If so, then
I would target this for early 3.9.
Comment 6 Stephan Herrmann CLA 2012-03-06 07:28:19 EST
(In reply to comment #5)
> Thanks for the explanation, Ayush. That tells me that this issue has existed
> from day one and is not a recent regression. Can you confirm ? If so, then
> I would target this for early 3.9.

I agree with Ayush's explanation and I'm pretty sure this never worked.
Still this is an ugly bug and I'll start experimenting with possible solutions as soon as I find the time. I'll post an update once I can estimate the complexity of required changes.
Comment 7 Holger Klene CLA 2012-07-08 16:48:00 EDT
Naively I would think it sufficient, to iterate any loop until no variable changes nullness any more. But on second thought I wonder how that performs on a loop like this:

Object a = null, b = new Object();
while (...) {
	Object temp = a;
	a = b;
	b = temp;
}

As the nullness toggles on every iteration, my naive suggestion won't ever finish analyzing that loop?!?
Comment 8 Stephan Herrmann CLA 2014-11-27 18:46:05 EST
Not marking as blocking the new umbrella bug 453483, because this one smells like correlation analysis, which is significantly harder than bug 453483 (which is already hard enough).
Comment 9 Sergey Olefir CLA 2015-12-14 05:56:21 EST
I'm not sure if this is the same issue, but here's another example where null-analysis is wrong for the loop (also there's another null-annotation bug with 'array' field):

@org.eclipse.jdt.annotation.NonNullByDefault
public class TestNullAnno {
	
//	@NonNull		// Uncommenting this causes warning: The nullness annotation is redundant with a default that applies to this location
					// Not uncommenting this causes warning in getArrayValue() below
	private static String[] array = new @NonNull String[3];

	public static void main(String[] args)
	{
		Map<String, String> map = new HashMap<>();
		
		map.put("key", "value");
		
		System.out.println(getKeyByValue(map, "value"));
	}
	
	private static <K, V> K getKeyByValue(Map<K, V> map, @Nullable V value)
	{
		@Nullable K result = null; // some nullability bug? assigning null results in compiler complaining 'result can only be null' below
		for (Entry<K, V> entry : map.entrySet())
		{
			boolean equals;
			if (value == null)
				equals = (entry.getValue() == null);
			else
				equals = value.equals(entry.getValue());
			
			if (equals)
			{
				if (result == null) // Incorrect warning: Redundant null check: The variable result can only be null at this location
					result = entry.getKey();
				else
					throw new IllegalStateException("Multiple matches for looking up key via value [" + value + "]: [" + result + "] and [" + entry.getKey() + "]");
			}
		}
		
		if (result == null) // Incorrect warning: Redundant null check: The variable result can only be null at this location
			throw new IllegalStateException("No matches for looking up key via value [" + value + "]");
		
		return result; // Incorrect warning: Dead code
	}
	
	public static String getArrayValue()
	{
		return array[0];  // If array is not annotated in declaration, we get warning here: Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String'
	}
}
Comment 10 Eclipse Genie CLA 2020-02-03 13:06:36 EST
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.
Comment 11 Stephan Herrmann CLA 2020-02-04 08:44:38 EST
FWIW, the examples in comment 0 still raise undesired warnings, but nobody came up with an implementation strategy to this fix.

The example from comment 9 causes no problems using latest ecj (today's HEAD).
Comment 12 Julian Ladisch CLA 2020-02-04 15:59:01 EST
Reopening because the issue with the examples in comment 0 still exists.

Ayushman Jain suggested this implementation strategy for a fix:
Make two passes that analyze the loop so that assignments of the first pass can be considered during the second pass.

I support this.

This will not fix cases that require more than two passes, but those are very, very rare.
Comment 13 Stephan Herrmann CLA 2020-02-04 16:59:25 EST
(In reply to Julian Ladisch from comment #12)
> Reopening because the issue with the examples in comment 0 still exists.
> 
> Ayushman Jain suggested this implementation strategy for a fix:
> Make two passes that analyze the loop so that assignments of the first pass
> can be considered during the second pass.
> 
> I support this.
> 
> This will not fix cases that require more than two passes, but those are
> very, very rare.

be our guest :)

Unfortunately, even the two pass approach would require huge code changes, because the current one-and-a-half passes implementation is already quite complex. Note that the entire analyseCode phase is implemented in a single traversal of all AST nodes. It will be hard also to allow some statements to be analysed twice.

I should have said "nobody came up with a *feasible* implementation strategy". Two phase analysis is theoretically useful, but not feasible when considering the necessary efforts. Sorry.