### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.ui.tests diff --git ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java index 0aad318..f406b87 100644 --- ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java +++ ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java @@ -59,6 +59,7 @@ return new Java18ProjectTestSetup(test); } + @Override protected void setUp() throws Exception { Hashtable options= TestOptions.getDefaultOptions(); options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE); @@ -74,6 +75,7 @@ fSourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); } + @Override protected void tearDown() throws Exception { JavaProjectHelper.clear(fJProject1, Java18ProjectTestSetup.getDefaultClasspath()); } @@ -930,6 +932,136 @@ assertExpectedExistInProposals(proposals, new String[] { expected1 }); } + // Bug 421479 + public void testConvertToLambda19() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("@FunctionalInterface\n"); + buf.append("interface Func2 {\n"); + buf.append(" enum En {\n"); + buf.append(" A, B;\n"); + buf.append(" enum COLOR {\n"); + buf.append(" D, E\n"); + buf.append(" }\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" void foo();\n"); + buf.append("\n"); + buf.append(" String NAME = \"\";\n"); + buf.append("\n"); + buf.append(" static String staticName() {\n"); + buf.append(" return NAME;\n"); + buf.append(" }\n"); + buf.append("}\n"); + buf.append("\n"); + buf.append("class Test2 {\n"); + buf.append(" void bar() {\n"); + buf.append(" Func2 f = new Func2() {\n"); + buf.append(" @Override\n"); + buf.append(" public void foo() {\n"); + buf.append(" System.out.println(NAME);\n"); + buf.append(" System.out.println(En.A);\n"); + buf.append(" System.out.println(En.COLOR.D);\n"); + buf.append(" System.out.println(Func2.NAME);\n"); + buf.append(" System.out.println(Func2.En.A);\n"); + buf.append(" System.out.println(Func2.En.COLOR.D);\n"); + buf.append(" bar();\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("}\n"); + ICompilationUnit cu= pack1.createCompilationUnit("Func2.java", buf.toString(), false, null); + + int offset= buf.toString().indexOf("public void"); + AssistContext context= getCorrectionContext(cu, offset, 0); + assertNoErrors(context); + List proposals= collectAssists(context, false); + + assertNumberOfProposals(proposals, 1); + assertCorrectLabels(proposals); + + buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("@FunctionalInterface\n"); + buf.append("interface Func2 {\n"); + buf.append(" enum En {\n"); + buf.append(" A, B;\n"); + buf.append(" enum COLOR {\n"); + buf.append(" D, E\n"); + buf.append(" }\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" void foo();\n"); + buf.append("\n"); + buf.append(" String NAME = \"\";\n"); + buf.append("\n"); + buf.append(" static String staticName() {\n"); + buf.append(" return NAME;\n"); + buf.append(" }\n"); + buf.append("}\n"); + buf.append("\n"); + buf.append("class Test2 {\n"); + buf.append(" void bar() {\n"); + buf.append(" Func2 f = () -> {\n"); + buf.append(" System.out.println(Func2.NAME);\n"); + buf.append(" System.out.println(Func2.En.A);\n"); + buf.append(" System.out.println(Func2.En.COLOR.D);\n"); + buf.append(" System.out.println(Func2.NAME);\n"); + buf.append(" System.out.println(Func2.En.A);\n"); + buf.append(" System.out.println(Func2.En.COLOR.D);\n"); + buf.append(" bar();\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("}\n"); + String expected1= buf.toString(); + + assertExpectedExistInProposals(proposals, new String[] { expected1 }); + } + + // Bug 421479 + public void testConvertToLambda20() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("@FunctionalInterface\n"); + buf.append("interface Func2 {\n"); + buf.append(" enum En {\n"); + buf.append(" A, B\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" void foo();\n"); + buf.append("\n"); + buf.append(" String NAME = \"\";\n"); + buf.append("\n"); + buf.append(" static String staticName() {\n"); + buf.append(" return NAME;\n"); + buf.append(" }\n"); + buf.append("}\n"); + buf.append("\n"); + buf.append("class Test2 {\n"); + buf.append(" void bar() {\n"); + buf.append(" Func2 f = new Func2() {\n"); + buf.append(" @Override\n"); + buf.append(" public void foo() {\n"); + buf.append(" System.out.println(NAME);\n"); + buf.append(" System.out.println(En.A);\n"); + buf.append(" foo();\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("}\n"); + ICompilationUnit cu= pack1.createCompilationUnit("Func2.java", buf.toString(), false, null); + + int offset= buf.toString().indexOf("public void"); + AssistContext context= getCorrectionContext(cu, offset, 0); + assertNoErrors(context); + List proposals= collectAssists(context, false); + + assertNumberOfProposals(proposals, 0); + + } + public void testConvertToLambdaAmbiguousOverridden() throws Exception { IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); StringBuffer buf= new StringBuffer(); #P org.eclipse.jdt.ui diff --git core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java index 39ee07a..277f5ec 100644 --- core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java +++ core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java @@ -34,9 +34,12 @@ import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.LambdaExpression; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; @@ -69,6 +72,53 @@ import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings; public class LambdaExpressionsFix extends CompilationUnitRewriteOperationsFix { + + private static final class InterfaceAccessQualifier extends HierarchicalASTVisitor { + + List nameNodes; + + private ITypeBinding typeBinding; + + public InterfaceAccessQualifier(ITypeBinding typeBinding) { + this.typeBinding= typeBinding; + nameNodes= new ArrayList(); + } + + @Override + public boolean visit(SimpleName node) { + IBinding resolveBinding= node.resolveBinding(); + if (resolveBinding instanceof IVariableBinding) { + ITypeBinding declaringClass= ((IVariableBinding) resolveBinding).getDeclaringClass(); + if (declaringClass != null && Bindings.equals(typeBinding, declaringClass)) { + nameNodes.add(node); + } + } + return true; + } + + @Override + public boolean visit(QualifiedName node) { + IBinding resolveBinding= node.resolveBinding(); + Name qualifier= node.getQualifier(); + if (!(qualifier instanceof SimpleName)) { + qualifier= ASTNodes.getLeftMostSimpleName(qualifier); + } + IBinding qualifierBinding= qualifier.resolveBinding(); + if (Bindings.equals(typeBinding, qualifierBinding)) { + return false; + } + if (resolveBinding instanceof IVariableBinding) { + ITypeBinding declaringClass= ((IVariableBinding) resolveBinding).getDeclaringClass(); + while (declaringClass != null && declaringClass.isEnum()) { + declaringClass= declaringClass.getDeclaringClass(); + } + if (declaringClass != null && Bindings.equals(typeBinding, declaringClass)) { + nameNodes.add(qualifier); + } + } + return false; + } + } private static final class FunctionalAnonymousClassesFinder extends ASTVisitor { @@ -177,6 +227,9 @@ if (binding != null && !JdtFlags.isStatic(binding) && node.getExpression() == null && Bindings.isSuperType(binding.getDeclaringClass(), fFunctionalInterface, false)) throw new AbortSearchException(); + if (Bindings.equals(fMethodDeclaration.resolveBinding(), binding)) { + throw new AbortSearchException(); + } return true; } } @@ -238,8 +291,11 @@ } } } - //TODO: Bug 421479: [1.8][clean up][quick assist] convert anonymous to lambda must consider lost scope of interface -// lambdaBody.accept(new InterfaceAccessQualifier(rewrite, classInstanceCreation.getType().resolveBinding())); //TODO: maybe need a separate ASTRewrite and string placeholder + + ITypeBinding typeBinding= classInstanceCreation.getType().resolveBinding(); + InterfaceAccessQualifier visitor= new InterfaceAccessQualifier(typeBinding); + lambdaBody.accept(visitor); + qualifyNameNodes(rewrite, typeBinding, visitor.nameNodes, group); lambdaExpression.setBody(rewrite.createCopyTarget(lambdaBody)); Expression replacement= lambdaExpression; @@ -248,7 +304,7 @@ cast.setExpression(lambdaExpression); ImportRewrite importRewrite= cuRewrite.getImportRewrite(); ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(classInstanceCreation, importRewrite); - Type castType= importRewrite.addImport(classInstanceCreation.getType().resolveBinding(), ast, importRewriteContext); + Type castType= importRewrite.addImport(typeBinding, ast, importRewriteContext); cast.setType(castType); importRemover.registerAddedImports(castType); replacement= cast; @@ -257,6 +313,21 @@ importRemover.registerRemovedNode(classInstanceCreation); importRemover.registerRetainedNode(lambdaBody); + } + } + + private void qualifyNameNodes(ASTRewrite rewrite, ITypeBinding typeBinding, List nameNodes, TextEditGroup group) { + for (Name name : nameNodes) { + AST ast= name.getAST(); + SimpleName simpleName= null; + String qualifier= typeBinding.getName(); + if (name instanceof SimpleName) { + simpleName= ast.newSimpleName(((SimpleName) name).getIdentifier()); + rewrite.set(name, SimpleName.IDENTIFIER_PROPERTY, ast.newQualifiedName(ast.newName(qualifier), simpleName).getFullyQualifiedName(), group); + } else if (name instanceof QualifiedName) { + qualifier= qualifier + '.' + ((QualifiedName) name).getQualifier().getFullyQualifiedName(); + rewrite.set(name, QualifiedName.QUALIFIER_PROPERTY, ast.newName(qualifier), group); + } } } @@ -531,7 +602,7 @@ return methodBinding.getReturnType().getFunctionalInterfaceMethod() != null; } - //TODO: should also check whether variable is of a functional type + //TODO: should also check whether variable is of a functional type return locationInParent == SingleVariableDeclaration.INITIALIZER_PROPERTY || locationInParent == VariableDeclarationFragment.INITIALIZER_PROPERTY || locationInParent == Assignment.RIGHT_HAND_SIDE_PROPERTY