diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.java index 54d92b1d56..b9a59306f3 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.java @@ -147,6 +147,9 @@ private CorrectionMessages() { public static String RenameRefactoringProposal_additionalInfo; public static String RenameRefactoringProposal_name; + public static String ModifierCorrectionSubProcessor_changemodifierto_final_description; + public static String ModifierCorrectionSubProcessor_changemodifierto_sealed_description; + public static String ModifierCorrectionSubProcessor_changemodifierto_nonsealed_description; public static String ModifierCorrectionSubProcessor_changemodifiertoabstract_description; public static String ModifierCorrectionSubProcessor_changemodifiertostatic_description; public static String ModifierCorrectionSubProcessor_changemodifiertononstatic_description; diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties index f3ed1f3a6a..4f93fa8921 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties @@ -146,6 +146,9 @@ RemoveDeclarationCorrectionProposal_removeunusedvar_description=Remove declarati RenameRefactoringProposal_additionalInfo=Start the Rename refactoring RenameRefactoringProposal_name=Rename in workspace +ModifierCorrectionSubProcessor_changemodifierto_final_description=Change ''{0}'' to ''final'' +ModifierCorrectionSubProcessor_changemodifierto_sealed_description=Change ''{0}'' to ''sealed'' +ModifierCorrectionSubProcessor_changemodifierto_nonsealed_description=Change ''{0}'' to ''non-sealed'' ModifierCorrectionSubProcessor_changemodifiertoabstract_description=Change ''{0}'' to ''abstract'' ModifierCorrectionSubProcessor_changemodifiertostatic_description=Change ''{0}'' to ''static'' ModifierCorrectionSubProcessor_changemodifiertodefault_description=Change ''{0}'' to ''default'' diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/QuickFixProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/QuickFixProcessor.java index 882d1bd598..2cc313947a 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/QuickFixProcessor.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/QuickFixProcessor.java @@ -190,6 +190,10 @@ private void process(IInvocationContext context, IProblemLocationCore problem, C case IProblem.IndirectAccessToStaticMethod: LocalCorrectionsSubProcessor.addCorrectAccessToStaticProposals(context, problem, proposals); break; + case IProblem.SealedMissingClassModifier: + case IProblem.SealedMissingInterfaceModifier: + ModifierCorrectionSubProcessor.addSealedMissingModifierProposal(context, problem, proposals); + break; case IProblem.StaticMethodRequested: case IProblem.NonStaticFieldFromStaticInvocation: case IProblem.InstanceMethodDuringConstructorInvocation: diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/text/correction/ModifierCorrectionSubProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/text/correction/ModifierCorrectionSubProcessor.java index 278c027410..112c79aab9 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/text/correction/ModifierCorrectionSubProcessor.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/text/correction/ModifierCorrectionSubProcessor.java @@ -962,4 +962,38 @@ private static Modifier findVisibilityModifier(List modifiers return null; } + public static void addSealedMissingModifierProposal(IInvocationContext context, IProblemLocationCore problem, Collection proposals) { + if (proposals == null) { + return; + } + ASTNode selectedNode = problem.getCoveringNode(context.getASTRoot()); + if (!(selectedNode instanceof SimpleName)) { + return; + } + if (!(((SimpleName) selectedNode).getParent() instanceof TypeDeclaration)) { + return; + } + TypeDeclaration typeDecl = (TypeDeclaration) ((SimpleName) selectedNode).getParent(); + boolean isInterface = typeDecl.isInterface(); + + ICompilationUnit cu = context.getCompilationUnit(); + ITypeBinding typeDeclBinding = typeDecl.resolveBinding(); + int relevance = IProposalRelevance.CHANGE_MODIFIER_TO_FINAL; + String label; + + if (!isInterface) { + // Add final modifier + label = Messages.format(CorrectionMessages.ModifierCorrectionSubProcessor_changemodifierto_final_description, typeDecl.getName()); + proposals.add(new ModifierChangeCorrectionProposal(label, cu, typeDeclBinding, typeDecl, Modifier.FINAL, 0, relevance)); + } + + // Add sealed modifier + label = Messages.format(CorrectionMessages.ModifierCorrectionSubProcessor_changemodifierto_sealed_description, typeDecl.getName()); + proposals.add(new ModifierChangeCorrectionProposal(label, cu, typeDeclBinding, typeDecl, Modifier.SEALED, 0, relevance)); + + // Add non-sealed modifier + label = Messages.format(CorrectionMessages.ModifierCorrectionSubProcessor_changemodifierto_nonsealed_description, typeDecl.getName()); + proposals.add(new ModifierChangeCorrectionProposal(label, cu, typeDeclBinding, typeDecl, Modifier.NON_SEALED, 0, relevance)); + } + } diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/ModifierCorrectionsQuickFixTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/ModifierCorrectionsQuickFixTest.java index aed4d26c92..2950a0d52f 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/ModifierCorrectionsQuickFixTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/ModifierCorrectionsQuickFixTest.java @@ -12,10 +12,15 @@ *******************************************************************************/ package org.eclipse.jdt.ls.core.internal.correction; +import java.util.HashMap; +import java.util.Map; + import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.internal.corext.util.JavaModelUtil; import org.junit.Before; import org.junit.Test; @@ -1203,4 +1208,51 @@ public void testClassExtendFinalClass() throws Exception { Expected e1 = new Expected("Remove 'final' modifier of 'X'", buf.toString()); assertCodeActions(cu, e1); } + + @Test + public void testAddSealedMissingClassModifierProposal() throws Exception { + Map options15 = new HashMap<>(); + JavaModelUtil.setComplianceOptions(options15, JavaCore.VERSION_15); + options15.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + options15.put(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE); + fJProject.setOptions(options15); + IPackageFragment pack1 = fSourceFolder.createPackageFragment("test", false, null); + assertNoErrors(fJProject.getResource()); + + StringBuilder buf = new StringBuilder(); + buf = new StringBuilder(); + buf.append("package test;\n"); + buf.append("\n"); + buf.append("public sealed class Shape permits Square {}\n"); + buf.append("\n"); + buf.append("class Square extends Shape {}\n"); + ICompilationUnit cu = pack1.createCompilationUnit("Shape.java", buf.toString(), false, null); + + buf = new StringBuilder(); + buf.append("package test;\n"); + buf.append("\n"); + buf.append("public sealed class Shape permits Square {}\n"); + buf.append("\n"); + buf.append("final class Square extends Shape {}\n"); + Expected e1 = new Expected("Change 'Square' to 'final'", buf.toString()); + assertCodeActions(cu, e1); + + buf = new StringBuilder(); + buf.append("package test;\n"); + buf.append("\n"); + buf.append("public sealed class Shape permits Square {}\n"); + buf.append("\n"); + buf.append("non-sealed class Square extends Shape {}\n"); + Expected e2 = new Expected("Change 'Square' to 'non-sealed'", buf.toString()); + assertCodeActions(cu, e2); + + buf = new StringBuilder(); + buf.append("package test;\n"); + buf.append("\n"); + buf.append("public sealed class Shape permits Square {}\n"); + buf.append("\n"); + buf.append("sealed class Square extends Shape {}\n"); + Expected e3 = new Expected("Change 'Square' to 'sealed'", buf.toString()); + assertCodeActions(cu, e3); + } }