[java-inspections] DeferFinalAssignmentFix: ModCommand

GitOrigin-RevId: b71aaf9098c2d60e69b63c18408845492918dc65
This commit is contained in:
Tagir Valeev
2023-07-06 18:26:03 +02:00
committed by intellij-monorepo-bot
parent 090e1b85cf
commit 001d26930f
10 changed files with 44 additions and 101 deletions

View File

@@ -1,59 +1,35 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.FileModifier;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInspection.PsiUpdateModCommandAction;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ig.psiutils.VariableNameGenerator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class DeferFinalAssignmentFix implements IntentionAction {
public class DeferFinalAssignmentFix extends PsiUpdateModCommandAction<PsiVariable> {
private static final Logger LOG = Logger.getInstance(DeferFinalAssignmentFix.class);
private final PsiVariable variable;
private final PsiReferenceExpression expression;
public DeferFinalAssignmentFix(@NotNull PsiVariable variable, @NotNull PsiReferenceExpression expression) {
this.variable = variable;
super(variable);
this.expression = expression;
}
@Override
public @Nullable FileModifier getFileModifierForPreview(@NotNull PsiFile target) {
if (variable.getContainingFile() != expression.getContainingFile()) return null;
return new DeferFinalAssignmentFix(PsiTreeUtil.findSameElementInCopy(variable, target),
PsiTreeUtil.findSameElementInCopy(expression, target));
}
@Override
@NotNull
public String getFamilyName() {
@@ -61,33 +37,24 @@ public class DeferFinalAssignmentFix implements IntentionAction {
}
@Override
@NotNull
public String getText() {
return QuickFixBundle.message("defer.final.assignment.with.temp.text", variable.getName());
}
@Nullable
@Override
public PsiElement getElementToMakeWritable(@NotNull PsiFile currentFile) {
return variable.getContainingFile();
protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiVariable variable) {
boolean available = expression.isValid() && !(variable instanceof PsiParameter) && !(variable instanceof ImplicitVariable);
return available ? Presentation.of(QuickFixBundle.message("defer.final.assignment.with.temp.text", variable.getName())) : null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (variable instanceof PsiField) {
deferField((PsiField)variable);
protected void invoke(@NotNull ActionContext context, @NotNull PsiVariable variable, @NotNull ModPsiUpdater updater) {
if (variable instanceof PsiField field) {
PsiCodeBlock codeBlock = getEnclosingCodeBlock(field, updater.getWritable(expression));
if (codeBlock == null) return;
deferVariable(codeBlock, variable, null, updater);
}
else {
deferLocalVariable((PsiLocalVariable)variable);
PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null);
deferVariable(outerCodeBlock, variable, variable.getParent(), updater);
}
}
private void deferField(PsiField field) throws IncorrectOperationException {
PsiCodeBlock codeBlock = getEnclosingCodeBlock(field, expression);
if (codeBlock == null) return;
deferVariable(codeBlock, field, null);
}
private static PsiCodeBlock getEnclosingCodeBlock(PsiField field, PsiElement element) {
PsiClass aClass = field.getContainingClass();
if (aClass == null) return null;
@@ -107,19 +74,20 @@ public class DeferFinalAssignmentFix implements IntentionAction {
return null;
}
private static void deferLocalVariable(PsiLocalVariable variable) throws IncorrectOperationException {
PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null);
deferVariable(outerCodeBlock, variable, variable.getParent());
}
private static void deferVariable(PsiElement outerCodeBlock, PsiVariable variable, PsiElement tempDeclarationAnchor) throws IncorrectOperationException {
private static void deferVariable(@Nullable PsiElement outerCodeBlock,
@NotNull PsiVariable variable,
@Nullable PsiElement tempDeclarationAnchor,
@NotNull ModPsiUpdater updater) throws IncorrectOperationException {
if (outerCodeBlock == null) return;
List<PsiReferenceExpression> outerReferences = new ArrayList<>();
collectReferences(outerCodeBlock, variable, outerReferences);
PsiElementFactory factory = JavaPsiFacade.getElementFactory(variable.getProject());
Project project = variable.getProject();
String tempName = suggestNewName(project, variable);
List<String> names =
new VariableNameGenerator(outerCodeBlock, VariableKind.LOCAL_VARIABLE).byName(variable.getName() + "1").byType(variable.getType())
.generateAll(true);
String tempName = names.get(0);
PsiDeclarationStatement tempVariableDeclaration = factory.createVariableDeclarationStatement(tempName, variable.getType(), null);
ControlFlow controlFlow;
@@ -149,9 +117,10 @@ public class DeferFinalAssignmentFix implements IntentionAction {
PsiStatement finalAssignment = factory.createStatementFromText(writeReference.getText()+" = "+tempName+";", outerCodeBlock);
if (!insertToDefinitelyReachedPlace(outerCodeBlock, finalAssignment, controlFlow, minOffset, outerReferences)) return;
outerCodeBlock.addAfter(tempVariableDeclaration, tempDeclarationAnchor);
tempVariableDeclaration = (PsiDeclarationStatement)outerCodeBlock.addAfter(tempVariableDeclaration, tempDeclarationAnchor);
replaceReferences(outerReferences, factory.createExpressionFromText(tempName, outerCodeBlock));
updater.rename(((PsiVariable)tempVariableDeclaration.getDeclaredElements()[0]), names);
}
@@ -198,30 +167,4 @@ public class DeferFinalAssignmentFix implements IntentionAction {
}
});
}
private static String suggestNewName(Project project, PsiVariable variable) {
// new name should not conflict with another variable at the variable declaration level and usage level
String name = Objects.requireNonNull(variable.getName());
// trim last digit to suggest variable names like i1, i2, i3...
if (name.length() > 1 && Character.isDigit(name.charAt(name.length()-1))) {
name = name.substring(0,name.length()-1);
}
return JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(name, variable, true);
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return
variable.isValid() &&
!(variable instanceof PsiParameter) &&
!(variable instanceof ImplicitVariable) &&
expression.isValid() &&
BaseIntentionAction.canModify(variable)
;
}
@Override
public boolean startInWriteAction() {
return true;
}
}

View File

@@ -588,7 +588,7 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
@NotNull
@Override
public IntentionAction createDeferFinalAssignmentFix(@NotNull PsiVariable variable, @NotNull PsiReferenceExpression expression) {
return new DeferFinalAssignmentFix(variable, expression);
return new DeferFinalAssignmentFix(variable, expression).asIntention();
}
@NotNull

View File

@@ -4,11 +4,11 @@ import java.io.*;
class a {
void f(InputStream in) {
final int n;
int n1;
int n1<caret>;
try {
n1 = in.read();
} catch (IOException e) {
<caret>n1 = -1;
n1 = -1;
}
n = n1;
int y = 4;

View File

@@ -4,10 +4,10 @@ import java.io.*;
class a {
void f(InputStream in) {
final int n;
int n1;
int n1<caret>;
if (in==null) {
n1 = 2;
<caret>n1 = 2;
n1 = 2;
}
else {
n1 =4;

View File

@@ -4,10 +4,10 @@ import java.io.*;
class a {
void f(InputStream in) {
final int n;
int n1;
int n1<caret>;
if (in==null) {
n1 = 2;
<caret>n1 = 2;
n1 = 2;
int h = n1;
}
else {

View File

@@ -4,10 +4,10 @@ import java.io.*;
class a {
final int n;
a(InputStream in) {
int n1;
int n1<caret>;
if (in==null) {
n1 = 2;
<caret>n1 = 2;
n1 = 2;
int h = n1;
}
else {

View File

@@ -4,9 +4,9 @@ import java.io.*;
class a {
void f(int k) {
final int i;
int i1;
int i1<caret>;
i1 = 4;
i1 = 4;
<caret>i1 = 4;
i = i1;
f(i);
}

View File

@@ -4,9 +4,9 @@ import java.io.*;
class a {
final int n;
{
int n1;
int n1<caret>;
n1 =3;
n1 =3;
<caret>n1 =3;
n = n1;
}
}

View File

@@ -4,9 +4,9 @@ import java.io.*;
class a {
final int n;
a(int n) {
int n1;
int n1<caret>;
n1 =n;
n1 =n;
<caret>n1 =n;
this.n = n1;
}
}

View File

@@ -4,11 +4,11 @@ import java.io.*;
class a {
void f(InputStream in) {
final int n;
int n1;
int n1<caret>;
try {
n1 = in.read();
} catch (IOException e) {
<caret>n1 = -1;
n1 = -1;
f(in);
}