Community
Participate
Working Groups
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.
Move to JDT/UI
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!
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!
verified in I20070809-0010
*** Bug 155562 has been marked as a duplicate of this bug. ***