Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 195817 - [extract interface] bug with enhanced for loop
Summary: [extract interface] bug with enhanced for loop
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.3   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.4 M1   Edit
Assignee: Martin Aeschlimann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 155562 (view as bug list)
Depends on:
Blocks:
 
Reported: 2007-07-09 08:26 EDT by Martin Thiim CLA
Modified: 2012-11-28 08:18 EST (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Thiim CLA 2007-07-09 08:26:35 EDT
Build ID: I20070621-1340

Steps To Reproduce:
The extract interface refactoring sometimes produces invalid code. I have seen a bug report (https://bugs.eclipse.org/bugs/show_bug.cgi?id=184766) with a more complex test case (with generics etc.) which may boil down to the same issue. Here's my simpler test case. It involves two classes:

public class MyTest 
{
	public void m1()
	{				
	}
	public void m2()
	{			
	}
}

public class Test {
	public static void main(String[] args)
	{
		MyTest[] arr = new MyTest[5];	

		for(MyTest mytest : arr)
		{
			mytest.m1();
		}
	}
}

To reproduce, use "Extract interface" on the class MyTest and select method m2() for extraction (keeping all other options default). The refactor function will incorrectly change the type MyTest in the for-loop to the new interface, which fails because m1() isn't extracted.

More information:
The error doesn't occur in case of for-constructs like "for(MyTest x = m; ; )" where m has type MyTest. In that case the refactoring realizes it can't use the extracted interface due to the call to m1(). This suggests that it is something specifically related to the handling of the for(type x : y) construct - "type" is substituted with the new type even if the substitution isn't compatible with the contents of the block.
Comment 1 Olivier Thomann CLA 2007-07-09 08:49:30 EDT
Move to JDT/UI
Comment 2 Martin Thiim CLA 2007-07-09 16:57:36 EDT
I have now taken a look at the source code to the refactoring and I think I have found the error. I've also included a suggestino for a fix. 
Note that this is the first time I'm taking a look at any JDT code and I'm not going to claim I understand all the details of the refactoring algorithms, so even though the fix suggested below solves the specific test case (and some extra test cases I've tried) it should be _carefully_ reviewed.

In the case of enhanced for-loops (ie. for(Type x : y)) the SuperTypeConstraintsCreator is invoked on a SingleVariableDeclaration rather than on a VariableDeclarationExpression as is the case for regular for-loops with variable declarations in the initializer. It appears that the endVisit() for SingleVariableDeclaration doesn't handle bindings in this case and hence doesn't create proper type constraints.

In the following I've added the handling of bindings (the comments indicate the added code):

public final void endVisit(final SingleVariableDeclaration node) {
   final ConstraintVariable2 ancestor= (ConstraintVariable2)node.getType().getProperty(PROPERTY_CONSTRAINT_VARIABLE);
   if (ancestor != null) {
      node.setProperty(PROPERTY_CONSTRAINT_VARIABLE, ancestor);
      final Expression expression= node.getInitializer();
      if (expression != null) {
         final ConstraintVariable2 descendant= (ConstraintVariable2)expression.getProperty(PROPERTY_CONSTRAINT_VARIABLE);
	if (descendant != null)
 	   fModel.createSubtypeConstraint(descendant, ancestor);
      }
      // New Code BEGIN!
      IVariableBinding binding = node.resolveBinding();
      if (binding != null) {
         ConstraintVariable2 descendant = fModel.createVariableVariable(binding);
	 if (descendant != null)
	    fModel.createEqualityConstraint(ancestor, descendant);
      }
      // New Code END!
   }
}

The above suggestion was inspired by looking at what happens in the case of a regular for-loop (for instance for(MyTest x = arr[0]; ; ) which eventually gets handled by the version of endVisit() that handles VariableDeclarationFragments and takes care of the bindings as in the code above.

Hope this helps!
Comment 3 Martin Aeschlimann CLA 2007-07-10 13:12:36 EDT
fixed > 20070710

The suggestion was good, the actual fix was similar but adds one more constraint. Thanks a lot for looking into it and help!
Comment 4 Karsten Becker CLA 2007-08-09 10:46:50 EDT
verified in  I20070809-0010
Comment 5 Dani Megert CLA 2012-11-28 08:18:22 EST
*** Bug 155562 has been marked as a duplicate of this bug. ***