Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate code action to ignore certain compiler issues. #1765

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal;

import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -251,16 +253,45 @@ private static void convertCreateFileChange(WorkspaceEdit edit, CreateFileChange

private static void convertTextChange(TextChange textChange, WorkspaceEdit rootEdit) {
Object modifiedElement = textChange.getModifiedElement();
if (!(modifiedElement instanceof IJavaElement)) {
return;
}

TextEdit textEdits = textChange.getEdit();
if (textEdits == null) {
return;
}
ICompilationUnit compilationUnit = (ICompilationUnit) ((IJavaElement) modifiedElement).getAncestor(IJavaElement.COMPILATION_UNIT);
convertTextEdit(rootEdit, compilationUnit, textEdits);

if (!(modifiedElement instanceof IJavaElement)) {
TextEdit edit = textChange.getEdit();
if (textChange instanceof ExternalFileChange && edit instanceof InsertEdit) {
URI uri = ((ExternalFileChange) textChange).getURI();
String fileUri = ResourceUtils.fixURI(uri);

InsertEdit insertEdit = (InsertEdit) edit;
org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit();
te.setNewText(insertEdit.getText());
Position pos = new Position(0, 0);
te.setRange(new Range(pos, pos));

if (JavaLanguageServerPlugin.getPreferencesManager().getClientPreferences().isResourceOperationSupported()) {
rgrunber marked this conversation as resolved.
Show resolved Hide resolved
List<Either<TextDocumentEdit, ResourceOperation>> changes = rootEdit.getDocumentChanges();
if (changes == null) {
changes = new ArrayList<>();
rootEdit.setDocumentChanges(changes);
}

VersionedTextDocumentIdentifier identifier = new VersionedTextDocumentIdentifier(fileUri, null);
TextDocumentEdit documentEdit = new TextDocumentEdit(identifier, Arrays.asList(te));
changes.add(Either.forLeft(documentEdit));
} else {
Map<String, List<org.eclipse.lsp4j.TextEdit>> changes = rootEdit.getChanges();
List<org.eclipse.lsp4j.TextEdit> edits = new ArrayList<>();
edits.add(te);
changes.put(fileUri, edits);
}
}
} else {
ICompilationUnit compilationUnit = (ICompilationUnit) ((IJavaElement) modifiedElement).getAncestor(IJavaElement.COMPILATION_UNIT);
convertTextEdit(rootEdit, compilationUnit, textEdits);
}
}

private static void convertTextEdit(WorkspaceEdit root, ICompilationUnit unit, TextEdit edit) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2021 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;

import java.net.URI;

import org.eclipse.jface.text.IDocument;
import org.eclipse.ltk.core.refactoring.DocumentChange;

public class ExternalFileChange extends DocumentChange {

private URI file;

public ExternalFileChange(String name, IDocument document, URI file) {
super(name, document);
this.file = file;
}

public URI getURI() {
return file;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal.handlers;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand All @@ -27,23 +29,29 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore;
import org.eclipse.jdt.internal.ui.text.correction.ProblemLocationCore;
import org.eclipse.jdt.ls.core.internal.ChangeUtil;
import org.eclipse.jdt.ls.core.internal.ExternalFileChange;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaCodeActionKind;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
import org.eclipse.jdt.ls.core.internal.corrections.DiagnosticsHelper;
import org.eclipse.jdt.ls.core.internal.corrections.InnovationContext;
import org.eclipse.jdt.ls.core.internal.corrections.QuickFixProcessor;
import org.eclipse.jdt.ls.core.internal.corrections.RefactorProcessor;
import org.eclipse.jdt.ls.core.internal.corrections.proposals.ChangeCorrectionProposal;
import org.eclipse.jdt.ls.core.internal.corrections.proposals.IProposalRelevance;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
import org.eclipse.jdt.ls.core.internal.text.correction.AssignToVariableAssistCommandProposal;
Expand All @@ -52,6 +60,8 @@
import org.eclipse.jdt.ls.core.internal.text.correction.QuickAssistProcessor;
import org.eclipse.jdt.ls.core.internal.text.correction.RefactoringCorrectionCommandProposal;
import org.eclipse.jdt.ls.core.internal.text.correction.SourceAssistProcessor;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.CodeActionKind;
Expand All @@ -61,6 +71,8 @@
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.text.edits.InsertEdit;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
Expand Down Expand Up @@ -159,6 +171,8 @@ public List<Either<Command, CodeAction>> getCodeActionCommands(CodeActionParams
try {
codeActions.addAll(nonProjectFixProcessor.getCorrections(params, context, locations));
List<ChangeCorrectionProposal> quickfixProposals = this.quickFixProcessor.getCorrections(context, locations);
List<ChangeCorrectionProposal> ignoreCompilerProblemProposals = getIgnoreCompilerProblemProposals(diagnostics);
quickfixProposals.addAll(ignoreCompilerProblemProposals);
quickfixProposals.sort(comparator);
proposals.addAll(quickfixProposals);
} catch (CoreException e) {
Expand Down Expand Up @@ -193,6 +207,7 @@ public List<Either<Command, CodeAction>> getCodeActionCommands(CodeActionParams
if (monitor.isCanceled()) {
return Collections.emptyList();
}

try {
for (ChangeCorrectionProposal proposal : proposals) {
Optional<Either<Command, CodeAction>> codeActionFromProposal = getCodeActionFromProposal(proposal, params.getContext());
Expand All @@ -217,6 +232,41 @@ public List<Either<Command, CodeAction>> getCodeActionCommands(CodeActionParams
return codeActions;
}

private List<ChangeCorrectionProposal> getIgnoreCompilerProblemProposals(List<Diagnostic> diagnostics) throws CoreException {
List<ChangeCorrectionProposal> result = new ArrayList<>();
String label = "Ignore this compiler problem";

URI settingsURI = JavaLanguageServerPlugin.getPreferencesManager().getPreferences().getSettingsAsURI();
if (settingsURI != null && URIUtil.isFileURI(settingsURI)) {
File settingsFile = ResourceUtils.toFile(settingsURI);
String content = ResourceUtils.getContent(settingsURI);
IDocument doc = new Document(content);

for (Diagnostic diag : diagnostics) {
int problemId = getProblemId(diag);
int irritant = ProblemReporter.getIrritant(problemId);
if (irritant != 0) {
String compilerOption = CompilerOptions.optionKeyFromIrritant(irritant);
if (compilerOption != null) {
String ignoreEntry = String.format("%s=%s\n", compilerOption, JavaCore.IGNORE);

Change change;
ExternalFileChange editChange = new ExternalFileChange(label, doc, settingsURI);
InsertEdit edit = new InsertEdit(0, ignoreEntry);
editChange.setEdit(edit);
if (settingsFile.exists()) {
change = editChange;
ChangeCorrectionProposal proposal = new ChangeCorrectionProposal(label, CodeActionKind.QuickFix, change, IProposalRelevance.ADD_SUPPRESSWARNINGS);
result.add(proposal);
}
}
}
}
}

return result;
}

private void populateDataFields(List<Either<Command, CodeAction>> codeActions) {
ResponseStore.ResponseItem<Either<ChangeCorrectionProposal, CodeActionProposal>> response = codeActionStore.createResponse();
List<Either<ChangeCorrectionProposal, CodeActionProposal>> proposals = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -48,6 +50,7 @@
import org.eclipse.jdt.ls.core.internal.corrections.CorrectionMessages;
import org.eclipse.jdt.ls.core.internal.preferences.ClientPreferences;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionContext;
import org.eclipse.lsp4j.CodeActionKind;
Expand All @@ -57,6 +60,7 @@
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
Expand Down Expand Up @@ -87,6 +91,9 @@ public void setup() throws Exception{
project = WorkspaceHelper.getProject("hello");
wcOwner = new LanguageServerWorkingCopyOwner(connection);
server = new JDTLanguageServer(projectsManager, this.preferenceManager);

File settings = new File(System.getProperty("java.io.tmpdir"), "settings.prefs");
settings.createNewFile();
}

@Override
Expand All @@ -95,6 +102,12 @@ protected ClientPreferences initPreferenceManager(boolean supportClassFileConten
return clientPreferences;
}

@Override
protected void initPreferences(Preferences preferences) throws IOException {
super.initPreferences(preferences);
preferences.setSettingsUrl(new File("/tmp/settings.prefs").toURI().toString());
}

@Test
public void testCodeAction_removeUnusedImport() throws Exception{
ICompilationUnit unit = getWorkingCopy(
Expand Down Expand Up @@ -605,6 +618,40 @@ public void testCodeAction_unimplementedMethodReference() throws Exception {
Assert.assertNotNull(codeActions);
CodeAction action = codeActions.get(1).getRight();
Assert.assertEquals("Add missing method 'action' to class 'Foo'", action.getTitle());
}

@Test
public void testCodeAction_ignoreCompilerIssue() throws Exception{
when(clientPreferences.isResourceOperationSupported()).thenReturn(true);
ICompilationUnit unit = getWorkingCopy(
"src/java/Foo.java",
"package java;\n"
+ "public class Foo {\n"
+ " @SuppressWarnings(\"deprecation\")\n"
+ " public void test () {\n"
+ " }\n"
+ "}");

CodeActionParams params = new CodeActionParams();
params.setTextDocument(new TextDocumentIdentifier(JDTUtils.toURI(unit)));
final Range range = CodeActionUtil.getRange(unit, "deprecation");
params.setRange(range);
params.setContext(new CodeActionContext(Arrays.asList(getDiagnostic(Integer.toString(IProblem.UnusedWarningToken), range))));
List<Either<Command, CodeAction>> codeActions = getCodeActions(params);

Assert.assertNotNull(codeActions);
Assert.assertFalse(codeActions.isEmpty());
Assert.assertEquals(codeActions.get(0).getRight().getKind(), CodeActionKind.QuickFix);
Command c = codeActions.get(0).getRight().getCommand();
Assert.assertEquals(CodeActionHandler.COMMAND_ID_APPLY_EDIT, c.getCommand());

Assert.assertNotNull(c.getArguments());
Assert.assertTrue(c.getArguments().get(0) instanceof WorkspaceEdit);
WorkspaceEdit we = (WorkspaceEdit) c.getArguments().get(0);
Assert.assertEquals(1, we.getDocumentChanges().size());

TextDocumentEdit textDocEdit = we.getDocumentChanges().get(0).getLeft();
Assert.assertEquals("org.eclipse.jdt.core.compiler.problem.unusedWarningToken=ignore\n", textDocEdit.getEdits().get(0).getNewText());
}

private List<Either<Command, CodeAction>> getCodeActions(CodeActionParams params) {
Expand Down