Bug 336451 - "Content Assist" does not complete normally on certain types
Summary: "Content Assist" does not complete normally on certain types
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.6.1   Edit
Hardware: PC Linux
: P3 major (vote)
Target Milestone: 3.7 RC1   Edit
Assignee: Ayushman Jain CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-02-06 07:27 EST by Alexey Titov CLA
Modified: 2011-05-12 09:16 EDT (History)
3 users (show)

See Also:
Olivier_Thomann: review+


Attachments
proposed fix (1.37 KB, patch)
2011-05-09 14:20 EDT, Ayushman Jain CLA
no flags Details | Diff
proposed fix v1.0 + regression tests (2.93 KB, patch)
2011-05-10 06:25 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 Alexey Titov CLA 2011-02-06 07:27:54 EST
What steps will reproduce the problem?
1.  Create the following example:

public class EclipseTest {
	private static interface InvokerIF{
		public <T extends ArgIF> T invoke(T arg) throws Exception;
	}
	
	private static class Invoker implements InvokerIF{
		public <T extends ArgIF> T invoke(T arg){
			return arg;
		}
	}
	
	private static interface ArgIF{
	}
	
	private static interface ArgIF2<C> extends ArgIF{
		
	}
	private static class ArgImpl<C> implements ArgIF2<C>{
		public ArgImpl() {
			super();
		}
	}
	public static void main(String[] args) throws Exception {
		InvokerIF test = new Invoker();
		test.invoke(new ArgImpl)
	}
}

2.  Place cursor after "new ArgImpl", before ")"
3.  Call "Content Assist" by pressing Ctrl+space

Note: if you change 

		public <T extends ArgIF> T invoke(T arg) throws Exception;
 
to

		public <T extends ArgIF> T invoke(T arg);

"Content Assist" works as expected.

-- Error Details --
Date: Sun Feb 06 15:12:48 MSK 2011
Message: org.eclipse.jdt.core.dom.MethodBinding cannot be cast to org.eclipse.jdt.core.dom.ITypeBinding
Severity: Error
Product: Eclipse 1.3.1.20100913-1228 (org.eclipse.epp.package.jee.product)
Plugin: org.eclipse.ui
Session Data:
eclipse.buildId=M20100909-0800
java.version=1.6.0_20
java.vendor=Sun Microsystems Inc.
BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=ru_RU
Framework arguments:  -product org.eclipse.epp.package.jee.product
Command-line arguments:  -os linux -ws gtk -arch x86_64 -product org.eclipse.epp.package.jee.product


Exception Stack Trace:
java.lang.ClassCastException: org.eclipse.jdt.core.dom.MethodBinding cannot be cast to org.eclipse.jdt.core.dom.ITypeBinding
	at org.eclipse.jdt.internal.ui.text.java.LazyGenericTypeProposal.getExpectedType(LazyGenericTypeProposal.java:612)
	at org.eclipse.jdt.internal.ui.text.java.LazyGenericTypeProposal.computeTypeArgumentProposals(LazyGenericTypeProposal.java:308)
	at org.eclipse.jdt.internal.ui.text.java.LazyGenericTypeProposal.apply(LazyGenericTypeProposal.java:205)
	at org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal.apply(AbstractJavaCompletionProposal.java:317)
	at org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal.apply(AbstractJavaCompletionProposal.java:332)
	at org.eclipse.jdt.internal.ui.text.java.JavaMethodCompletionProposal.apply(JavaMethodCompletionProposal.java:55)
	at org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal.apply(AbstractJavaCompletionProposal.java:477)
	at org.eclipse.jface.text.contentassist.CompletionProposalPopup.insertProposal(CompletionProposalPopup.java:928)
	at org.eclipse.jface.text.contentassist.CompletionProposalPopup.access$21(CompletionProposalPopup.java:892)
	at org.eclipse.jface.text.contentassist.CompletionProposalPopup$2.run(CompletionProposalPopup.java:495)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
	at org.eclipse.jface.text.contentassist.CompletionProposalPopup.showProposals(CompletionProposalPopup.java:482)
	at org.eclipse.jface.text.contentassist.ContentAssistant.showPossibleCompletions(ContentAssistant.java:1660)
	at org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor$AdaptedSourceViewer.doOperation(CompilationUnitEditor.java:182)
	at org.eclipse.ui.texteditor.ContentAssistAction$1.run(ContentAssistAction.java:82)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
	at org.eclipse.ui.texteditor.ContentAssistAction.run(ContentAssistAction.java:80)
	at org.eclipse.jface.action.Action.runWithEvent(Action.java:498)
	at org.eclipse.ui.commands.ActionHandler.execute(ActionHandler.java:185)
	at org.eclipse.ui.internal.handlers.LegacyHandlerWrapper.execute(LegacyHandlerWrapper.java:109)
	at org.eclipse.core.commands.Command.executeWithChecks(Command.java:476)
	at org.eclipse.core.commands.ParameterizedCommand.executeWithChecks(ParameterizedCommand.java:508)
	at org.eclipse.ui.internal.handlers.HandlerService.executeCommand(HandlerService.java:169)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.executeCommand(WorkbenchKeyboard.java:468)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.press(WorkbenchKeyboard.java:786)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.processKeyEvent(WorkbenchKeyboard.java:885)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.filterKeySequenceBindings(WorkbenchKeyboard.java:567)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.access$3(WorkbenchKeyboard.java:508)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard$KeyDownFilter.handleEvent(WorkbenchKeyboard.java:123)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
	at org.eclipse.swt.widgets.Display.filterEvent(Display.java:1524)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1257)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1282)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1267)
	at org.eclipse.swt.widgets.Widget.sendKeyEvent(Widget.java:1294)
	at org.eclipse.swt.widgets.Widget.gtk_key_press_event(Widget.java:730)
	at org.eclipse.swt.widgets.Control.gtk_key_press_event(Control.java:2841)
	at org.eclipse.swt.widgets.Composite.gtk_key_press_event(Composite.java:734)
	at org.eclipse.swt.widgets.Widget.windowProc(Widget.java:1743)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:4796)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:4360)
	at org.eclipse.swt.internal.gtk.OS._gtk_main_do_event(Native Method)
	at org.eclipse.swt.internal.gtk.OS.gtk_main_do_event(OS.java:8168)
	at org.eclipse.swt.widgets.Display.eventProc(Display.java:1238)
	at org.eclipse.swt.internal.gtk.OS._g_main_context_iteration(Native Method)
	at org.eclipse.swt.internal.gtk.OS.g_main_context_iteration(OS.java:2229)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3159)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2640)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2604)
	at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2438)
	at org.eclipse.ui.internal.Workbench$7.run(Workbench.java:671)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:664)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:115)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:369)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:616)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:619)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:574)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1407)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1383)
Comment 1 Kentarou Fukuda CLA 2011-02-06 13:05:07 EST
Does this relate to JDT core?
Comment 2 Olivier Thomann CLA 2011-02-06 20:37:35 EST
(In reply to comment #1)
> Does this relate to JDT core?
I think so. I believe the underlying problem comes from JDT/Core.

Ayushman, please investigate.
Thanks.
Comment 3 Ayushman Jain CLA 2011-05-09 13:18:15 EDT
This happens because of incorrect parsing of the binding key.
org.eclipse.jdt.internal.core.util.BindingKeyParser.parseMethod() first parses the method signature and then parses the exceptions via the call to parseThrownExceptions(), and then the parsing of the type variables is done in org.eclipse.jdt.internal.core.util.BindingKeyParser.parse(boolean), line 647 after the parseMethod() has returned.

Now the problem is that the parsing of the exceptions in parseThrownExceptions() is done by creating another parser but sharing the same scanner. So when the sub parser starts parsing, it finds the qualified name representing the signature and then when it tries to look for type variables, it also finds them since the signature looks like this:

LEclipseTest$InvokerIF;.invoke<T::LEclipseTest$ArgIF;>(TT;)TT;|Ljava/lang/RuntimeException;:TT;

The :TT; represents the type parameters and since they're immediately followed by the thrown exception, the parser moves the scanner ahead(though the type parameters are not relevant for this parser and it simply ignores it, the scanner cannot be reset back now). This way, when the control returns back to the original parser, and when it tries to look for the type variables of the method invoke(), it cannot find it since the scanner has already moved one place ahead.

A possible fix is to make sure the parser does not look for type variable when parsing thrown exceptions, since exceptions cannot have type variables. I'm testing this fix.
Comment 4 Ayushman Jain CLA 2011-05-09 14:20:42 EDT
Created attachment 195119 [details]
proposed fix

Patch passes all tests
Comment 5 Ayushman Jain CLA 2011-05-10 06:25:47 EDT
Created attachment 195191 [details]
proposed fix v1.0 + regression tests

This patch simply makes sure that the scanner doesnt look for tyoe variables while parsing thrown exceptions
Comment 6 Ayushman Jain CLA 2011-05-10 06:26:41 EDT
Olivier, can you please review? Thanks!

A UI test will have to be added to check that content assist doesn't throw an exception here.
Comment 7 Olivier Thomann CLA 2011-05-10 10:02:17 EDT
+1. The problem does come from the fact parsing the thrown exceptions also consuming the next type variable. The binding key resolver has lots of side-effects everywhere.
Consuming the exception sets the type binding to be the exception type and the enclosing keybinding parser method binding is not remembered. So when scanning the type variable at the same time the exceptions are scanned, there is no match for the type variable binding name, because the type variable of the exception type are retrieved instead of the one from the method binding.

So forcing to skip the type variable scanning while scanning the exception type seems to make sense. Once the exception is scanned, the binding key parser still needs to scan the type variable but in this case the method binding is there to return its type variables.
Comment 8 Ayushman Jain CLA 2011-05-10 15:33:01 EDT
Thanks Olivier.

Released in HEAD for 3.7RC1
Comment 9 Ayushman Jain CLA 2011-05-12 06:51:27 EDT
> A UI test will have to be added to check that content assist doesn't throw an
> exception here.
Added test via bug 345363
Comment 10 Satyam Kandula CLA 2011-05-12 09:16:57 EDT
Verified for 3.7RC1 using I20110511-0800