[java-inspections] SameErasureButDifferentMethodsFix: ModCommand

GitOrigin-RevId: 5fbc113100be4f0d0dfcbf32f97342a69b089b39
This commit is contained in:
Tagir Valeev
2023-11-13 18:45:02 +01:00
committed by intellij-monorepo-bot
parent 9c34f534f0
commit 3f0c4e919a
4 changed files with 49 additions and 115 deletions

View File

@@ -1,149 +1,73 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.intention.impl;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiUpdateModCommandAction;
import com.intellij.psi.*;
import com.intellij.psi.util.JavaClassSupers;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.refactoring.JavaRefactoringFactory;
import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
// @Override void f(List<String> p); -> @Override void f(List<? super String> p);
public class SameErasureButDifferentMethodsFix extends LocalQuickFixAndIntentionActionOnPsiElement {
private final SmartPsiElementPointer<PsiMethod> methodPtr;
public class SameErasureButDifferentMethodsFix extends PsiUpdateModCommandAction<PsiMethod> {
private final SmartPsiElementPointer<PsiMethod> superMethodPtr;
public SameErasureButDifferentMethodsFix(@NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
super(superMethod);
methodPtr = SmartPointerManager.getInstance(method.getProject()).createSmartPsiElementPointer(method);
super(method);
superMethodPtr = SmartPointerManager.createPointer(superMethod);
}
@Override
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
PsiMethod method = methodPtr.getElement();
if (method == null) {
return IntentionPreviewInfo.EMPTY;
}
if (!(getStartElement() instanceof PsiMethod superMethod)) {
return IntentionPreviewInfo.EMPTY;
}
List<ParameterInfoImpl> infos = getParameterInfos(superMethod, method);
String before = getMethodDescription(method, null);
String after = getMethodDescription(method, infos);
if (before == null || after == null) {
return IntentionPreviewInfo.EMPTY;
}
return new IntentionPreviewInfo.CustomDiff(JavaFileType.INSTANCE, before, after);
}
@Nullable
private static String getMethodDescription(@Nullable PsiMethod method, @Nullable List<ParameterInfoImpl> infos) {
if (method == null) {
return null;
}
StringBuilder builder = new StringBuilder();
PsiCodeBlock body = method.getBody();
PsiParameterList list = method.getParameterList();
for (PsiElement child : method.getChildren()) {
if (child == body) {
break;
}
if (child == list && infos != null) {
StringJoiner joiner = new StringJoiner(", ", "(", ")");
for (ParameterInfoImpl info : infos) {
joiner.add(info.getTypeText() + " " + info.getName());
}
builder.append(joiner);
continue;
}
builder.append(child.getText());
}
return builder.toString();
}
@Override
public void invoke(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
if (!isAvailable(project, file, startElement, endElement)) return;
PsiMethod superMethod = (PsiMethod)startElement;
PsiMethod method = methodPtr.getElement();
List<ParameterInfoImpl> infos = getParameterInfos(superMethod, method);
if (infos == null) return;
var processor = JavaRefactoringFactory.getInstance(project)
.createChangeSignatureProcessor(method, false, null, method.getName(), method.getReturnType(), infos.toArray(new ParameterInfoImpl[]{}),
null, null, null, null);
processor.run();
}
@Nullable
private static List<ParameterInfoImpl> getParameterInfos(PsiMethod superMethod, PsiMethod method) {
if (method == null || !method.isValid()) return null;
protected void invoke(@NotNull ActionContext context, @NotNull PsiMethod method, @NotNull ModPsiUpdater updater) {
PsiMethod superMethod = superMethodPtr.getElement();
if (superMethod == null || !superMethod.isValid()) return;
PsiClass containingClass = method.getContainingClass();
PsiClass superContainingClass = superMethod.getContainingClass();
if (containingClass == null || superContainingClass == null) return null;
if (containingClass == null || superContainingClass == null) return;
PsiSubstitutor superSubstitutor = JavaClassSupers.getInstance()
.getSuperClassSubstitutor(superContainingClass, containingClass, containingClass.getResolveScope(), PsiSubstitutor.EMPTY);
if (superSubstitutor == null) return null;
if (superSubstitutor == null) return;
PsiParameter[] parameters = method.getParameterList().getParameters();
PsiParameter[] superParameters = superMethod.getParameterList().getParameters();
if (parameters.length != superParameters.length) return null;
List<ParameterInfoImpl> infos = new ArrayList<>(parameters.length);
if (parameters.length != superParameters.length) return;
updater.trackDeclaration(method);
PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.project());
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
PsiParameter superParameter = superParameters[i];
PsiType superParameterType = superSubstitutor.substitute(superParameter.getType());
infos.add(ParameterInfoImpl.create(i).withName(parameter.getName()).withType(superParameterType));
PsiTypeElement typeElement = parameter.getTypeElement();
if (typeElement != null && !superParameterType.equals(typeElement.getType())) {
typeElement.replace(factory.createTypeElement(superParameterType));
}
}
return infos;
}
@Override
public boolean isAvailable(@NotNull Project project,
@NotNull PsiFile file,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
PsiMethod superMethod = (PsiMethod)startElement;
PsiMethod method = methodPtr.getElement();
if (method == null || !method.isValid()) return false;
JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiMethod method) {
PsiMethod superMethod = superMethodPtr.getElement();
if (superMethod == null || !superMethod.isValid()) return null;
JavaPsiFacade facade = JavaPsiFacade.getInstance(context.project());
PsiClass containingClass = method.getContainingClass();
PsiClass superContainingClass = superMethod.getContainingClass();
if (containingClass == null || superContainingClass == null) return false;
if (!facade.getResolveHelper().isAccessible(superMethod, containingClass, null)) return false;
if (containingClass == null || superContainingClass == null) return null;
if (!facade.getResolveHelper().isAccessible(superMethod, containingClass, null)) return null;
MethodSignature signature = method.getSignature(PsiSubstitutor.EMPTY);
PsiSubstitutor superSubstitutor = JavaClassSupers.getInstance()
.getSuperClassSubstitutor(superContainingClass, containingClass, containingClass.getResolveScope(), PsiSubstitutor.EMPTY);
if (superSubstitutor == null) return false;
if (superSubstitutor == null) return null;
MethodSignature superSignature = superMethod.getSignature(superSubstitutor);
if (method.getParameterList().getParametersCount() != superMethod.getParameterList().getParametersCount()) return false;
return !signature.equals(superSignature) && MethodSignatureUtil.areSignaturesErasureEqual(signature, superSignature);
}
@NotNull
@Override
public String getText() {
PsiMethod method = methodPtr.getElement();
if (method == null || !method.isValid()) return getFamilyName();
return JavaBundle.message("intention.text.fix.method.0.parameters.with.bounded.wildcards", method.getName());
if (method.getParameterList().getParametersCount() != superMethod.getParameterList().getParametersCount()) return null;
if (signature.equals(superSignature) || !MethodSignatureUtil.areSignaturesErasureEqual(signature, superSignature)) return null;
return Presentation.of(JavaBundle.message("intention.text.fix.method.0.parameters.with.bounded.wildcards", method.getName()));
}
@Nls
@@ -152,9 +76,4 @@ public class SameErasureButDifferentMethodsFix extends LocalQuickFixAndIntention
public String getFamilyName() {
return JavaBundle.message("intention.family.fix.bounded.wildcards");
}
@Override
public boolean startInWriteAction() {
return false;
}
}

View File

@@ -955,7 +955,7 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
@NotNull
@Override
public IntentionAction createSameErasureButDifferentMethodsFix(@NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
return new SameErasureButDifferentMethodsFix(method, superMethod);
return new SameErasureButDifferentMethodsFix(method, superMethod).asIntention();
}
@NotNull

View File

@@ -1 +1,17 @@
public void foo(List<? super String> s)
// "Fix method 'foo' parameters with bounded wildcards" "true-preview"
import java.util.List;
interface Xo {
void foo(List<? super String> s);
}
public class ErrWarn implements Xo {
public void foo(List<? super String> s) {
}
}
class D extends ErrWarn {
@Override
public void foo(List<String> s) {
super.foo(s);
}
}