Bug 403749 - [1.8][clean up][quick assist] migrate anonymous class creations to lambda expressions
Summary: [1.8][clean up][quick assist] migrate anonymous class creations to lambda exp...
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.3   Edit
Hardware: All All
: P3 enhancement (vote)
Target Milestone: BETA J8   Edit
Assignee: Deepak Azad CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 417795 417796 (view as bug list)
Depends on: 412726
Blocks: 421479
  Show dependency tree
 
Reported: 2013-03-19 06:43 EDT by Markus Keller CLA
Modified: 2013-12-06 10:17 EST (History)
6 users (show)

See Also:
markus.kell.r: review+


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Keller CLA 2013-03-19 06:43:32 EDT
Create a clean up / multi fix / quick assist that migrates anonymous class creations to lambda expressions.

The result must be semantically equivalent to the original code. The migration can only work if the type is a "Functional Interface", and if the anonymous class body only contains a single method.
Comment 1 Deepak Azad CLA 2013-07-11 01:09:09 EDT
Markus, I pushed branch the following branch with an initial implementation of the quick assist - http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/log/?h=dazad/BETA_JAVA8

We need an API from JDT Core to check if an anonymous class creation corresponds to a Functional interface. For now I implemented a very simple check in our code.

Note: the branch has SHARED_AST_LEVEL= AST.JLS8 otherwise the quick assist will not work.
Comment 2 Deepak Azad CLA 2013-07-11 01:17:28 EDT
(In reply to comment #1)
> We need an API from JDT Core to check if an anonymous class creation
> corresponds to a Functional interface. For now I implemented a very simple
> check in our code.

Opened Bug 412726
Comment 3 Deepak Azad CLA 2013-07-23 19:13:36 EDT
Clean up implementation - first draft
http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?h=dazad/BETA_JAVA8&id=00ea70edad7c0cafb22fe394db2115b0a0aa2e66

For now, the preference for the new clean up is on 'Code Style' page. But I am wondering if there should be a new tab specifically for Java 8 features, Markus?
Comment 5 Markus Keller CLA 2013-07-31 19:43:27 EDT
Thanks Deepak, looks like we're on a good track here.

I didn't do a thorough review yet, but I just noticed one UI glitch in the Clean Up edit dialog: The "Use lambda where possible" option does not update the enabled clean ups counter, and hence it can't be run as the sole clean up.

Please also start thinking about the inverse operation. For the "Enhanced for loop", it turned out that users also need to go back to the verbose form. Initially, to convert 1.5 code to 1.4, but also on a case-by-case basis if more control over the iteration is necessary.

I think we should at least offer a quick assist to convert a lambda to an anonymous class, but maybe we also want this as a clean up?
Comment 6 Deepak Azad CLA 2013-07-31 20:07:38 EDT
(In reply to comment #5)
> I think we should at least offer a quick assist to convert a lambda to an
> anonymous class, but maybe we also want this as a clean up?
Fair point. I will add the reverse quick assist. If it would be useful, we can also add the clean up since it is not that much more work.

I will also take a look at the UI glitch.
Comment 7 Markus Keller CLA 2013-07-31 20:15:24 EDT
If the method body just contains a single return statement, then the lambda should use the short form with just an expression body, e.g.:

        Callable<String> c = new Callable<String>() {
            @Override
            public String call() {
                return "OK";
            }
        };

=>

        Callable<String> c = () -> "OK";
Comment 8 Deepak Azad CLA 2013-08-03 17:37:36 EDT
- I created the reverse quick assist. But I was not sure that we need the reverse clean up as well, hence I just implemented the quick assist as a multi-fix so that if we need the cleanup it will be a trivial task. 

http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?h=dazad/BETA_JAVA8&id=5fe26f2d0a91a33b0f75c6538337ab3494489948

- Fixed the UI glitch from comment 5
- Fixed the use case from comment 7

To ensure that everything works without throwing an exception I had to fix a few usages of MethodDeclaration#thrownExceptions(). For this I have followed the following code pattern...
int apiLevel= ast.apiLevel();
List<ASTNode> thrownExceptions= apiLevel < AST.JLS8 ? decl.thrownExceptions() : decl.thrownExceptionTypes();

- There are a few TODOs in LambdaExpressionsFix which relate to minor (style) points.

Awaiting review...
Comment 9 Deepak Azad CLA 2013-09-23 15:02:52 EDT
*** Bug 417795 has been marked as a duplicate of this bug. ***
Comment 10 Deepak Azad CLA 2013-09-23 15:03:41 EDT
*** Bug 417796 has been marked as a duplicate of this bug. ***
Comment 11 Srikanth Sankaran CLA 2013-09-25 01:37:57 EDT
We are hoping this will be in reasonable shape for us to include it in
a demo in ECE Oct 30th. Any help needed from JDT/Core, let us know, we
will pull out all stops to make it happen. Thank you.
Comment 12 Deepak Azad CLA 2013-09-25 02:19:08 EDT
(In reply to Srikanth Sankaran from comment #11)
> We are hoping this will be in reasonable shape for us to include it in
> a demo in ECE Oct 30th. Any help needed from JDT/Core, let us know, we
> will pull out all stops to make it happen. Thank you.

This can be demoed. The code is in dazad/BETA_JAVA8 branch (and not in the common BETA_JAVA8 branch), but that should not be a blocker for it to be demoed.
Comment 13 Srikanth Sankaran CLA 2013-10-24 08:53:12 EDT
(In reply to Deepak Azad from comment #12)
> (In reply to Srikanth Sankaran from comment #11)
> > We are hoping this will be in reasonable shape for us to include it in
> > a demo in ECE Oct 30th. Any help needed from JDT/Core, let us know, we
> > will pull out all stops to make it happen. Thank you.
> 
> This can be demoed. The code is in dazad/BETA_JAVA8 branch (and not in the
> common BETA_JAVA8 branch), but that should not be a blocker for it to be
> demoed.

4.3.1 + bundles from http://dist.springsource.com/snapshot/TOOLS/java8/e43
as the IDE, I launched on a clean workspace, clone the UI repo, imported the
projects and switched to your branch. Launched an inner instance on a
clean workspace, created a 1.8 project and pasted the code below.

I get the offer of assist to transform anonymous class to lambda and it
works fine for the case below.

Thanks. However any attempt to even ever so lightly modify the file results
in numerous exceptions being thrown about use of unsupported operations in
JLS8 (multiple errors have occurred, operation supported only in JLS2,3, 4.

So while this feature looks ready, other foundational pieces don't. We are
evaluating our options.

// ---
import java.util.EventListener;

interface IResourceChangeEvent {
	int CHEESE_MOVE = 0;
	Object getDelta();
}

interface IResourceChangeListener extends EventListener {
	// ...
	public void handleResourceChange(IResourceChangeEvent event);
	// ...
}

interface IWorkspace {
	// ...
	public void addResourceChangeListener(IResourceChangeListener listener, int eventMask);
	// ...
}
public class X {
	void foo(IWorkspace workspace) {
		workspace.addResourceChangeListener(new IResourceChangeListener() {
			@Override
			public void handleResourceChange(IResourceChangeEvent event) {
				handleCheeseMove(event.getDelta());
				
			}
		}, IResourceChangeEvent.CHEESE_MOVE);
	}
	private void handleCheeseMove(Object delta) {
		// ...
	}
}
// --
Comment 14 Stephan Herrmann CLA 2013-10-24 11:37:54 EDT
From playing with this for the demo:

- the quickfix doesn't seem to respect the formatter settings,
  notably: I get no linebreaks within the lambda, although it
  exceeds the Maximum line width by many (> 50) characters
  Is this expected?
  Deselecting the lambda and hitting Ctrl-Shift-F produces a
  somewhat useful layout (we probably don't yet have formatter
  settings for really smart formatting of lambdas, right?).

- are there any interesting variations where the quickfix behaves
  differently, apart from just not being available if, e.g.,
  not having a functional interface type?
Comment 15 Srikanth Sankaran CLA 2013-10-28 00:09:19 EDT
Deepak, now that https://bugs.eclipse.org/bugs/show_bug.cgi?id=403927 is resolved,
can you pull the changes and get your branch up to date so we can test it and
use it for a demo on 30th Oct if possible ? 

Please also see unanswered questions in comment#14

Thanks in advance.
Comment 16 Srikanth Sankaran CLA 2013-10-28 00:41:19 EDT
Markus, since it appears Deepak may be done with this work and it is
waiting for review from your side, if you can give it a look through and
if is ready to be promoted to branch head, go ahead and release it that
would help.

TIA.
Comment 17 Markus Keller CLA 2013-10-28 06:52:05 EDT
I've already merged dazad/BETA_JAVA8 into BETA_JAVA8 in my workspace and I'll push it to BETA_JAVA8 after I'm done with my review.
Comment 18 Markus Keller CLA 2013-10-28 16:35:18 EDT
I'm not yet done with the full review, but a fairly-well-working preview is now in branch mkeller/BETA_JAVA8:
http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?h=mkeller/BETA_JAVA8&id=455ffd012f312dd5147ffb01ee159912d5c26317

This can be used to prepare a demo, but don't base other work on it, since I'll massage the commits a bit before it goes into BETA_JAVA8.
Comment 19 Deepak Azad CLA 2013-10-29 03:41:11 EDT
(In reply to Stephan Herrmann from comment #14)
> From playing with this for the demo:
> 
> - the quickfix doesn't seem to respect the formatter settings,
>   notably: I get no linebreaks within the lambda, although it
>   exceeds the Maximum line width by many (> 50) characters
>   Is this expected?
>   Deselecting the lambda and hitting Ctrl-Shift-F produces a
>   somewhat useful layout (we probably don't yet have formatter
>   settings for really smart formatting of lambdas, right?).
Umm.. I think the formatter usually handles this by default. We don't really do anything special in UI to format the code, ASTRewrite is supposed to take care of things.
 
> - are there any interesting variations where the quickfix behaves
>   differently, apart from just not being available if, e.g.,
>   not having a functional interface type?
Not really, the only non-trivial case where the quick assist is not offered was when the anonymous class had fields or say implemented methods from the Object class. 

There could be other minor variations which are currently TODOs in code
// TODO: minor: no parentheses for single inferred-type parameter?
// TODO: minor: do we want to create VaribaleDeclarationFragments or inferred-type parameter - never?

But I doubt if any of these are too interesting to be worth mentioning in a demo.
Comment 20 Srikanth Sankaran CLA 2013-10-29 04:47:57 EDT
(In reply to Markus Keller from comment #18)
> I'm not yet done with the full review, but a fairly-well-working preview is
> now in branch mkeller/BETA_JAVA8:
> http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?h=mkeller/
> BETA_JAVA8&id=455ffd012f312dd5147ffb01ee159912d5c26317
> 
> This can be used to prepare a demo, but don't base other work on it, since
> I'll massage the commits a bit before it goes into BETA_JAVA8.

This works pretty well, I tested it a bit by editing a file with the sample
code from comment#13 and by adding default methods, static methods, redeclaring
java.lang.Object's methods, converting interface to abstract class, adding
a second abstract method etc. I get the quick assist offer where I should and
don't where I shouldn't.

This is great news, Thanks Markus & Deepak.

Is there any chance this will be to get promoted to BETA_JAVA8 after review
with follow up bugs raised for open items found during code review which can be
addressed in due course ? 

It would be nice to give a demo right from BETA_JAVA8 without having to 
launch an inner IDE, so the audience know this is real & live - but only if 
it makes sense from your team's pov.
Comment 21 Markus Keller CLA 2013-12-04 09:10:19 EST
Fixed with http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/commit/?id=21b51b36c3333f310d1cb3db44748ee5ee28f721

LambdaExpressionsFix has a few TODOs that can be handles in bug 421479.
Comment 22 Srikanth Sankaran CLA 2013-12-05 19:56:17 EST
See that this transformation has the potential to break programs:

e.g:

interface I {
	int foo(String s);
}

interface J {
	Integer foo(String s);
}

public class X {
	static void goo(I i) {
		System.out.println("goo(I)");
	}
	static void goo(J j) {
		System.out.println("goo(J)");
	}
	
	public static void main(String[] args) {
		goo(new I() {

			@Override
			public int foo(String s) {
				return 0;
			}});
	}
}

The transformed code won't compile any more. There could be scenarios
where the call would compile, but bind to a different method altogether.

If you need some help from Core to figure out whether the overload semantics
would alter the resolution, please file an ER with a proposed API. Thanks.
Comment 23 Srikanth Sankaran CLA 2013-12-05 19:59:59 EST
(In reply to Srikanth Sankaran from comment #22)
> See that this transformation has the potential to break programs:

[...]

> If you need some help from Core to figure out whether the overload semantics
> would alter the resolution, please file an ER with a proposed API. Thanks.

In the interim, if you wish, you can stick in a cast to the functional type.
A casted poly expression ceases to be a poly expression and cannot be influenced
by the context or influence the context.
Comment 24 Markus Keller CLA 2013-12-06 10:17:40 EST
(In reply to Srikanth Sankaran from comment #22 and comment #23)
Noopur is working on this in 408966.

Filed bug 423439 to fix the cleanup/quickassist, since bug 421479 is about something else.