diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/MethodChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/MethodChecker.java index 49a02a1c4b27..55fee98293dc 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/MethodChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/MethodChecker.java @@ -527,7 +527,7 @@ final class MethodChecker { MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY); List methods = (List)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))); } } diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java index b68af75d8d51..0e5b9aba0a89 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java @@ -640,11 +640,11 @@ public final class JavaErrorKinds { owner.getTypeParameters().length)); public static final Simple TYPE_PARAMETER_ACTUAL_INFERRED_MISMATCH = error("type.parameter.actual.inferred.mismatch"); - public static final Simple METHOD_DUPLICATE = - error(PsiMethod.class, "method.duplicate") - .withRange(JavaErrorFormatUtil::getMethodDeclarationTextRange) + public static final Parameterized 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 METHOD_NO_PARAMETER_LIST = error(PsiMethod.class, "method.no.parameter.list").withAnchor(PsiMethod::getNameIdentifier); public static final Simple 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) {} } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/DefaultJavaErrorFixProvider.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/DefaultJavaErrorFixProvider.java index bd82dc08b5b8..ee56179564b5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/DefaultJavaErrorFixProvider.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/DefaultJavaErrorFixProvider.java @@ -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() { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesFromFirstToSecond.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesFromFirstToSecond.java new file mode 100644 index 000000000000..c5a87f637460 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesFromFirstToSecond.java @@ -0,0 +1,7 @@ +// "Navigate to duplicate method" "true" + +public class MyClass { + public void foo() {} + public void foo() {} + public void foo() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesFromFirstToSecondGenericMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesFromFirstToSecondGenericMethod.java new file mode 100644 index 000000000000..e4ff84c9774d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesFromFirstToSecondGenericMethod.java @@ -0,0 +1,7 @@ +// "Navigate to duplicate method" "true" + +public class MyClass { + public void foo(T bar) {} + public void foo(T bar) {} + public void foo(T bar) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesFromThirdToFirst.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesFromThirdToFirst.java new file mode 100644 index 000000000000..dcd4b88e103d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesFromThirdToFirst.java @@ -0,0 +1,7 @@ +// "Navigate to duplicate method" "true" + +public class MyClass { + public void foo() {} + public void foo() {} + public void foo() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesToEnumValueOfWhenDeclaredTwice.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesToEnumValueOfWhenDeclaredTwice.java new file mode 100644 index 000000000000..b2b9f4c0211e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesToEnumValueOfWhenDeclaredTwice.java @@ -0,0 +1,11 @@ +// "Navigate to duplicate method" "true" + +enum MyEnum { + FIRST; + public MyEnum valueOf(String s) { + return null; + } + public MyEnum valueOf(String s) { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesToEnumValuesWhenDeclaredTwice.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesToEnumValuesWhenDeclaredTwice.java new file mode 100644 index 000000000000..b5d06b3784d5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/afterNavigatesToEnumValuesWhenDeclaredTwice.java @@ -0,0 +1,11 @@ +// "Navigate to duplicate method" "true" + +enum MyEnum { + FIRST; + public MyEnum[] values() { + return null; + } + public MyEnum[] values() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeDoesntNavigateToEnumValueOf.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeDoesntNavigateToEnumValueOf.java new file mode 100644 index 000000000000..76b0074260f6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeDoesntNavigateToEnumValueOf.java @@ -0,0 +1,8 @@ +// "Navigate to duplicate method" "false" + +enum MyEnum { + FIRST; + public MyEnum valueOf(String s) { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeDoesntNavigateToEnumValues.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeDoesntNavigateToEnumValues.java new file mode 100644 index 000000000000..0db2a6210b97 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeDoesntNavigateToEnumValues.java @@ -0,0 +1,8 @@ +// "Navigate to duplicate method" "false" + +enum MyEnum { + FIRST; + public MyEnum[] values() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesFromFirstToSecond.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesFromFirstToSecond.java new file mode 100644 index 000000000000..d13831c2f596 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesFromFirstToSecond.java @@ -0,0 +1,7 @@ +// "Navigate to duplicate method" "true" + +public class MyClass { + public void foo() {} + public void foo() {} + public void foo() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesFromFirstToSecondGenericMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesFromFirstToSecondGenericMethod.java new file mode 100644 index 000000000000..bfd5c39aeda9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesFromFirstToSecondGenericMethod.java @@ -0,0 +1,7 @@ +// "Navigate to duplicate method" "true" + +public class MyClass { + public void foo(T bar) {} + public void foo(T bar) {} + public void foo(T bar) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesFromThirdToFirst.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesFromThirdToFirst.java new file mode 100644 index 000000000000..88d43abf483b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesFromThirdToFirst.java @@ -0,0 +1,7 @@ +// "Navigate to duplicate method" "true" + +public class MyClass { + public void foo() {} + public void foo() {} + public void foo() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesToEnumValueOfWhenDeclaredTwice.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesToEnumValueOfWhenDeclaredTwice.java new file mode 100644 index 000000000000..a3751a12b8e6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesToEnumValueOfWhenDeclaredTwice.java @@ -0,0 +1,11 @@ +// "Navigate to duplicate method" "true" + +enum MyEnum { + FIRST; + public MyEnum valueOf(String s) { + return null; + } + public MyEnum valueOf(String s) { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesToEnumValuesWhenDeclaredTwice.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesToEnumValuesWhenDeclaredTwice.java new file mode 100644 index 000000000000..92faa00fba87 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/navigateToDuplicateMethodFix/beforeNavigatesToEnumValuesWhenDeclaredTwice.java @@ -0,0 +1,11 @@ +// "Navigate to duplicate method" "true" + +enum MyEnum { + FIRST; + public MyEnum[] values() { + return null; + } + public MyEnum[] values() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateMethodFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateMethodFixTest.java new file mode 100644 index 000000000000..3aeeb98d42c1 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateMethodFixTest.java @@ -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"; + } +}