[java-inspections] CreateFieldFromParameterAction: ModCommand

Otherwise, it's hard to make it compatible with new-style unused inspection (IDEA-349083)
Also: remove empty line in constructor body automatically

GitOrigin-RevId: ab1cd08352d9f0168b86acc1d4b87cb89cfe7d69
This commit is contained in:
Tagir Valeev
2024-03-21 10:55:45 +01:00
committed by intellij-monorepo-bot
parent 5ceb7551aa
commit 6c6697d422
13 changed files with 128 additions and 50 deletions

View File

@@ -492,9 +492,9 @@ public abstract class QuickFixFactory {
public abstract IntentionAction createAddEmptyRecordHeaderFix(@NotNull PsiClass record);
@NotNull
public abstract IntentionAction createCreateFieldFromParameterFix();
public abstract IntentionAction createCreateFieldFromParameterFix(@NotNull PsiParameter parameter);
@NotNull
public abstract IntentionAction createAssignFieldFromParameterFix();
public abstract IntentionAction createAssignFieldFromParameterFix(@NotNull PsiParameter parameter);
@NotNull
public abstract IntentionAction createFillPermitsListFix(@NotNull PsiIdentifier classIdentifier);

View File

@@ -281,7 +281,7 @@ public final class UnusedSymbolLocalInspection extends AbstractBaseJavaLocalInsp
registerProblem(
parameter, message,
ContainerUtil.append(
getFixesForUnusedParameter(method),
getFixesForUnusedParameter(method, parameter),
quickFixFactory.createRenameToIgnoredFix(parameter, true),
PriorityIntentionActionWrapper.highPriority(quickFixFactory.createSafeDeleteUnusedParameterInHierarchyFix(
parameter, checkParameterExcludingHierarchy() && isOverriddenOrOverrides(method)))));
@@ -341,17 +341,14 @@ public final class UnusedSymbolLocalInspection extends AbstractBaseJavaLocalInsp
return null;
}
private static @NotNull List<IntentionAction> getFixesForUnusedParameter(@Nullable PsiMethod declarationMethod) {
if (declarationMethod != null) {
IntentionAction assignFix = QuickFixFactory.getInstance().createAssignFieldFromParameterFix();
IntentionAction createFieldFix = QuickFixFactory.getInstance().createCreateFieldFromParameterFix();
if (!declarationMethod.isConstructor()) {
assignFix = PriorityIntentionActionWrapper.lowPriority(assignFix);
createFieldFix = PriorityIntentionActionWrapper.lowPriority(createFieldFix);
}
return List.of(assignFix, createFieldFix);
private static @NotNull List<IntentionAction> getFixesForUnusedParameter(@NotNull PsiMethod declarationMethod, @NotNull PsiParameter parameter) {
IntentionAction assignFix = QuickFixFactory.getInstance().createAssignFieldFromParameterFix(parameter);
IntentionAction createFieldFix = QuickFixFactory.getInstance().createCreateFieldFromParameterFix(parameter);
if (!declarationMethod.isConstructor()) {
assignFix = PriorityIntentionActionWrapper.lowPriority(assignFix);
createFieldFix = PriorityIntentionActionWrapper.lowPriority(createFieldFix);
}
return List.of();
return List.of(assignFix, createFieldFix);
}
@Override

View File

@@ -13,7 +13,10 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
@@ -27,11 +30,13 @@ public final class AssignFieldFromParameterAction extends PsiUpdateModCommandAct
private final boolean myIsFix;
public AssignFieldFromParameterAction() {
this(false);
}
public AssignFieldFromParameterAction(boolean isFix) {
super(PsiParameter.class);
myIsFix = isFix;
myIsFix = false;
}
public AssignFieldFromParameterAction(@NotNull PsiParameter parameter) {
super(parameter);
myIsFix = true;
}
@Override

View File

@@ -5,37 +5,49 @@ import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingLevelManager;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
import com.intellij.java.JavaBundle;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiUpdateModCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author Max Medvedev
*/
public final class CreateFieldFromParameterAction extends CreateFieldFromParameterActionBase {
public final class CreateFieldFromParameterAction extends PsiUpdateModCommandAction<PsiParameter> {
private final boolean myIsFix;
/** intention entry point, see /META-INF/JavaPlugin.xml */
public CreateFieldFromParameterAction() {
this(false);
super(PsiParameter.class);
myIsFix = false;
}
/** quickfix entry point, see {@link com.intellij.codeInsight.intention.QuickFixFactory#createCreateFieldFromParameterFix()} */
public CreateFieldFromParameterAction(boolean isFix) {
myIsFix = isFix;
/** quickfix entry point, see {@link com.intellij.codeInsight.intention.QuickFixFactory#createCreateFieldFromParameterFix(PsiParameter)} ()} */
public CreateFieldFromParameterAction(@NotNull PsiParameter parameter) {
super(parameter);
myIsFix = true;
}
@Override
protected boolean isAvailable(@NotNull PsiParameter parameter) {
private boolean isAvailable(@NotNull PsiParameter parameter) {
PsiElement scope = parameter.getDeclarationScope();
if (!(scope instanceof PsiMethod)) {
if (!(scope instanceof PsiMethod method)) {
return false;
}
PsiCodeBlock body = ((PsiMethod)scope).getBody();
PsiCodeBlock body = method.getBody();
if (body == null) return false;
if (!myIsFix && !VariableAccessUtils.variableIsUsed(parameter, body) && isUnusedSymbolInspectionEnabled(body)) {
@@ -48,6 +60,45 @@ public final class CreateFieldFromParameterAction extends CreateFieldFromParamet
parameter.getLanguage().isKindOf(JavaLanguage.INSTANCE);
}
@Override
protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiParameter parameter) {
if (!isAvailable(parameter)) {
return null;
}
return Presentation.of(JavaBundle.message("intention.create.field.from.parameter.text", parameter.getName()));
}
@Override
@NotNull
public String getFamilyName() {
return JavaBundle.message("intention.create.field.from.parameter.family");
}
@Override
protected void invoke(@NotNull ActionContext context, @NotNull PsiParameter parameter, @NotNull ModPsiUpdater updater) {
Project project = parameter.getProject();
PsiType type = getSubstitutedType(parameter);
JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
String parameterName = parameter.getName();
String propertyName = styleManager.variableNameToPropertyName(parameterName, VariableKind.PARAMETER);
PsiMethod method = (PsiMethod)parameter.getDeclarationScope();
PsiClass targetClass = method.getContainingClass();
if (targetClass == null) return;
boolean isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC);
VariableKind kind = isMethodStatic ? VariableKind.STATIC_FIELD : VariableKind.FIELD;
SuggestedNameInfo suggestedNameInfo = styleManager.suggestVariableName(kind, propertyName, null, type);
SuggestedNameInfo uniqueNameInfo = styleManager.suggestUniqueVariableName(suggestedNameInfo, targetClass, true);
boolean isFinal = !isMethodStatic && method.isConstructor();
PsiVariable variable = createField(project, targetClass, method, parameter, type, uniqueNameInfo.names[0], isMethodStatic, isFinal);
updater.rename(variable, List.of(uniqueNameInfo.names));
}
private static boolean isUnusedSymbolInspectionEnabled(@NotNull PsiElement element) {
HighlightDisplayKey unusedSymbolKey = HighlightDisplayKey.find(UnusedDeclarationInspectionBase.SHORT_NAME);
PsiFile file = element.getContainingFile();
@@ -60,20 +111,18 @@ public final class CreateFieldFromParameterAction extends CreateFieldFromParamet
return levelManager.shouldInspect(file);
}
@Override
protected PsiType getSubstitutedType(@NotNull PsiParameter parameter) {
private static PsiType getSubstitutedType(@NotNull PsiParameter parameter) {
return FieldFromParameterUtils.getSubstitutedType(parameter);
}
@Override
protected PsiVariable createField(@NotNull Project project,
@NotNull PsiClass targetClass,
@NotNull PsiMethod method,
@NotNull PsiParameter myParameter,
PsiType type,
@NotNull String fieldName,
boolean methodStatic,
boolean isFinal) {
private static PsiVariable createField(@NotNull Project project,
@NotNull PsiClass targetClass,
@NotNull PsiMethod method,
@NotNull PsiParameter myParameter,
PsiType type,
@NotNull String fieldName,
boolean methodStatic,
boolean isFinal) {
return FieldFromParameterUtils.createFieldAndAddAssignment(project, targetClass, method, myParameter, type, fieldName, methodStatic, isFinal);
}
}

View File

@@ -3,6 +3,7 @@ package com.intellij.codeInsight.intention.impl;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
import com.intellij.lang.ASTFactory;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
@@ -219,6 +220,18 @@ public final class FieldFromParameterUtils {
if (methodBody == null) return null;
PsiStatement[] statements = methodBody.getStatements();
if (statements.length == 0) {
PsiElement element = methodBody.getFirstBodyElement();
if (element instanceof PsiWhiteSpace whiteSpace) {
String text = whiteSpace.getText();
int lastLineBreak = text.lastIndexOf('\n');
if (lastLineBreak >= 0 && text.indexOf('\n') != lastLineBreak) {
// At least two linebreaks in the body: remove last one
text = text.substring(0, lastLineBreak);
methodBody.getNode().replaceChild(whiteSpace.getNode(), ASTFactory.leaf(TokenType.WHITE_SPACE, text));
}
}
}
Ref<PsiField> anchor = new Ref<>();
AtomicBoolean isBefore = new AtomicBoolean();

View File

@@ -1036,13 +1036,13 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
}
@Override
public @NotNull IntentionAction createCreateFieldFromParameterFix() {
return new CreateFieldFromParameterAction(true);
public @NotNull IntentionAction createCreateFieldFromParameterFix(@NotNull PsiParameter parameter) {
return new CreateFieldFromParameterAction(parameter).asIntention();
}
@Override
public @NotNull IntentionAction createAssignFieldFromParameterFix() {
return new AssignFieldFromParameterAction(true).asIntention();
public @NotNull IntentionAction createAssignFieldFromParameterFix(@NotNull PsiParameter parameter) {
return new AssignFieldFromParameterAction(parameter).asIntention();
}
@Override

View File

@@ -4,7 +4,6 @@ class BindField {
private final Integer myInstrumentAgent;
public BindField(String agent, Integer instrumentAgent) {
myAgent = agent;
myInstrumentAgent = instrumentAgent;
}

View File

@@ -2,7 +2,7 @@
class Person {
private String __fname;
private final int myId;
private final int <caret>myId;
private String __lname;
private String __street;
@@ -14,7 +14,7 @@ class Person {
__street = i_street;
}
public Person ( int id<caret>, String i_lname, String i_fname, String i_street)
public Person ( int id, String i_lname, String i_fname, String i_street)
{
myId = id;
__lname = i_lname;

View File

@@ -6,10 +6,10 @@ import java.util.HashMap;
public class TestBefore {
private String myName;
private final HashMap myTest;
private final HashMap <caret>myTest;
private String myPerson;
public TestBefore(String name, int length, HashMap test<caret>, String person) {
public TestBefore(String name, int length, HashMap test, String person) {
super();
myName = name;
myTest = test;

View File

@@ -1,10 +1,10 @@
// "Create field for parameter 'p1'" "true-preview"
class Test{
private final int myP1;
private final int <caret>myP1;
int myP2;
Test(int p<caret>1, int p2){
Test(int p1, int p2){
myP1 = p1;
myP2 = p2;
}

View File

@@ -0,0 +1,9 @@
// "Create field for parameter 'name'" "true-preview"
class Person {
private final String myName;
public Person(String name) {
myName = name;
}
}

View File

@@ -0,0 +1,7 @@
// "Create field for parameter 'name'" "true-preview"
class Person {
public Person(String na<caret>me) {
}
}

View File

@@ -141,7 +141,6 @@ public class IntentionPreviewTest extends LightJavaCodeInsightFixtureTestCase {
private final String b;
Test(int a, String b) {
this.a = a;
this.b = b;
}