[java] IDEA-216276 Provide fix for "method is already defined in"

For each "method is already defined in" error, a quick fix is added with "Navigate to duplicate method" text. In case of many duplicates, quick fix navigates to the topmost one.

Merge-request: IJ-MR-170181
Merged-by: Marcin Mikosik <marcin.mikosik@jetbrains.com>

GitOrigin-RevId: d609be3a3e4526bed37471f814d2e675cbd0d2a6
This commit is contained in:
Marcin Mikosik
2025-07-31 11:45:47 +00:00
committed by intellij-monorepo-bot
parent e5a45550c6
commit 787051b0be
16 changed files with 128 additions and 5 deletions
@@ -527,7 +527,7 @@ final class MethodChecker {
MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY);
List<PsiMethod> methods = (List<PsiMethod>)duplicateMethods.get(methodSignature);
if (methods.size() > 1) {
myVisitor.report(JavaErrorKinds.METHOD_DUPLICATE.create(method));
myVisitor.report(JavaErrorKinds.METHOD_DUPLICATE.create(method, new JavaErrorKinds.DuplicateMethodsContext(methods)));
}
}
@@ -640,11 +640,11 @@ public final class JavaErrorKinds {
owner.getTypeParameters().length));
public static final Simple<PsiTypeElement> TYPE_PARAMETER_ACTUAL_INFERRED_MISMATCH = error("type.parameter.actual.inferred.mismatch");
public static final Simple<PsiMethod> METHOD_DUPLICATE =
error(PsiMethod.class, "method.duplicate")
.withRange(JavaErrorFormatUtil::getMethodDeclarationTextRange)
public static final Parameterized<PsiMethod, DuplicateMethodsContext> METHOD_DUPLICATE =
parameterized(PsiMethod.class, DuplicateMethodsContext.class, "method.duplicate")
.withRange((method, duplicates) -> getMethodDeclarationTextRange(method))
.withDescription(
method -> message("method.duplicate", formatMethod(method), formatClass(requireNonNull(method.getContainingClass()))));
(method, duplicates) -> message("method.duplicate", formatMethod(method), formatClass(requireNonNull(method.getContainingClass()))));
public static final Simple<PsiMethod> METHOD_NO_PARAMETER_LIST =
error(PsiMethod.class, "method.no.parameter.list").withAnchor(PsiMethod::getNameIdentifier);
public static final Simple<PsiJavaCodeReferenceElement> METHOD_THROWS_CLASS_NAME_EXPECTED =
@@ -1775,4 +1775,6 @@ public final class JavaErrorKinds {
public record DeconstructionCountMismatchContext(@NotNull PsiPattern @NotNull [] patternComponents,
@NotNull PsiRecordComponent @NotNull [] recordComponents,
boolean hasMismatch) {}
public record DuplicateMethodsContext(@NotNull List<@NotNull PsiMethod> methods) {}
}
@@ -14,6 +14,7 @@ import com.intellij.codeInspection.dataFlow.fix.RedundantInstanceofFix;
import com.intellij.core.JavaPsiBundle;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.java.codeserver.core.JavaPsiMethodUtil;
import com.intellij.java.codeserver.core.JavaPsiModifierUtil;
import com.intellij.java.codeserver.core.JavaPsiSwitchUtil;
import com.intellij.java.codeserver.core.JpmsModuleAccessInfo;
@@ -334,6 +335,13 @@ public final class DefaultJavaErrorFixProvider extends AbstractJavaErrorFixProvi
fix(METHOD_GENERIC_CLASH, error ->
error.context().method() instanceof SyntheticElement ?
null : myFactory.createSameErasureButDifferentMethodsFix(error.context().method(), error.context().superMethod()));
fixes(METHOD_DUPLICATE, (error, sink) -> {
error.context().methods().stream()
.filter(m -> !m.equals(error.psi()))
.filter(m -> !(m instanceof SyntheticElement)) // filters out synthetic methods, such as Enum#values()
.findFirst()
.ifPresent(m -> sink.accept(myFactory.createNavigateToDuplicateElementFix(m)));
});
}
private void createExceptionFixes() {
@@ -0,0 +1,7 @@
// "Navigate to duplicate method" "true"
public class MyClass {
public void foo() {}
public void <selection><caret>foo</selection>() {}
public void foo() {}
}
@@ -0,0 +1,7 @@
// "Navigate to duplicate method" "true"
public class MyClass {
public <T> void foo(T bar) {}
public <T> void <selection><caret>foo</selection>(T bar) {}
public <T> void foo(T bar) {}
}
@@ -0,0 +1,7 @@
// "Navigate to duplicate method" "true"
public class MyClass {
public void <selection><caret>foo</selection>() {}
public void foo() {}
public void foo() {}
}
@@ -0,0 +1,11 @@
// "Navigate to duplicate method" "true"
enum MyEnum {
FIRST;
public MyEnum valueOf(String s) {
return null;
}
public MyEnum <selection><caret>valueOf</selection>(String s) {
return null;
}
}
@@ -0,0 +1,11 @@
// "Navigate to duplicate method" "true"
enum MyEnum {
FIRST;
public MyEnum[] values() {
return null;
}
public MyEnum[] <selection><caret>values</selection>() {
return null;
}
}
@@ -0,0 +1,8 @@
// "Navigate to duplicate method" "false"
enum MyEnum {
FIRST;
public MyEnum valueOf<caret>(String s) {
return null;
}
}
@@ -0,0 +1,8 @@
// "Navigate to duplicate method" "false"
enum MyEnum {
FIRST;
public MyEnum[] values<caret>() {
return null;
}
}
@@ -0,0 +1,7 @@
// "Navigate to duplicate method" "true"
public class MyClass {
public void foo<caret>() {}
public void foo() {}
public void foo() {}
}
@@ -0,0 +1,7 @@
// "Navigate to duplicate method" "true"
public class MyClass {
public <T> void foo<caret>(T bar) {}
public <T> void foo(T bar) {}
public <T> void foo(T bar) {}
}
@@ -0,0 +1,7 @@
// "Navigate to duplicate method" "true"
public class MyClass {
public void foo() {}
public void foo() {}
public void foo<caret>() {}
}
@@ -0,0 +1,11 @@
// "Navigate to duplicate method" "true"
enum MyEnum {
FIRST;
public MyEnum valueOf<caret>(String s) {
return null;
}
public MyEnum valueOf(String s) {
return null;
}
}
@@ -0,0 +1,11 @@
// "Navigate to duplicate method" "true"
enum MyEnum {
FIRST;
public MyEnum[] values<caret>() {
return null;
}
public MyEnum[] values() {
return null;
}
}
@@ -0,0 +1,11 @@
// Copyright 2000-2025 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.quickFix.LightQuickFixParameterizedTestCase;
public class NavigateToDuplicateMethodFixTest extends LightQuickFixParameterizedTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix";
}
}