From 2bd42bb48766b71e85ffd48b8dc7b470c199b446 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 11 Sep 2025 16:05:36 +0200 Subject: [PATCH] [java-intentions] IDEA-379168 Suggest renaming underscore variable when it's used. GitOrigin-RevId: eb0cfd7ddf8d5d730198ea5cd064cca79724348e --- .../messages/JavaPsiBundle.properties | 3 + .../AdditionalJavaErrorFixProvider.java | 10 ++- .../impl/quickfix/RenameUnderscoreFix.java | 81 +++++++++++++++++++ .../intellij/psi/util/JavaElementKind.java | 17 ++++ .../com/intellij/psi/impl/PsiImplUtil.java | 8 +- .../java/PsiDeclarationStatementImpl.java | 3 +- .../tree/java/PsiForeachStatementImpl.java | 4 +- .../tree/java/PsiTypeTestPatternImpl.java | 4 +- .../intellij/psi/scope/ElementClassHint.java | 5 ++ .../quickFix/renameUnderscore/afterCatch.java | 16 ++++ .../renameUnderscore/afterForEach.java | 16 ++++ .../renameUnderscore/afterLambdaParam.java | 16 ++++ .../afterMethodParameter.java | 6 ++ .../renameUnderscore/beforeCatch.java | 16 ++++ .../renameUnderscore/beforeForEach.java | 16 ++++ .../renameUnderscore/beforeLambdaParam.java | 16 ++++ .../beforeMethodParameter.java | 6 ++ .../renameUnderscore/beforeNoDef.java | 12 +++ .../impl/quickfix/RenameUnderscoreTest.java | 11 +++ .../resources/messages/JavaBundle.properties | 3 + 20 files changed, 260 insertions(+), 9 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RenameUnderscoreFix.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterCatch.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterForEach.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterLambdaParam.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterMethodParameter.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeCatch.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeForEach.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeLambdaParam.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeMethodParameter.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeNoDef.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RenameUnderscoreTest.java diff --git a/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties b/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties index 65c608e9f06b..be03c1803e3b 100644 --- a/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties +++ b/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties @@ -21,6 +21,7 @@ default.language.level.description=SDK default element.abstract_method=abstract method element.annotation=annotation element.anonymous_class=anonymous class +element.catch_parameter=catch parameter element.class=class element.type.parameter=type parameter element.type.parameters=type parameters @@ -30,9 +31,11 @@ element.enum=enum element.enum_constant=enum constant element.expression=expression element.field=field +element.for_parameter='for' loop parameter element.initializer=initializer element.interface=interface element.label=label +element.lambda_parameter=lambda parameter element.local_variable=local variable element.method=method element.module=module diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/AdditionalJavaErrorFixProvider.java b/java/java-impl-inspections/src/com/intellij/codeInspection/AdditionalJavaErrorFixProvider.java index ae20970e3440..8ffe60e671c1 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/AdditionalJavaErrorFixProvider.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/AdditionalJavaErrorFixProvider.java @@ -3,10 +3,7 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.daemon.impl.analysis.AbstractJavaErrorFixProvider; import com.intellij.codeInsight.daemon.impl.analysis.HighlightFixUtil; -import com.intellij.codeInsight.daemon.impl.quickfix.AddExceptionToCatchFix; -import com.intellij.codeInsight.daemon.impl.quickfix.AddFinallyFix; -import com.intellij.codeInsight.daemon.impl.quickfix.InsertMissingTokenFix; -import com.intellij.codeInsight.daemon.impl.quickfix.VariableAccessFromInnerClassJava10Fix; +import com.intellij.codeInsight.daemon.impl.quickfix.*; import com.intellij.codeInsight.intention.CommonIntentionAction; import com.intellij.codeInspection.streamMigration.SimplifyForEachInspection; import com.intellij.core.JavaPsiBundle; @@ -17,6 +14,8 @@ import org.jetbrains.annotations.NotNull; import java.util.function.Consumer; +import static com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds.UNDERSCORE_IDENTIFIER_UNNAMED; + /** * Some quick-fixes not accessible from the java.analysis module are registered here. */ @@ -26,6 +25,9 @@ public final class AdditionalJavaErrorFixProvider extends AbstractJavaErrorFixPr fix(JavaErrorKinds.VARIABLE_MUST_BE_EFFECTIVELY_FINAL_LAMBDA, error -> new VariableAccessFromInnerClassJava10Fix(error.psi())); fix(JavaErrorKinds.VARIABLE_MUST_BE_EFFECTIVELY_FINAL_GUARD, error -> new VariableAccessFromInnerClassJava10Fix(error.psi())); fixes(JavaErrorKinds.SYNTAX_ERROR, (error, info) -> registerErrorElementFixes(info, error.psi())); + fix(UNDERSCORE_IDENTIFIER_UNNAMED, error -> error.psi().getParent() instanceof PsiReferenceExpression ref && + "_".equals(ref.getReferenceName()) ? + new RenameUnderscoreFix(ref) : null); } private static void registerErrorElementFixes(@NotNull Consumer info, diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RenameUnderscoreFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RenameUnderscoreFix.java new file mode 100644 index 000000000000..0909793f937c --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RenameUnderscoreFix.java @@ -0,0 +1,81 @@ +// 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.java.JavaBundle; +import com.intellij.modcommand.*; +import com.intellij.openapi.util.Key; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.VariableKind; +import com.intellij.psi.scope.ElementClassHint; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.scope.util.PsiScopesUtil; +import com.intellij.psi.util.JavaElementKind; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.psiutils.VariableNameGenerator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public final class RenameUnderscoreFix extends PsiBasedModCommandAction { + public RenameUnderscoreFix(@NotNull PsiReferenceExpression element) { + super(element); + } + + @Override + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiReferenceExpression element) { + if (findDeclarations(element).isEmpty()) return null; + return super.getPresentation(context, element); + } + + @Override + protected @NotNull ModCommand perform(@NotNull ActionContext context, @NotNull PsiReferenceExpression element) { + List declarations = findDeclarations(element); + List<@NotNull ModCommandAction> actions = ContainerUtil.map(declarations, var -> ModCommand.psiUpdateStep( + var, JavaBundle.message("intention.rename.underscore.name", JavaElementKind.fromElement(var).subject(), var.getType().getPresentableText()), (v, updater) -> { + if (!element.isValid()) return; + PsiReferenceExpression writableRef = updater.getWritable(element); + List names = new VariableNameGenerator(v, VariableKind.LOCAL_VARIABLE) + .byType(v.getType()) + .generateAll(true); + String defaultName = names.getFirst(); + v.setName(defaultName); + writableRef.replace(JavaPsiFacade.getElementFactory(context.project()).createExpressionFromText(defaultName, writableRef)); + updater.rename(v, names); + }, v -> v.getTextRange())); + return ModCommand.chooseAction(JavaBundle.message("intention.rename.underscore.popup.title"), actions); + } + + @Override + public @NotNull String getFamilyName() { + return JavaBundle.message("intention.rename.underscore.family.name"); + } + + public static List findDeclarations(@NotNull PsiReferenceExpression expression) { + List result = new ArrayList<>(); + PsiScopesUtil.treeWalkUp(new PsiScopeProcessor() { + @SuppressWarnings("unchecked") + @Override + public @Nullable T getHint(@NotNull Key hintKey) { + if (hintKey == ElementClassHint.KEY) { + return (T)(ElementClassHint)(ElementClassHint.DeclarationKind.VARIABLE::equals); + } + if (hintKey == ElementClassHint.PROCESS_UNNAMED_VARIABLES) { + return (T)Boolean.TRUE; + } + return null; + } + + @Override + public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) { + if (element instanceof PsiVariable variable && PsiUtil.isJvmLocalVariable(variable) && variable.isUnnamed()) { + result.add(variable); + } + return true; + } + }, expression, expression.getContainingFile()); + return result; + } +} diff --git a/java/java-psi-api/src/com/intellij/psi/util/JavaElementKind.java b/java/java-psi-api/src/com/intellij/psi/util/JavaElementKind.java index f76f348ad2a6..4d5b56504fa0 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/JavaElementKind.java +++ b/java/java-psi-api/src/com/intellij/psi/util/JavaElementKind.java @@ -16,6 +16,7 @@ public enum JavaElementKind { ABSTRACT_METHOD("element.abstract_method"), ANNOTATION("element.annotation"), ANONYMOUS_CLASS("element.anonymous_class"), + CATCH_PARAMETER("element.catch_parameter"), CLASS("element.class"), CONSTANT("element.constant"), CONSTRUCTOR("element.constructor"), @@ -24,9 +25,11 @@ public enum JavaElementKind { EXPRESSION("element.expression"), EXTENDS_LIST("element.extends.list"), FIELD("element.field"), + FOR_PARAMETER("element.for_parameter"), INITIALIZER("element.initializer"), INTERFACE("element.interface"), LABEL("element.label"), + LAMBDA_PARAMETER("element.lambda_parameter"), LOCAL_VARIABLE("element.local_variable"), METHOD("element.method"), METHOD_CALL("element.method.call"), @@ -86,6 +89,10 @@ public enum JavaElementKind { case TYPE_PARAMETER: case ANONYMOUS_CLASS: return CLASS; + case LAMBDA_PARAMETER: + case CATCH_PARAMETER: + case FOR_PARAMETER: + return PARAMETER; default: return this; } @@ -171,6 +178,16 @@ public enum JavaElementKind { return PATTERN_VARIABLE; } if (element instanceof PsiParameter) { + PsiElement scope = ((PsiParameter)element).getDeclarationScope(); + if (scope instanceof PsiForeachStatement) { + return FOR_PARAMETER; + } + if (scope instanceof PsiLambdaExpression) { + return LAMBDA_PARAMETER; + } + if (scope instanceof PsiCatchSection) { + return CATCH_PARAMETER; + } return PARAMETER; } if (element instanceof PsiReceiverParameter) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java index 9825174a5ad1..414814a230d1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java @@ -204,7 +204,10 @@ public final class PsiImplUtil { if (fromBody) { final PsiParameter[] parameters = element.getParameterList().getParameters(); for (PsiParameter parameter : parameters) { - if (parameter.isUnnamed()) continue; + if (parameter.isUnnamed() && + !Boolean.TRUE.equals(processor.getHint(ElementClassHint.PROCESS_UNNAMED_VARIABLES))) { + continue; + } if (!processor.execute(parameter, state)) return false; } } @@ -222,7 +225,8 @@ public final class PsiImplUtil { for (PsiResourceListElement resource : resourceList) { if (resource == lastParent) break; if (resource instanceof PsiResourceVariable && - !((PsiResourceVariable)resource).isUnnamed() && + !(((PsiResourceVariable)resource).isUnnamed() && + !Boolean.TRUE.equals(processor.getHint(ElementClassHint.PROCESS_UNNAMED_VARIABLES))) && !processor.execute(resource, state)) return false; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiDeclarationStatementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiDeclarationStatementImpl.java index 6d829c233efd..0459f314c19f 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiDeclarationStatementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiDeclarationStatementImpl.java @@ -119,7 +119,8 @@ public class PsiDeclarationStatementImpl extends CompositePsiElement implements for (PsiElement element : getDeclaredElements()) { if (element != lastParent) { - if (element instanceof PsiVariable && ((PsiVariable)element).isUnnamed()) continue; + if (element instanceof PsiVariable && ((PsiVariable)element).isUnnamed() && + !Boolean.TRUE.equals(processor.getHint(ElementClassHint.PROCESS_UNNAMED_VARIABLES))) continue; if (!processor.execute(element, state)) return false; } else { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiForeachStatementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiForeachStatementImpl.java index 070564f9b285..3b9eacd5f9d5 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiForeachStatementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiForeachStatementImpl.java @@ -4,6 +4,7 @@ package com.intellij.psi.impl.source.tree.java; import com.intellij.psi.*; import com.intellij.psi.impl.source.Constants; import com.intellij.psi.impl.source.tree.ChildRole; +import com.intellij.psi.scope.ElementClassHint; import com.intellij.psi.scope.PsiScopeProcessor; import org.jetbrains.annotations.NotNull; @@ -32,7 +33,8 @@ public class PsiForeachStatementImpl extends PsiForeachStatementBaseImpl impleme return true; PsiParameter parameter = getIterationParameter(); - if (parameter.isUnnamed()) return true; + if (parameter.isUnnamed() && + !Boolean.TRUE.equals(processor.getHint(ElementClassHint.PROCESS_UNNAMED_VARIABLES))) return true; return processor.execute(parameter, state); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiTypeTestPatternImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiTypeTestPatternImpl.java index 24afd636f7d2..e794bb7e88a8 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiTypeTestPatternImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiTypeTestPatternImpl.java @@ -4,6 +4,7 @@ package com.intellij.psi.impl.source.tree.java; import com.intellij.psi.*; import com.intellij.psi.impl.source.Constants; import com.intellij.psi.impl.source.tree.CompositePsiElement; +import com.intellij.psi.scope.ElementClassHint; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; @@ -46,7 +47,8 @@ public class PsiTypeTestPatternImpl extends CompositePsiElement implements PsiTy processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this); PsiPatternVariable variable = getPatternVariable(); - if (variable != null && variable != lastParent && !variable.isUnnamed()) { + if (variable != null && variable != lastParent && + !(variable.isUnnamed() && !Boolean.TRUE.equals(processor.getHint(ElementClassHint.PROCESS_UNNAMED_VARIABLES)))) { return processor.execute(variable, state); } return true; diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/ElementClassHint.java b/java/java-psi-impl/src/com/intellij/psi/scope/ElementClassHint.java index 2ac4061f6251..c495514fd71f 100644 --- a/java/java-psi-impl/src/com/intellij/psi/scope/ElementClassHint.java +++ b/java/java-psi-impl/src/com/intellij/psi/scope/ElementClassHint.java @@ -7,6 +7,11 @@ import org.jetbrains.annotations.NotNull; public interface ElementClassHint { Key KEY = Key.create("ElementClassHint"); + /** + * If this hint is set to true, then the unnamed variables will be processed. By default, they are skipped. + */ + Key PROCESS_UNNAMED_VARIABLES = Key.create("ElementClassHint.PROCESS_UNNAMED_VARIABLES"); + enum DeclarationKind { CLASS, PACKAGE, diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterCatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterCatch.java new file mode 100644 index 000000000000..a6676ad91d90 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterCatch.java @@ -0,0 +1,16 @@ +// "Rename unnamed variable|->Rename catch parameter of type NumberFormatException" "true-preview" +import java.util.function.BiConsumer; + +public class JavaTest { + public static void main(String[] args) { + BiConsumer cons = (_, _) -> { + for (int _ : new int[10]) { + try { + Integer.parseInt("123s"); + } catch (NumberFormatException numberFormatException) { + System.out.println(numberFormatException); + } + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterForEach.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterForEach.java new file mode 100644 index 000000000000..c195ad459c07 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterForEach.java @@ -0,0 +1,16 @@ +// "Rename unnamed variable|->Rename 'for' loop parameter of type int" "true-preview" +import java.util.function.BiConsumer; + +public class JavaTest { + public static void main(String[] args) { + BiConsumer cons = (_, _) -> { + for (int i : new int[10]) { + try { + Integer.parseInt("123s"); + } catch (NumberFormatException _) { + System.out.println(i); + } + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterLambdaParam.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterLambdaParam.java new file mode 100644 index 000000000000..04a94f7f6d28 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterLambdaParam.java @@ -0,0 +1,16 @@ +// "Rename unnamed variable|->Rename lambda parameter of type String" "true-preview" +import java.util.function.BiConsumer; + +public class JavaTest { + public static void main(String[] args) { + BiConsumer cons = (s, _) -> { + for (int _ : new int[10]) { + try { + Integer.parseInt("123s"); + } catch (NumberFormatException _) { + System.out.println(s); + } + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterMethodParameter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterMethodParameter.java new file mode 100644 index 000000000000..5e46639a59e5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/afterMethodParameter.java @@ -0,0 +1,6 @@ +// "Rename unnamed variable" "true-preview" +class X { + void test(int i) { + System.out.println(i); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeCatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeCatch.java new file mode 100644 index 000000000000..24efdde1d492 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeCatch.java @@ -0,0 +1,16 @@ +// "Rename unnamed variable|->Rename catch parameter of type NumberFormatException" "true-preview" +import java.util.function.BiConsumer; + +public class JavaTest { + public static void main(String[] args) { + BiConsumer cons = (_, _) -> { + for (int _ : new int[10]) { + try { + Integer.parseInt("123s"); + } catch (NumberFormatException _) { + System.out.println(_); + } + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeForEach.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeForEach.java new file mode 100644 index 000000000000..8ab1645d1575 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeForEach.java @@ -0,0 +1,16 @@ +// "Rename unnamed variable|->Rename 'for' loop parameter of type int" "true-preview" +import java.util.function.BiConsumer; + +public class JavaTest { + public static void main(String[] args) { + BiConsumer cons = (_, _) -> { + for (int _ : new int[10]) { + try { + Integer.parseInt("123s"); + } catch (NumberFormatException _) { + System.out.println(_); + } + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeLambdaParam.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeLambdaParam.java new file mode 100644 index 000000000000..a833a26e5bc3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeLambdaParam.java @@ -0,0 +1,16 @@ +// "Rename unnamed variable|->Rename lambda parameter of type String" "true-preview" +import java.util.function.BiConsumer; + +public class JavaTest { + public static void main(String[] args) { + BiConsumer cons = (_, _) -> { + for (int _ : new int[10]) { + try { + Integer.parseInt("123s"); + } catch (NumberFormatException _) { + System.out.println(_); + } + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeMethodParameter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeMethodParameter.java new file mode 100644 index 000000000000..a1d8aa08b40a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeMethodParameter.java @@ -0,0 +1,6 @@ +// "Rename unnamed variable" "true-preview" +class X { + void test(int _) { + System.out.println(_); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeNoDef.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeNoDef.java new file mode 100644 index 000000000000..3c5337bd34fd --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore/beforeNoDef.java @@ -0,0 +1,12 @@ +// "Rename unnamed variable" "false" +class X { + void test() { + System.out.println(_); + } + + void other() { + for (var _ : new int[10]) { + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RenameUnderscoreTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RenameUnderscoreTest.java new file mode 100644 index 000000000000..514e568dcc67 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RenameUnderscoreTest.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 RenameUnderscoreTest extends LightQuickFixParameterizedTestCase { + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/renameUnderscore"; + } +} \ No newline at end of file diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties index f06531fb1719..f03e6f617186 100644 --- a/java/openapi/resources/messages/JavaBundle.properties +++ b/java/openapi/resources/messages/JavaBundle.properties @@ -1987,3 +1987,6 @@ preview.api.usage={0} is a preview API and may be removed in a future release preview.api.usage.reflective={0} is a reflective preview API and may be removed in a future release todo.0=todo "{0}" todo.item=todo item +intention.rename.underscore.name=Rename {0} of type {1} +intention.rename.underscore.popup.title=Select Declaration +intention.rename.underscore.family.name=Rename unnamed variable