Bug 292087 - anonymous class in array member initializer confuses content assist
Summary: anonymous class in array member initializer confuses content assist
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.6   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 3.8 M2   Edit
Assignee: Ayushman Jain CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-10-12 18:28 EDT by Rafał Rzepecki CLA
Modified: 2011-09-13 08:16 EDT (History)
5 users (show)

See Also:
satyam.kandula: review+


Attachments
Patch under consideration. (1.35 KB, patch)
2010-03-26 04:49 EDT, Srikanth Sankaran CLA
no flags Details | Diff
Final patch with tests (4.06 KB, patch)
2010-03-31 02:45 EDT, Srikanth Sankaran CLA
no flags Details | Diff
proposed fix (3.21 KB, patch)
2010-05-11 04:57 EDT, Ayushman Jain CLA
no flags Details | Diff
proposed fix v2.0 + tests (19.86 KB, patch)
2011-04-19 08:15 EDT, Ayushman Jain CLA
no flags Details | Diff
updated test (1.33 KB, patch)
2011-04-21 13:14 EDT, Ayushman Jain CLA
no flags Details | Diff
patch updated for HEAD (20.57 KB, patch)
2011-08-12 05:53 EDT, Ayushman Jain CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Rafał Rzepecki CLA 2009-10-12 18:28:06 EDT
User-Agent:       Mozilla/5.0 (compatible; Konqueror/4.3; Linux) KHTML/4.3.1 (like Gecko) SUSE
Build Identifier: I20080617-2000

One (commented) example is worth a thousand words:

public class Test extends Thread {
	private class Nested extends LinkedList<String> {
		// here content assist works correctly
		private Runnable member[] = { new Runnable() {
			public void run() {};
		}
		// here it thinks you're not in the array initializer anymore
		};
		// here it thinks you're not in Nested anymore
	}
	// here it thinks you're not in Test anymore
}


Reproducible: Always
Comment 1 Dani Megert CLA 2009-10-13 08:48:46 EDT
Looks like a JDT Core issue.
Comment 2 Srikanth Sankaran CLA 2010-01-20 05:19:14 EST
Ayush, please investigate -- Thanks.
Comment 3 Srikanth Sankaran CLA 2010-03-26 04:49:07 EDT
Created attachment 163070 [details]
Patch under consideration.

This patch fixes the current problem and also passes
RunAllCompletionTests.

Patch does not include automated tests - these need
to be created. As well, more testing is needed in the
area of content assist in the presence of anonymous
types.

See that with or without patch, there is a bogus
proposal with this example:

import java.util.LinkedList;

public class Test extends Thread {
    private class Nested extends LinkedList<String> {
    	int field;
        // here content assist works correctly
        private Test member[] = { new Test(){
        	void abc() {}
        	Nest|  // completion here proposes the default ctor for Nested
        	       // which makes no sense.
            }
        };
    }
}

Ayush, can you take over from here ? Please raise separate defects for any
new issues you uncover unless it is very pertinent to the current subject
at hand -- Thanks.
Comment 4 Srikanth Sankaran CLA 2010-03-29 01:56:19 EDT
(In reply to comment #3)

> See that with or without patch, there is a bogus
> proposal with this example:

FYI -- I have raised bug#307337 to track this.
Comment 5 Ayushman Jain CLA 2010-03-31 02:37:50 EDT
(In reply to comment #3)
> Created an attachment (id=163070) [details]
> Patch under consideration.

Patch looks good.
Comment 6 Srikanth Sankaran CLA 2010-03-31 02:45:02 EDT
Created attachment 163490 [details]
Final patch with tests

Ayush, Thanks for the review and the unit test.
Comment 7 Srikanth Sankaran CLA 2010-03-31 02:49:43 EDT
Released in HEAD for 3.6M7
Comment 8 Olivier Thomann CLA 2010-04-26 14:27:36 EDT
There is no completion proposal inside the array initializer.
On line "// here it thinks you're not in the array initializer anymore".
Is this expected ?
Comment 9 Srikanth Sankaran CLA 2010-04-27 07:15:01 EDT
(In reply to comment #8)
> There is no completion proposal inside the array initializer.
> On line "// here it thinks you're not in the array initializer anymore".
> Is this expected ?

Don't quite know what is expected here.

(1) If you attempt completion at the point below, which is
logically the same point as the one you have used, I get 
several proposals, none of which makes sense though:

import java.util.LinkedList;
public class Test extends Thread {
    private class Nested extends LinkedList<String> { 	
        private Runnable member[] = { 	
        // completion here brings up dubious proposals
        new Runnable() {
            public void run() {};
        },
        // completion here brings up no proposals.
       };
        
    }
    
}


(2) Also try the following:

public class Test {
	private Runnable member[] = {
	    // completion here brings up useless proposals

	};	
}

In this case, if we are truly completing inside the initializer,
we should perhaps be using Runnable as the expected type
and filter out the rest as there is no point in proposing the
methods of java.lang.Object as we do now.
Comment 10 Olivier Thomann CLA 2010-04-27 09:14:41 EDT
Should I reopen? I believe the patch did improve the initial test case, but maybe not enough.
Comment 11 Srikanth Sankaran CLA 2010-04-28 01:00:26 EDT
(In reply to comment #10)
> Should I reopen? I believe the patch did improve the initial test case, but
> maybe not enough.

I have raised bug# 310747 to cover the case (2) in comment# 9.
I have reopened the current defect to look into the anomaly
around case (1) in comment# 9 -- Thanks.
Comment 12 Olivier Thomann CLA 2010-04-28 12:55:53 EDT
No need to fix it for M7. Targetting RC1.
Comment 13 Srikanth Sankaran CLA 2010-04-29 02:59:44 EDT
import java.util.LinkedList;

public class Test extends Thread {
    private class Nested extends LinkedList<String> {
        // here content assist works correctly
        private Runnable member[] = { 
        
        // completion works before and after the null
        // more or less the same way.
        // if null were to be replaced by an anonymous type,
        // completion works before it, not after it.
        		
        null,
        
        };   
    }
}

Ayush, please see why and how this case differs from
the original, that should offer a clue about the fix.
Comment 14 Srikanth Sankaran CLA 2010-05-06 22:26:42 EDT
This is a bit more complicated than anticipated and
needs a bit more time for fuller analysis.
Retargetting to 3.6.1
Comment 15 Ayushman Jain CLA 2010-05-11 04:57:30 EDT
Created attachment 167879 [details]
proposed fix

The above fix fixes the case at hand and passes all tests, but I'm not sure if it does fix the root cause of the problem. I'm also not sure whether its a good idea to use goForBlockStatements in this case, since its usually used for parsing inside method bodies during recovery.

Also, Srikanth and I, after some analysis of the recovery mechanism in code assist, have observed that the parser doesnt seem to take a coherent path while trying to parse an anonymous class in an array initializer. In the case given below:

private X[] field = {
      new X(){},
      | // Completion asked here
}
Normally, we would expect the parser to first figure out new X(){} as an anonymous class declaration (which it does) and then figure out the whole thing as a variable initializer (which it currently fails to do). This has no problems if in place of anonymous class we simply had new X().

David, what do you think about the above issues?
Comment 16 Ayushman Jain CLA 2010-05-11 04:59:59 EDT
David, it'll be really helpful if you could look at the patch and comment. Thanks!
Comment 17 Srikanth Sankaran CLA 2010-08-11 06:29:10 EDT
Since only a small part of the original problem remains
and the fix for that part seems to be complicated enough
IMO, it is not a good idea to target this for the maintenance
stream. Will look at this for 3.7
Comment 18 Ayushman Jain CLA 2011-04-19 08:15:57 EDT
Created attachment 193572 [details]
proposed fix v2.0 + tests

This fix looks better than the fix above.
Basically, there are 2 ways in which parsing can discover a completion node - (a)either in the diet parse mode at org.eclipse.jdt.internal.codeassist.CompletionEngine.complete(ICompilationUnit, int, int, ITypeRoot) line 1736.
(b)or during the block statements parsing at line 1842 int the same method.

The block statements parsing is particularly done for completion inside methods, class intializers or member types (see org.eclipse.jdt.internal.codeassist.impl.Engine.parseBlockStatements(TypeDeclaration, CompilationUnitDeclaration, int)).

In the given case, we're completing inside a field array initializer, which is not covered by the block statements parsing here. A field array initializer is first found during the diet parsing, and thats where its statements should also get parsed in the case of completion, because the completion node may be inside the array intializer. This explains the change in AssistParser.resumeAfterRecovery() which now goes for BlockStatementsOpt parsing if we're in an array initializer. The use of BlockStatementsOpt has been inspired from how the block statements are parsed in org.eclipse.jdt.internal.codeassist.impl.AssistParser.parseBlockStatements(Initializer, TypeDeclaration, CompilationUnitDeclaration).

The change in RecoveredField.java is a bit unrelated and is done because the alreadyCompletedFieldInitialization field gets set to true even if we havent exited the initializer yet. (this is shown in CompletionTest.test292087d() and CompletionparserTest.test292087b())

The other change in CompletionParser.recoveryExitFromVariable is to make sure that when focus is shifted from the field initializer to the corresponding class, the elementKindStack has the right topmost element kind.
Comment 19 Ayushman Jain CLA 2011-04-19 08:17:47 EDT
Satyam, can you please review. Thanks!
Comment 20 Satyam Kandula CLA 2011-04-21 07:35:57 EDT
Changes look good for me.  
+1.
Comment 21 Ayushman Jain CLA 2011-04-21 09:44:26 EDT
Released in HEAD for 3.7M7
Comment 22 Olivier Thomann CLA 2011-04-21 12:26:20 EDT
This seems to break test org.eclipse.jdt.core.tests.compiler.parser.DietRecoveryTest.test75() in the BETA_JAVA7 branch.
Could you please investigate?
Thanks.

The same test passes fine in HEAD.
Comment 23 Olivier Thomann CLA 2011-04-21 12:42:20 EDT
The problem seems to come from the method:
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=292087
protected boolean isInsideArrayInitializer(){
	int i = this.elementPtr;
	if (i > -1 && this.elementKindStack[i] == K_ARRAY_INITIALIZER) {
		return true;
	}
	return false;
}
added in CompletionParser.
If I remove it, the test passes.

I'd like to identify why this is different from HEAD.
Comment 24 Olivier Thomann CLA 2011-04-21 12:48:05 EDT
(In reply to comment #23)
> I'd like to identify why this is different from HEAD.
I was out of synch on HEAD. Once I synchronized, HEAD is also broken.
Comment 25 Olivier Thomann CLA 2011-04-21 12:54:06 EDT
Because isInsideArrayInitializer() returns true for test75, the recovery is different. It doesn't go for headers, but it goes for goForBlockStatementsopt().

I am investigating if the recovery looks good.
Comment 26 Ayushman Jain CLA 2011-04-21 13:14:17 EDT
Created attachment 193863 [details]
updated test

The recovery has changed inside the array initializer for int[] ii because it has an array initializer. The new parsed unit that we get is valid because now for completion, we go inside the array initializers and parse the statements as well. This is done via the goForBlockStatementsOpt() in the AssistParser.resumeAfterRecovery() since isInsideArrayInitializer() returns true. Earlier, we used to skip the contents of the array init and thus never found the class Local. 

Hence, updating the test is sufficient.
Comment 27 Ayushman Jain CLA 2011-04-21 13:15:28 EDT
Released updated test in HEAD.
Comment 28 Dani Megert CLA 2011-04-22 03:04:27 EDT
Are we 100% sure about this fix? It caused regressions several times now and we're just a few days away from RC0.
Comment 29 Ayushman Jain CLA 2011-04-22 03:20:35 EDT
(In reply to comment #28)
> Are we 100% sure about this fix? It caused regressions several times now and
> we're just a few days away from RC0.

Thats not quite correct. Any fix for this didnt really cause a regression, but as mentioned in comment 10, improved the assist but not enough. The latest test failure yesterday was because the recovery has got changed in one case and the test for that had not been updated.

So i'd say its safe.
Comment 30 Srikanth Sankaran CLA 2011-04-27 02:39:05 EDT
My recollection (could be faulty as I have more gray hairs than
black :-() was that we had decided the best time to release this
was in the M1/M2 frame of 3.7. That window gone, it would have been
better to have waited for 3.8 M1/M2.

Again my recollection was that though there was some uncertainty
about the fix, it was fairly well localized.
Comment 31 Olivier Thomann CLA 2011-04-27 13:24:43 EDT
Reopening.
For safety reason, we should revert that fix. I am not too confident when one parser reports a different tree from other parsers. We might introduce subtle inconsistencies and this is late in the game to get fixed.
Targeting 3.8M1 when available is better and safer.
Comment 32 Ayushman Jain CLA 2011-04-27 15:56:13 EDT
(In reply to comment #31)
> Targeting 3.8M1 when available is better and safer.

Reverted fix in HEAD and BETA_JAVA7. Will re-release in 3.8M1.
Comment 33 Ayushman Jain CLA 2011-08-12 05:53:23 EDT
Created attachment 201378 [details]
patch updated for HEAD

Patch to be released for 3.8M2
Comment 34 Ayushman Jain CLA 2011-08-12 14:36:46 EDT
Released in HEAD for 3.8M2.
Comment 35 Stephan Herrmann CLA 2011-09-13 08:16:00 EDT
Verified for 3.8M2 using build I20110911-2000.