From 4a7d0ae1e46c66da12c482791fc08e5d503bb07a Mon Sep 17 00:00:00 2001 From: Snjezana Peco Date: Thu, 18 Jan 2024 16:15:23 +0100 Subject: [PATCH] Add Quickfix 'UninitializedBlankFinalField' Signed-off-by: Snjezana Peco --- .../corrections/QuickFixProcessor.java | 5 + .../UnInitializedFinalFieldSubProcessor.java | 56 +++++++++++ .../handlers/BaseDiagnosticsHandler.java | 2 +- .../internal/handlers/CodeActionHandler.java | 5 + .../org.eclipse.jdt.ls.tp.target | 2 +- .../UnInitializedFinalFieldQuickFixTest.java | 93 +++++++++++++++++++ 6 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/UnInitializedFinalFieldSubProcessor.java create mode 100644 org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/UnInitializedFinalFieldQuickFixTest.java 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 720ce74dbb..551a0b6d07 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 @@ -42,6 +42,7 @@ import org.eclipse.jdt.internal.ui.text.correction.IInvocationContextCore; import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore; import org.eclipse.jdt.internal.ui.text.correction.IProposalRelevance; +import org.eclipse.jdt.internal.ui.text.correction.UnInitializedFinalFieldBaseSubProcessor; import org.eclipse.jdt.internal.ui.text.correction.proposals.ReplaceCorrectionProposalCore; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.corrections.proposals.AddImportCorrectionProposal; @@ -158,6 +159,10 @@ private void process(CodeActionParams params, IInvocationContextCore context, IP case IProblem.UnresolvedVariable: UnresolvedElementsSubProcessor.getVariableProposals(context, problem, null, proposals); break; + case IProblem.UninitializedBlankFinalField: + UnInitializedFinalFieldBaseSubProcessor processor = new UnInitializedFinalFieldSubProcessor(); + processor.addProposals(context, problem, proposals); + break; case IProblem.AmbiguousType: case IProblem.JavadocAmbiguousType: UnresolvedElementsSubProcessor.getAmbiguousTypeReferenceProposals(context, problem, proposals); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/UnInitializedFinalFieldSubProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/UnInitializedFinalFieldSubProcessor.java new file mode 100644 index 0000000000..e49c2aba03 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/UnInitializedFinalFieldSubProcessor.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corrections; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore; +import org.eclipse.jdt.internal.ui.text.correction.IProposalRelevance; +import org.eclipse.jdt.internal.ui.text.correction.UnInitializedFinalFieldBaseSubProcessor; +import org.eclipse.jdt.internal.ui.text.correction.proposals.InitializeFinalFieldProposalCore; +import org.eclipse.jdt.ls.core.internal.handlers.CodeActionHandler; +import org.eclipse.lsp4j.CodeActionKind; + +/** + * @author snjeza + * + */ +public class UnInitializedFinalFieldSubProcessor extends UnInitializedFinalFieldBaseSubProcessor { + + @Override + protected ProposalKindWrapper createInitializeFinalFieldProposal(IProblemLocationCore problem, ICompilationUnit targetCU, SimpleName node, IVariableBinding targetBinding, int createConstructor) { + InitializeFinalFieldProposalCore proposal = new InitializeFinalFieldProposalCore(problem, targetCU, node, targetBinding, IProposalRelevance.CREATE_CONSTRUCTOR); + return CodeActionHandler.wrap(proposal, CodeActionKind.QuickFix); + } + + @Override + protected ProposalKindWrapper createInitializeFinalFieldProposal(IProblemLocationCore problem, ICompilationUnit targetCU, MethodDeclaration node, int createConstructor, int updateAtConstructor) { + InitializeFinalFieldProposalCore proposal = new InitializeFinalFieldProposalCore(problem, targetCU, node, IProposalRelevance.CREATE_CONSTRUCTOR, InitializeFinalFieldProposalCore.UPDATE_AT_CONSTRUCTOR); + return CodeActionHandler.wrap(proposal, CodeActionKind.QuickFix); + } + + @Override + protected ProposalKindWrapper conditionallyCreateInitializeFinalFieldProposal(IProblemLocationCore problem, ICompilationUnit targetCU, MethodDeclaration node, int createConstructor, int updateAtConstructor) { + InitializeFinalFieldProposalCore initializeFinalFieldProposal = new InitializeFinalFieldProposalCore(problem, targetCU, node, IProposalRelevance.CREATE_CONSTRUCTOR, InitializeFinalFieldProposalCore.UPDATE_CONSTRUCTOR_NEW_PARAMETER); + try { + if (initializeFinalFieldProposal.hasProposal()) { + return CodeActionHandler.wrap(initializeFinalFieldProposal, CodeActionKind.QuickFix); + } + } catch (CoreException ce) { + } + return null; + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/BaseDiagnosticsHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/BaseDiagnosticsHandler.java index 3cdafd751a..0618fc143c 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/BaseDiagnosticsHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/BaseDiagnosticsHandler.java @@ -206,7 +206,7 @@ public static List toDiagnosticsArray(IOpenable openable, List - + diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/UnInitializedFinalFieldQuickFixTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/UnInitializedFinalFieldQuickFixTest.java new file mode 100644 index 0000000000..646dd9caf2 --- /dev/null +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/correction/UnInitializedFinalFieldQuickFixTest.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.correction; + +import static org.junit.Assert.assertNotNull; + +import java.util.List; +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.ls.core.internal.JavaCodeActionKind; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.junit.Before; +import org.junit.Test; + +public class UnInitializedFinalFieldQuickFixTest extends AbstractQuickFixTest { + + private IJavaProject javaProject; + private IPackageFragmentRoot sourceFolder; + + @Before + public void setup() throws Exception { + javaProject = newEmptyProject(); + Map options = TestOptions.getDefaultOptions(); + options.put(JavaCore.COMPILER_PB_NO_EFFECT_ASSIGNMENT, JavaCore.IGNORE); + options.put(JavaCore.COMPILER_PB_INDIRECT_STATIC_ACCESS, JavaCore.ERROR); + javaProject.setOptions(options); + sourceFolder = javaProject.getPackageFragmentRoot(javaProject.getProject().getFolder("src")); + setIgnoredKind(CodeActionKind.Refactor, JavaCodeActionKind.QUICK_ASSIST, JavaCodeActionKind.SOURCE_OVERRIDE_METHODS, JavaCodeActionKind.SOURCE_GENERATE_TO_STRING, JavaCodeActionKind.SOURCE_GENERATE_CONSTRUCTORS, + JavaCodeActionKind.SOURCE_GENERATE_FINAL_MODIFIERS, JavaCodeActionKind.SOURCE_GENERATE_ACCESSORS, JavaCodeActionKind.REFACTOR_EXTRACT_FIELD, JavaCodeActionKind.REFACTOR_EXTRACT_VARIABLE, + JavaCodeActionKind.REFACTOR_INTRODUCE_PARAMETER, CodeActionKind.RefactorInline); + } + + @Test + public void testUnInitializedFinalField() throws Exception { + IPackageFragment pack1 = sourceFolder.createPackageFragment("org.sample", false, null); + StringBuilder buf = new StringBuilder(); + buf.append("package org.sample;\n"); + buf.append("\n"); + buf.append("public class Foo {\n"); + buf.append(" static final int i;\n"); + buf.append(" final int j;\n"); + buf.append(" static {\n"); + buf.append(" assert (i = 0) == 0;\n"); + buf.append(" System.out.println(i);\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public Foo() {\n"); + buf.append(" }\n"); + buf.append("}\n"); + ICompilationUnit cu = pack1.createCompilationUnit("Foo.java", buf.toString(), false, null); + List> codeActions = evaluateCodeActions(cu); + Either codeAction = codeActions.stream().filter(c -> getTitle(c).matches("Initialize final field 'i' at declaration.")).findFirst().orElse(null); + assertNotNull(codeAction); + buf = new StringBuilder(); + buf.append("package org.sample;\n"); + buf.append("\n"); + buf.append("public class Foo {\n"); + buf.append(" static final int i = 0;\n"); + buf.append(" final int j;\n"); + buf.append(" static {\n"); + buf.append(" assert (i = 0) == 0;\n"); + buf.append(" System.out.println(i);\n"); + buf.append(" }\n"); + buf.append("\n"); + buf.append(" public Foo() {\n"); + buf.append(" }\n"); + buf.append("}\n"); + Expected expected = new Expected("Initialize final field 'i' at declaration.", buf.toString(), CodeActionKind.QuickFix); + Range range = new Range(new Position(4, 22), new Position(4, 22)); + assertCodeActions(cu, range, expected); + } + +}