mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-intentions] IDEA-379168 Suggest renaming underscore variable when it's used.
GitOrigin-RevId: eb0cfd7ddf8d5d730198ea5cd064cca79724348e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
719fe48b48
commit
2bd42bb487
@@ -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
|
||||
|
||||
+6
-4
@@ -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<? super CommonIntentionAction> info,
|
||||
|
||||
+81
@@ -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<PsiReferenceExpression> {
|
||||
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<PsiVariable> 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<String> 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<PsiVariable> findDeclarations(@NotNull PsiReferenceExpression expression) {
|
||||
List<PsiVariable> result = new ArrayList<>();
|
||||
PsiScopesUtil.treeWalkUp(new PsiScopeProcessor() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> @Nullable T getHint(@NotNull Key<T> 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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
+3
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -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;
|
||||
|
||||
@@ -7,6 +7,11 @@ import org.jetbrains.annotations.NotNull;
|
||||
public interface ElementClassHint {
|
||||
Key<ElementClassHint> KEY = Key.create("ElementClassHint");
|
||||
|
||||
/**
|
||||
* If this hint is set to true, then the unnamed variables will be processed. By default, they are skipped.
|
||||
*/
|
||||
Key<Boolean> PROCESS_UNNAMED_VARIABLES = Key.create("ElementClassHint.PROCESS_UNNAMED_VARIABLES");
|
||||
|
||||
enum DeclarationKind {
|
||||
CLASS,
|
||||
PACKAGE,
|
||||
|
||||
+16
@@ -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<String, Integer> cons = (_, _) -> {
|
||||
for (int _ : new int[10]) {
|
||||
try {
|
||||
Integer.parseInt("123s");
|
||||
} catch (NumberFormatException numberFormatException) {
|
||||
System.out.println(numberFormatException);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+16
@@ -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<String, Integer> cons = (_, _) -> {
|
||||
for (int i : new int[10]) {
|
||||
try {
|
||||
Integer.parseInt("123s");
|
||||
} catch (NumberFormatException _) {
|
||||
System.out.println(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+16
@@ -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<String, Integer> cons = (s, _) -> {
|
||||
for (int _ : new int[10]) {
|
||||
try {
|
||||
Integer.parseInt("123s");
|
||||
} catch (NumberFormatException _) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Rename unnamed variable" "true-preview"
|
||||
class X {
|
||||
void test(int i) {
|
||||
System.out.println(i);
|
||||
}
|
||||
}
|
||||
+16
@@ -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<String, Integer> cons = (_, _) -> {
|
||||
for (int _ : new int[10]) {
|
||||
try {
|
||||
Integer.parseInt("123s");
|
||||
} catch (NumberFormatException _) {
|
||||
System.out.println(<caret>_);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+16
@@ -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<String, Integer> cons = (_, _) -> {
|
||||
for (int _ : new int[10]) {
|
||||
try {
|
||||
Integer.parseInt("123s");
|
||||
} catch (NumberFormatException _) {
|
||||
System.out.println(<caret>_);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+16
@@ -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<String, Integer> cons = (_, _) -> {
|
||||
for (int _ : new int[10]) {
|
||||
try {
|
||||
Integer.parseInt("123s");
|
||||
} catch (NumberFormatException _) {
|
||||
System.out.println(<caret>_);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Rename unnamed variable" "true-preview"
|
||||
class X {
|
||||
void test(int _) {
|
||||
System.out.println(<caret>_);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Rename unnamed variable" "false"
|
||||
class X {
|
||||
void test() {
|
||||
System.out.println(<caret>_);
|
||||
}
|
||||
|
||||
void other() {
|
||||
for (var _ : new int[10]) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user