From 89a9c2d53b4155f8ab67d4e00192d86cc423febd Mon Sep 17 00:00:00 2001 From: Roland Grunberg Date: Wed, 14 Oct 2020 15:34:06 -0400 Subject: [PATCH] Implement methods in newly created type from an inherited sealed class. - When a non-existent type is permitted by a sealed class, the newly created type should implement the necessary methods - Related #1553 Signed-off-by: Roland Grunberg --- .../corrections/proposals/NewCUProposal.java | 40 +++++++++++++++++++ .../UnresolvedTypesQuickFixTest.java | 10 ++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/NewCUProposal.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/NewCUProposal.java index 0e3d8a48d0..bc0b0507a3 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/NewCUProposal.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/NewCUProposal.java @@ -17,11 +17,15 @@ *******************************************************************************/ package org.eclipse.jdt.ls.core.internal.corrections.proposals; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.Objects; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.ICompilationUnit; @@ -49,8 +53,14 @@ import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; +import org.eclipse.jdt.internal.corext.fix.AddUnimplementedMethodsOperation; +import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation; +import org.eclipse.jdt.internal.corext.fix.IProposableFix; +import org.eclipse.jdt.internal.corext.fix.UnimplementedCodeFixCore; import org.eclipse.jdt.internal.corext.util.JavaModelUtil; import org.eclipse.jdt.internal.corext.util.Messages; +import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.corext.refactoring.nls.changes.CreateFileChange; import org.eclipse.jdt.ls.core.internal.corrections.CorrectionMessages; import org.eclipse.lsp4j.CodeActionKind; @@ -276,6 +286,7 @@ protected Change createChange() throws CoreException { CompositeChange change = new CompositeChange(fName); change.add(new CreateFileChange(targetType.getResource().getRawLocation(), "", "")); change.add(constructNewCUChange(parentCU)); + return change; } else { return null; @@ -353,6 +364,35 @@ private CompilationUnitChange constructNewCUChange(ICompilationUnit cu) throws C String lineDelimiter = StubUtility.getLineDelimiterUsed(fCompilationUnit.getJavaProject()); String typeStub = constructTypeStub(cu, fTypeNameWithParameters, Flags.AccPublic, lineDelimiter); String cuContent = constructCUContent(cu, typeStub, lineDelimiter); + + IType cuType = fCompilationUnit.findPrimaryType(); + String[] permittedNames = cuType.getPermittedSubtypeNames(); + boolean isPermitted = Arrays.asList(permittedNames).stream().anyMatch(p -> fTypeNameWithParameters.equals(p)); + + if (isPermitted) { + IProject fakeProj = JavaLanguageServerPlugin.getProjectsManager().getDefaultProject(); + IFile fakeFile = fakeProj.getFile(cu.getPath()); + InputStream is = new ByteArrayInputStream(new byte[0]); + fakeFile.create(is, false, null); + + ICompilationUnit fakeCU = JDTUtils.getFakeCompilationUnit(fakeFile.getLocationURI().toString()); + + fakeCU.createType(typeStub, null, false, null); + ASTParser parser = ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL); + parser.setSource(fakeCU); + parser.setResolveBindings(true); + CompilationUnit cuRoot = (CompilationUnit) parser.createAST(null); + + AddUnimplementedMethodsOperation operation = new AddUnimplementedMethodsOperation((ASTNode) cuRoot.types().get(0)); + if (operation.getMethodsToImplement().length > 0) { + IProposableFix fix = new UnimplementedCodeFixCore(CorrectionMessages.UnimplementedMethodsCorrectionProposal_description, cuRoot, new CompilationUnitRewriteOperation[] { operation }); + CompilationUnitChange addChange = fix.createChange(null); + cuContent = addChange.getPreviewContent(null); + } + + fakeCU.delete(true, null); + } + CompilationUnitChange cuChange = new CompilationUnitChange("", cu); cuChange.setEdit(new InsertEdit(0, cuContent)); return cuChange; diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/UnresolvedTypesQuickFixTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/UnresolvedTypesQuickFixTest.java index def2c0c51f..1724c3092e 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/UnresolvedTypesQuickFixTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/UnresolvedTypesQuickFixTest.java @@ -1498,7 +1498,8 @@ public void testTypeInSealedTypeDeclaration() throws Exception { StringBuilder buf = new StringBuilder(); buf.append("package test1;\n"); buf.append("public sealed interface E permits F {\n"); - buf.append("}\n"); + buf.append("void methodE();\n"); + buf.append("}"); ICompilationUnit cu = pack1.createCompilationUnit("E.java", buf.toString(), false, null); buf = new StringBuilder(); @@ -1510,7 +1511,14 @@ public void testTypeInSealedTypeDeclaration() throws Exception { buf.append("\n"); buf.append("public final class F implements E {\n"); buf.append("\n"); + buf.append("\t@Override\n"); + buf.append("\tpublic void methodE() {\n"); + buf.append("\t\t// TODO Auto-generated method stub\n"); + buf.append("\t\t\n"); + buf.append("\t}\n"); + buf.append("\n"); buf.append("}\n"); + buf.append("\n"); Expected e1 = new Expected("Create class 'F'", buf.toString()); assertCodeActions(cu, e1);