Bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop)
Summary: [compiler][null] detect null problems in nested code (local class inside a loop)
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.8   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 3.8 M3   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-10-08 13:07 EDT by Stephan Herrmann CLA
Modified: 2011-10-24 03:55 EDT (History)
2 users (show)

See Also:


Attachments
test & proposed fix (7.17 KB, patch)
2011-10-08 13:35 EDT, Stephan Herrmann CLA
no flags Details | Diff
more tests & improved fix (15.11 KB, patch)
2011-10-08 16:27 EDT, Stephan Herrmann CLA
no flags Details | Diff
additional test & fix (5.01 KB, patch)
2011-10-09 11:43 EDT, Stephan Herrmann CLA
no flags Details | Diff
test & additional fix (12.44 KB, patch)
2011-10-12 18:28 EDT, Stephan Herrmann CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Stephan Herrmann CLA 2011-10-08 13:07:40 EDT
The null analysis doesn't find issues in code like this:

    void foo() {
        final String s1 = null;
        for (int i=0; i<4; i++)
            new Runnable() {
                public void run() {
                     s1.toString();
                }
            }.run();
    }

This is because information is dropped at two locations in the implementation:
When analysing the body of a loop the flowInfo is set to the
flowInfo.nullInfoLessUnconditionalCopy() because null-analysis inside loops
must be deferred.

However, inside TypeDeclaration.internalAnalyseCode(..) we also cut the
connection among flow contexts that would otherwise do the deferred checking:
  method.analyseCode(this.scope, null, flowInfo.copy());
the second argument (null) means that the method is analysed within a
parent-less flow context.

Both details together imply that no connection can be made between the
state outside the loop and insdide the method in the local class, although
this could easily be made given that the local in question (s1) must be
final by construction.
Comment 1 Stephan Herrmann CLA 2011-10-08 13:35:35 EDT
Created attachment 204806 [details]
test & proposed fix

I was briefly considering special treatment for final varialbles inside
FlowInfo.nullInfoLessUnconditionalCopy(). This would probably work OK
since there's no reason to defer analysis of final variables, however, the
change would be non-trivial (respecting extraBits inside FlowInfo, 
avoiding double reporting regarding final variables both immediately
*and* deferred etc.)

Changing the other location in question looks much easier and does achieve
the desired outcome: by passing down the enclosing flowContext of a local
type into analysing its methods we can indeed establish the missing link.

I'm currently running the tests.
Comment 2 Stephan Herrmann CLA 2011-10-08 16:27:16 EDT
Created attachment 204814 [details]
more tests & improved fix

This patch includes tests also for
 - constructors
 - initializers
 - types nested in a try-finally

With only small changes the fix also covers these issues.

Passes all tests in compiler.regression (except for the unrelated regressions
mentioned in bug 359943 comment 17) and AllJavaModelTests
Comment 3 Stephan Herrmann CLA 2011-10-08 16:31:09 EDT
Released as commit f103afa2b75b8198880ca7616628c69d234642fd for 3.8 M3.
Comment 4 Stephan Herrmann CLA 2011-10-09 10:53:08 EDT
Reopening as this caused regressions in org.eclipse.jdt.core.tests.eval.
Investigating.
Comment 5 Stephan Herrmann CLA 2011-10-09 11:43:20 EDT
Created attachment 204838 [details]
additional test & fix

Here's what happened in VariableTest et al:

We were analysing a deeply nested return statement:

class declaration
- static initializer
  - try-catch
     - local class declaration
       - method
          - return statement

Inside ReturnStatement.analyseCode we have a loop that traverses the
parent chain of flow contexts, which would normally iterate until a null parent
is found. If an initializer context is found, we raise an error.

With the change from this bug we were simply traversing too far, finding the
initializer as a parent of the return statement. 
The new patch fixes this by simply breaking from the loop when we hit the 
(innermost) enclosing method.

I checked for similar loops, but could only find BreakStatement and 
ContinueStatement which already properly stop traversing when the 
targetContext is found. No action required here.

Thus the fix consists of one more break condition inside the affected loop.

I added a test that checks the three statements (return/break/continue) 
in the given nesting structure. 
Currently rerunning the tests.
Comment 6 Stephan Herrmann CLA 2011-10-09 12:53:09 EDT
Released additional fix:
commit 2d0b70991183be97a67695fa28c23b0cb3fbbdbf for 3.8 M3.
Comment 7 Ayushman Jain CLA 2011-10-12 09:50:51 EDT
This fix introduced a regression, it causes ECJ to compile the following successfully, while javac rejects it

package p;
class F {
                 
                 void f () {
                 while (true) {
                 class Inner {
                 { if (true) break; }
                 }
                 new Inner();
                 }
                 } 

}
Comment 8 Stephan Herrmann CLA 2011-10-12 18:28:21 EDT
Created attachment 205072 [details]
test & additional fix

(In reply to comment #7)
> This fix introduced a regression, it causes ECJ to compile the following
> successfully, while javac rejects it

I'm sorry! I failed to see that many other loops over the parent chain,
which exist inside the flow package, should not cross the newly introduced
link either.

I believe it's safest to let all these loops use the same check (inside
new method FlowContext.getLocalParent()) and rather return null than
an out-of-scope parent. The effect should be that most analyses are 
performed exactly as before this bug, except for deferred null analysis 
which needs to see null status even from outside the current class.

This patch actually reverts part of the patch from comment 5 and replaces 
it with the more general approach.
Comment 9 Ayushman Jain CLA 2011-10-13 05:18:55 EDT
Thanks for the prompt follow up Stephan. :)
The fix looks good.
Comment 10 Stephan Herrmann CLA 2011-10-13 15:05:52 EDT
Released additional fix from comment 8:
commit 1adff7e346cbd79e4672d3b7b0f319fb8170d9e9 for 3.8 M3
Comment 11 Srikanth Sankaran CLA 2011-10-24 03:55:18 EDT
Verified for 3.8 M3 using build id: N20111022-2000