Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 414122 - [1.8][quick fix] 'Create method' quick fix with interface members
Summary: [1.8][quick fix] 'Create method' quick fix with interface members
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.4   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 4.6 M7   Edit
Assignee: Noopur Gupta CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 490742 (view as bug list)
Depends on:
Blocks:
 
Reported: 2013-07-31 08:02 EDT by Noopur Gupta CLA
Modified: 2016-04-06 04:23 EDT (History)
5 users (show)

See Also:
noopur_gupta: review-


Attachments
Patch with testcases. (31.72 KB, patch)
2013-08-27 06:07 EDT, Martin Mathew CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Noopur Gupta CLA 2013-07-31 08:02:19 EDT
public interface A_test1 { // creates default methods without body
	int i= aaa(); 
	
	default void defaultM() {
		int a= bbb();
	}
	
	static void staticM() {
		int x= ccc();
	}
}

interface A_test2 { // creates static method without body
	int i = foo1();
	static int foo() {return 0;}
}

interface A_test3 { // creates abstract method
	int i = foo2();
}
-------------------------------------------------------------
In the above example, 'Create method' quick fix creates incorrect methods as given below:

public interface A_test1 {	// creates default methods without body
	int i= aaa(); 
	
	default void defaultM() {
		int a= bbb();
	}
	
	default int bbb();

	default int aaa();

	static void staticM() {
		int x= ccc();
	}

	default int ccc();
}

interface A_test2 { // creates static method without body
	int i = foo1();
	static int foo() {return 0;}
	static int foo1();
}

interface A_test3 { // creates abstract method
	int i = foo2();

	int foo2();
}
-------------------------------------------------------------
It should not create an abstract method in any case.
Also, we should check if a default or a static method has to be created and create the correct method with body accordingly.
Comment 1 Martin Mathew CLA 2013-08-08 04:34:22 EDT
In the current implementation, if 'create method' quick fix is initiated from an interface, then the algorithm gets hold of the first method in the enclosing interface and copies its modifiers.
Below are the clarifications required to continue the task:
         i) If the interface is enclosed in a class, then shouldn't the second quick fix create a 'static' method in the class, so that after the quick fix user does not have anymore errors to be fixed.
	ii) For Java 8, if the quick fix is initiated from the interface, then always create a static method, so that it can be accessed from an interface variable declaration, another static method or from a default method.
	iii) Couldn't come up with a valid case where an abstract method should be created when the quick fix is initiated from an interface.

To sum it up, for less than Java 8, if 'create method' quick fix is initiated from an interface which is enclosed in a class, then the quick fix should create a static method in the class. In other cases no quick fix to create method should be provided.
For Java 8, 2 quick fix proposal can be given, one to create the method within the interface and the other to create the method in the class if the interface is enclosed within a class.

Kindly share your thoughts.
Comment 2 Markus Keller CLA 2013-08-16 11:31:19 EDT
Before 1.8, the "Create method" quick fix was of limited value when it was invoked from a reference inside an interface member. In that case, the only valid situation was indeed when the interface was nested in a class, and the only applicable fix should be to create a static method in the enclosing class. (That proposal makes sense for 1.8 as well.)

But note that this quick fix was mainly intended to be applied to references from outside of the interface declaration, e.g.:

interface IContainer {
	public abstract String name();
}
class Ref {
	void foo(IContainer c) {
		int[] v= c.values(); // create abstract IContainer#values()
	}
}

In that situation, it also makes sense to copy the modifiers of the first existing method declaration in the interface.


Java 1.8 changes the game significantly, so we need adapted heuristics:

(a) For references from outside the interface declaration, where the method invocation's expression has an interface type, the quick fix should keep creating an abstract method (since that's still the main intent of an interface). We should still use a heuristic to copy redundant public and abstract modifiers for new interface methods:
1. from the first abstract method, or if none available:
2. from the first method, or if none available:
3. from the first member

Examples:
void bar(Interface c) {
    int[] a= c.values(); // create abstract method
    Object o= Interface.getGlobal(); // create static method
}

(b) for references from inside an interface declaration (unqualified or qualified with "this."), it would be better to create a method that matches the static-ness of the enclosing member. I.e.:
- for references from a default method, create an abstract method
- for references from a static member, create a static method

Examples:
interface Container {
    default void doit() {
        Arrays.sort(this.values()); // create abstract
    }
    Object OBJ= init(); // create static
}
Comment 3 Martin Mathew CLA 2013-08-27 06:07:40 EDT
Created attachment 234795 [details]
Patch with testcases.

With this patch i have covered most of the points Markus pointed out.

In the below example, due to bug 414113 abstract method will not be created. But will be automatically fixed once the bug is resolved.
interface Container {
    default void doit() {
        Arrays.sort(this.values()); // create abstract
    }
}
Markus, please review.
Comment 4 Noopur Gupta CLA 2016-03-31 00:57:44 EDT
*** Bug 490742 has been marked as a duplicate of this bug. ***
Comment 5 Noopur Gupta CLA 2016-04-01 04:46:06 EDT
(In reply to Manju Mathew from comment #3)
> Created attachment 234795 [details] [diff]
> Patch with testcases.
> 
> With this patch i have covered most of the points Markus pointed out.

The patch does not create an abstract method for an unqualified method reference from a default method:

(In reply to Markus Keller from comment #2)
> (b) for references from inside an interface declaration (unqualified or
> qualified with "this."), it would be better to create a method that matches
> the static-ness of the enclosing member. I.e.:
> - for references from a default method, create an abstract method

Also, the quick fix images are incorrect -- do not reflect the correct visibility of the method which will be created.

I will upload a patch with these fixes.
Comment 6 Noopur Gupta CLA 2016-04-01 12:31:50 EDT
Other issues to be fixed:

It results in CCE if the invocation expression is an expression other than 'this' or a Name:
java.lang.ClassCastException: org.eclipse.jdt.core.dom.ParenthesizedExpression cannot be cast to org.eclipse.jdt.core.dom.Name
	at org.eclipse.jdt.internal.ui.text.correction.proposals.NewMethodCorrectionProposal.evaluateModifiers(NewMethodCorrectionProposal.java:96)
...

Also, it just refers to the modifiers of the first field and not the first member.
Comment 7 Eclipse Genie CLA 2016-04-06 03:26:36 EDT
New Gerrit change created: https://git.eclipse.org/r/69983