[java-intentions] Remove unused assignment/initializer: preview-friendly

GitOrigin-RevId: de9da180223f231638eb51804b7ed9c26c36d772
This commit is contained in:
Tagir Valeev
2022-07-29 15:47:33 +02:00
committed by intellij-monorepo-bot
parent 7e1064ea3c
commit 1361a10017
35 changed files with 125 additions and 76 deletions

View File

@@ -41,6 +41,13 @@ public final class RemoveUnusedVariableUtil {
return !writes.isEmpty();
}
public static RemoveMode getModeForPreview(PsiExpression element, @Nullable PsiVariable variableToIgnore) {
List<PsiElement> sideEffects = new ArrayList<>();
boolean hasSideEffects = checkSideEffects(element, variableToIgnore, sideEffects);
if (!hasSideEffects || sideEffects.isEmpty()) return RemoveMode.DELETE_ALL;
return RemoveMode.MAKE_STATEMENT;
}
public static PsiElement replaceElementWithExpression(PsiExpression expression,
PsiElementFactory factory,
PsiElement element) throws IncorrectOperationException {

View File

@@ -16,13 +16,16 @@
package com.intellij.codeInspection;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableUtil;
import com.intellij.codeInsight.editorActions.DeclarationJoinLinesHandler;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,15 +39,8 @@ public class RemoveAssignmentFix extends RemoveInitializerFix {
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
final PsiElement parent;
if (element instanceof PsiReferenceExpression) {
parent = element.getParent();
} else {
parent = element;
}
if (!(parent instanceof PsiAssignmentExpression)) return;
PsiAssignmentExpression parentExpr = (PsiAssignmentExpression)parent;
PsiAssignmentExpression parentExpr = getAssignment(descriptor);
if (parentExpr == null) return;
PsiElement gParentExpr = parentExpr.getParent();
PsiExpression initializer = getInitializer(parentExpr);
if (initializer == null) return;
@@ -63,9 +59,37 @@ public class RemoveAssignmentFix extends RemoveInitializerFix {
PsiElement resolve = resolveExpression(element, parentExpr);
if (!(resolve instanceof PsiVariable)) return;
sideEffectAwareRemove(project, initializer, parent, (PsiVariable)resolve);
sideEffectAwareRemove(project, initializer, parentExpr, (PsiVariable)resolve);
}
PsiAssignmentExpression getAssignment(@NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
final PsiElement parent = element instanceof PsiReferenceExpression ? element.getParent() : element;
return ObjectUtils.tryCast(parent, PsiAssignmentExpression.class);
}
@Override
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
final PsiElement element = previewDescriptor.getPsiElement();
PsiAssignmentExpression parentExpr = getAssignment(previewDescriptor);
if (parentExpr == null) return IntentionPreviewInfo.EMPTY;
PsiElement gParentExpr = parentExpr.getParent();
PsiExpression initializer = getInitializer(parentExpr);
if (initializer == null) return IntentionPreviewInfo.EMPTY;
if (mayBeFixedWithoutSideEffect(gParentExpr)) {
PsiElement target = gParentExpr instanceof PsiParenthesizedExpression ? gParentExpr : parentExpr;
target.replace(initializer);
return IntentionPreviewInfo.DIFF;
}
PsiVariable resolve = ObjectUtils.tryCast(resolveExpression(element, parentExpr), PsiVariable.class);
if (resolve == null) return IntentionPreviewInfo.EMPTY;
RemoveUnusedVariableUtil.RemoveMode res = RemoveUnusedVariableUtil.getModeForPreview(initializer, resolve);
doRemove(project, initializer, parentExpr, resolve, resolve.getParent(), res);
return IntentionPreviewInfo.DIFF;
}
@Nullable
private static PsiExpression getInitializer(@NotNull PsiAssignmentExpression assignmentExpr) {
final IElementType operationSign = assignmentExpr.getOperationTokenType();

View File

@@ -19,15 +19,16 @@ import com.intellij.codeInsight.BlockUtils;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableFix;
import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableUtil;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiExpressionTrimRenderer;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.NotNull;
@@ -36,7 +37,6 @@ import java.util.ArrayList;
import java.util.List;
public class RemoveInitializerFix implements LocalQuickFix {
private static final Logger LOG = Logger.getInstance(RemoveInitializerFix.class);
@Override
@NotNull
@@ -96,38 +96,56 @@ public class RemoveInitializerFix implements LocalQuickFix {
else {
res = RemoveUnusedVariableUtil.RemoveMode.DELETE_ALL;
}
WriteAction.run(() -> {
if (res == RemoveUnusedVariableUtil.RemoveMode.DELETE_ALL) {
if (elementToDelete instanceof PsiExpression && !ExpressionUtils.isVoidContext((PsiExpression)elementToDelete) &&
!PsiTreeUtil.isAncestor(variable, elementToDelete, true)) {
String name = variable.getName();
if (name != null) {
new CommentTracker().replaceAndRestoreComments(elementToDelete, name);
return;
}
}
elementToDelete.delete();
}
else if (res == RemoveUnusedVariableUtil.RemoveMode.MAKE_STATEMENT) {
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiStatement statementFromText = factory.createStatementFromText(psiInitializer.getText() + ";", null);
final PsiElement parent = elementToDelete.getParent();
if (parent instanceof PsiExpressionStatement) {
parent.replace(statementFromText);
}
else {
elementToDelete.delete();
if (declaration instanceof PsiClass) {
PsiClassInitializer initializer = factory.createClassInitializer();
initializer = (PsiClassInitializer)declaration.addAfter(initializer, variable);
initializer.getBody().add(statementFromText);
return;
}
PsiElement grandParent = declaration.getParent();
BlockUtils.addBefore(((PsiStatement) (grandParent instanceof PsiForStatement ? grandParent : declaration)), statementFromText);
}
}
});
WriteAction.run(() -> doRemove(project, psiInitializer, elementToDelete, variable, declaration, res));
return true;
}
@Override
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
PsiExpression psiInitializer = ObjectUtils.tryCast(previewDescriptor.getPsiElement(), PsiExpression.class);
if (psiInitializer == null) return IntentionPreviewInfo.EMPTY;
PsiVariable variable = ObjectUtils.tryCast(psiInitializer.getParent(), PsiVariable.class);
if (variable == null) return IntentionPreviewInfo.EMPTY;
RemoveUnusedVariableUtil.RemoveMode res = RemoveUnusedVariableUtil.getModeForPreview(psiInitializer, variable);
doRemove(project, psiInitializer, psiInitializer, variable, variable.getParent(), res);
return IntentionPreviewInfo.DIFF;
}
static void doRemove(@NotNull Project project,
@NotNull PsiExpression psiInitializer,
@NotNull PsiElement elementToDelete,
@NotNull PsiVariable variable,
PsiElement declaration,
@NotNull RemoveUnusedVariableUtil.RemoveMode res) {
if (res == RemoveUnusedVariableUtil.RemoveMode.DELETE_ALL) {
if (elementToDelete instanceof PsiExpression && !ExpressionUtils.isVoidContext((PsiExpression)elementToDelete) &&
!PsiTreeUtil.isAncestor(variable, elementToDelete, true)) {
String name = variable.getName();
if (name != null) {
new CommentTracker().replaceAndRestoreComments(elementToDelete, name);
return;
}
}
elementToDelete.delete();
}
else if (res == RemoveUnusedVariableUtil.RemoveMode.MAKE_STATEMENT) {
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiStatement statementFromText = factory.createStatementFromText(psiInitializer.getText() + ";", null);
final PsiElement parent = elementToDelete.getParent();
if (parent instanceof PsiExpressionStatement) {
parent.replace(statementFromText);
}
else {
elementToDelete.delete();
if (declaration instanceof PsiClass) {
PsiClassInitializer initializer = factory.createClassInitializer();
initializer = (PsiClassInitializer)declaration.addAfter(initializer, variable);
initializer.getBody().add(statementFromText);
return;
}
PsiElement grandParent = declaration.getParent();
BlockUtils.addBefore(((PsiStatement)(grandParent instanceof PsiForStatement ? grandParent : declaration)), statementFromText);
}
}
}
}

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
public String getContexts(final String env) {
String contexts = "a";

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class Test {
void foo() {
String var;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
static int n;
static {

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class Foo {
int sent;

View File

@@ -1,4 +1,4 @@
// "Remove redundant initializer" "true"
// "Remove redundant initializer" "true-preview"
class A {
int n;
{ n = 1; }

View File

@@ -1,4 +1,4 @@
// "Remove redundant initializer" "true"
// "Remove redundant initializer" "true-preview"
class A {
private String myFoo;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class X {
void test(boolean b) {
if (b) {}

View File

@@ -1,4 +1,4 @@
// "Remove redundant initializer" "true"
// "Remove redundant initializer" "true-preview"
class A {
void testFor() {
if (true) {

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
int v() {
int x;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
public static void main(String[] args) {
int x = 3 * 4;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class Foo {
void bar(int begin) {
int current;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
void m() {
int i;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
void m(int i) {
i = 0;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
record R2(int y) {
public R2 {
this.y = y;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
A a = null;
String m(String str) {

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
{
String ss = "";

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
public String getContexts(final String env) {
String contexts = "a";

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class Test {
void foo() {
String var;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
static int n;
static { <caret>n = 1; }

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class Foo {
int sent;

View File

@@ -1,4 +1,4 @@
// "Remove redundant initializer" "true"
// "Remove redundant initializer" "true-preview"
class A {
int n = <caret>0;
{ n = 1; }

View File

@@ -1,4 +1,4 @@
// "Remove redundant initializer" "true"
// "Remove redundant initializer" "true-preview"
class A {
private String myFoo = ab<caret>c();

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class X {
void test(boolean b) {
if (<caret>b ^= false) {}

View File

@@ -1,4 +1,4 @@
// "Remove redundant initializer" "true"
// "Remove redundant initializer" "true-preview"
class A {
void testFor() {
if (true)

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
int v() {
int x;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
public static void main(String[] args) {
int x = (<caret>x = 3) * 4;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class Foo {
void bar(int begin) {
int current;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
void m() {
int i;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
void m(int i) {
<caret>i = 9;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
record R2(int y) {
public R2 {
this.y = y;

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
A a = null;
String m(String str) {

View File

@@ -1,4 +1,4 @@
// "Remove redundant assignment" "true"
// "Remove redundant assignment" "true-preview"
class A {
{
String ss = "";