diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java index 945474e..0fa6766 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest18.java @@ -1304,6 +1304,286 @@ public class AssistQuickFixTest18 extends QuickFixTest { assertExpectedExistInProposals(proposals, new String[] { expected1 }); } + public void testConvertToAnonymousClassCreation8() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class C {\n"); + buf.append(" C() {\n"); + buf.append(" IntSupplier i = () -> this.m();\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public int m() {\n"); + buf.append(" return 7;\n"); + buf.append(" }\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("C.java", buf.toString(), false, null); + + int offset= buf.toString().indexOf("->"); + AssistContext context= getCorrectionContext(cu, offset, 0); + assertNoErrors(context); + List proposals= collectAssists(context, false); + + assertNumberOfProposals(proposals, 4); + assertCorrectLabels(proposals); + + buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class C {\n"); + buf.append(" C() {\n"); + buf.append(" IntSupplier i = new IntSupplier() {\n"); + buf.append(" @Override\n"); + buf.append(" public int getAsInt() {\n"); + buf.append(" return C.this.m();\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public int m() {\n"); + buf.append(" return 7;\n"); + buf.append(" }\n"); + buf.append("}\n"); + + + String expected1= buf.toString(); + + assertExpectedExistInProposals(proposals, new String[] { expected1 }); + } + + public void testConvertToAnonymousClassCreation9() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class C {\n"); + buf.append(" int varA;\n"); + buf.append(" C() {\n"); + buf.append(" IntSupplier i = () -> this.varA;\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public int m() {\n"); + buf.append(" return 7;\n"); + buf.append(" }\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("C.java", buf.toString(), false, null); + + int offset= buf.toString().indexOf("->"); + AssistContext context= getCorrectionContext(cu, offset, 0); + assertNoErrors(context); + List proposals= collectAssists(context, false); + + assertNumberOfProposals(proposals, 4); + assertCorrectLabels(proposals); + + buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class C {\n"); + buf.append(" int varA;\n"); + buf.append(" C() {\n"); + buf.append(" IntSupplier i = new IntSupplier() {\n"); + buf.append(" @Override\n"); + buf.append(" public int getAsInt() {\n"); + buf.append(" return C.this.varA;\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public int m() {\n"); + buf.append(" return 7;\n"); + buf.append(" }\n"); + buf.append("}\n"); + + + String expected1= buf.toString(); + + assertExpectedExistInProposals(proposals, new String[] { expected1 }); + } + + public void testConvertToAnonymousClassCreation10() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class B extends C {\n"); + buf.append(" int var;\n"); + buf.append(" B() {\n"); + buf.append(" IntSupplier i = () -> {\n"); + buf.append(" System.out.println(this.var);\n"); + buf.append(" System.out.println(this.varC);\n"); + buf.append(" super.o();\n"); + buf.append(" return this.n();\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public int n() {\n"); + buf.append(" return 7;\n"); + buf.append(" }\n"); + buf.append("}\n"); + buf.append("\n"); + buf.append("class C {\n"); + buf.append(" int varC;\n"); + buf.append(" public void o() {}\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("B.java", buf.toString(), false, null); + + int offset= buf.toString().indexOf("->"); + AssistContext context= getCorrectionContext(cu, offset, 0); + assertNoErrors(context); + List proposals= collectAssists(context, false); + + assertNumberOfProposals(proposals, 3); + assertCorrectLabels(proposals); + + buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class B extends C {\n"); + buf.append(" int var;\n"); + buf.append(" B() {\n"); + buf.append(" IntSupplier i = new IntSupplier() {\n"); + buf.append(" @Override\n"); + buf.append(" public int getAsInt() {\n"); + buf.append(" System.out.println(B.this.var);\n"); + buf.append(" System.out.println(B.this.varC);\n"); + buf.append(" B.super.o();\n"); + buf.append(" return B.this.n();\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public int n() {\n"); + buf.append(" return 7;\n"); + buf.append(" }\n"); + buf.append("}\n"); + buf.append("\n"); + buf.append("class C {\n"); + buf.append(" int varC;\n"); + buf.append(" public void o() {}\n"); + buf.append("}\n"); + + + String expected1= buf.toString(); + + assertExpectedExistInProposals(proposals, new String[] { expected1 }); + } + + public void testConvertToAnonymousClassCreation11() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class D {\n"); + buf.append(" D() {\n"); + buf.append(" Runnable r = new Runnable() {\n"); + buf.append(" int x= 10;\n"); + buf.append(" @Override\n"); + buf.append(" public void run() {\n"); + buf.append(" IntSupplier i = () -> this.x;\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("D.java", buf.toString(), false, null); + + int offset= buf.toString().indexOf("->"); + AssistContext context= getCorrectionContext(cu, offset, 0); + assertNoErrors(context); + List proposals= collectAssists(context, false); + + assertNumberOfProposals(proposals, 4); + assertCorrectLabels(proposals); + + buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class D {\n"); + buf.append(" D() {\n"); + buf.append(" Runnable r = new Runnable() {\n"); + buf.append(" int x= 10;\n"); + buf.append(" @Override\n"); + buf.append(" public void run() {\n"); + buf.append(" IntSupplier i = new IntSupplier() {\n"); + buf.append(" @Override\n"); + buf.append(" public int getAsInt() {\n"); + buf.append(" return x;\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + + + String expected1= buf.toString(); + + assertExpectedExistInProposals(proposals, new String[] { expected1 }); + } + + public void testConvertToAnonymousClassCreation12() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + StringBuffer buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class D {\n"); + buf.append(" D() {\n"); + buf.append(" Runnable r = new Runnable() {\n"); + buf.append(" @Override\n"); + buf.append(" public void run() {\n"); + buf.append(" IntSupplier i = () -> D.this.n();\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public int n() {\n"); + buf.append(" return 7;\n"); + buf.append(" }\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("D.java", buf.toString(), false, null); + + int offset= buf.toString().indexOf("->"); + AssistContext context= getCorrectionContext(cu, offset, 0); + assertNoErrors(context); + List proposals= collectAssists(context, false); + + assertNumberOfProposals(proposals, 4); + assertCorrectLabels(proposals); + + buf= new StringBuffer(); + buf.append("package test1;\n"); + buf.append("import java.util.function.IntSupplier;\n"); + buf.append("public class D {\n"); + buf.append(" D() {\n"); + buf.append(" Runnable r = new Runnable() {\n"); + buf.append(" @Override\n"); + buf.append(" public void run() {\n"); + buf.append(" IntSupplier i = new IntSupplier() {\n"); + buf.append(" @Override\n"); + buf.append(" public int getAsInt() {\n"); + buf.append(" return D.this.n();\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append(" };\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public int n() {\n"); + buf.append(" return 7;\n"); + buf.append(" }\n"); + buf.append("}\n"); + + + String expected1= buf.toString(); + + assertExpectedExistInProposals(proposals, new String[] { expected1 }); + } + public void testConvertToAnonymousClassCreationWithParameterName() throws Exception { IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); StringBuffer buf= new StringBuffer(); diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java index dec2ec4..dafbe22 100644 --- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java +++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/fix/LambdaExpressionsFix.java @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Jerome Cambon - [1.8][clean up][quick assist] Convert lambda to anonymous must qualify references to 'this'/'super' - https://bugs.eclipse.org/430573 *******************************************************************************/ package org.eclipse.jdt.internal.corext.fix; @@ -33,12 +34,14 @@ import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; +import org.eclipse.jdt.core.dom.FieldAccess; 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.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.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; @@ -110,6 +113,69 @@ public class LambdaExpressionsFix extends CompilationUnitRewriteOperationsFix { return true; } } + + private static final class ThisOrSuperQualifier extends ASTVisitor { + + private String fClassName; + private ASTRewrite fRewrite; + + public static void perform(ASTNode node, String className, ASTRewrite rewrite) { + ThisOrSuperQualifier qualifier= new ThisOrSuperQualifier(); + qualifier.fClassName= className; + qualifier.fRewrite= rewrite; + node.accept(qualifier); + } + + @Override + public boolean visit(ThisExpression node) { + if (isValidClassName() && ((node.getQualifier() == null))) { + ThisExpression newThisExpression= (ThisExpression) ASTNode.copySubtree(fRewrite.getAST(), node); + newThisExpression.setQualifier(getNewDeclaringClass()); + fRewrite.replace(node, newThisExpression, null); + } + return true; + } + + @Override + public boolean visit(SuperFieldAccess node) { + if (isValidClassName() && ((node.getQualifier() == null))) { + SuperFieldAccess newSuperFieldAccess= (SuperFieldAccess) ASTNode.copySubtree(fRewrite.getAST(), node); + newSuperFieldAccess.setQualifier(getNewDeclaringClass()); + fRewrite.replace(node, newSuperFieldAccess, null); + } + return true; + } + + @Override + public boolean visit(SuperMethodInvocation node) { + if (isValidClassName() && ((node.getQualifier() == null))) { + SuperMethodInvocation newSuperMethodInvocation= (SuperMethodInvocation) ASTNode.copySubtree(fRewrite.getAST(), node); + newSuperMethodInvocation.setQualifier(getNewDeclaringClass()); + fRewrite.replace(node, newSuperMethodInvocation, null); + } + return true; + } + + @Override + public boolean visit(FieldAccess node) { + // In case of className not defined (e.g lambda in an anonymous), we need to remove the 'this' qualifier + // !! Issue in some cases (see AssistQuickFixTest18.testConvertToAnonymousClassCreation11) + if (!isValidClassName() && (node.getExpression() instanceof ThisExpression)) { + node.getParent(); + SimpleName simpleName= (SimpleName) ASTNode.copySubtree(fRewrite.getAST(), node.getName()); + fRewrite.replace(node, simpleName, null); + } + return true; + } + + private Name getNewDeclaringClass() { + return fRewrite.getAST().newSimpleName(fClassName); + } + + private boolean isValidClassName() { + return (fClassName != null) && (fClassName.length() > 0); + } + } private static class AbortSearchException extends RuntimeException { private static final long serialVersionUID= 1L; @@ -395,6 +461,10 @@ public class LambdaExpressionsFix extends CompilationUnitRewriteOperationsFix { MethodDeclaration methodDeclaration= StubUtility2.createImplementationStub(cuRewrite.getCu(), rewrite, importRewrite, importContext, methodBinding, parameterNames, lambdaTypeBinding.getName(), settings, false); + // Qualify reference to this or super + ITypeBinding typeBinding= lambdaExpression.resolveMethodBinding().getDeclaringClass(); + ThisOrSuperQualifier.perform(lambdaExpression, typeBinding.getName(), rewrite); + Block block; ASTNode lambdaBody= lambdaExpression.getBody(); if (lambdaBody instanceof Block) { @@ -547,7 +617,7 @@ public class LambdaExpressionsFix extends CompilationUnitRewriteOperationsFix { 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