mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 04:51:24 +07:00
[java] Unnamed variables
IDEA-323910 Implement parser for "JEP 443: Unnamed Patterns and Variables (Preview)" IDEA-323960 Support error highlighting for unnamed variables (JEP 443) GitOrigin-RevId: 1b9ee424063dfd4d32c2215fc8b0a9838dbdcd95
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9fb2a08303
commit
a41ef84fea
@@ -648,3 +648,7 @@ notification.file.system.issue=File Operation Issue
|
||||
notification.content.cannot.move.file=Cannot move ''{0}'' into ''{1}'': {2}
|
||||
intention.family.name.replace.with.unnamed.pattern=Replace with unnamed pattern
|
||||
intention.name.ignore.exception=Ignore exception ''{0}''
|
||||
error.unnamed.variable.not.allowed=Unnamed variable is not allowed
|
||||
error.unnamed.field.not.allowed=Unnamed field is not allowed
|
||||
error.unnamed.method.parameter.not.allowed=Unnamed method parameter is not allowed
|
||||
error.unnamed.local.variable.not.allowed.in.this.context=Unnamed local variable is not allowed in this context
|
||||
|
||||
@@ -699,7 +699,7 @@ public final class HighlightUtil {
|
||||
}
|
||||
|
||||
public static HighlightInfo.Builder checkVariableAlreadyDefined(@NotNull PsiVariable variable) {
|
||||
if (variable instanceof ExternallyDefinedPsiElement) return null;
|
||||
if (variable instanceof ExternallyDefinedPsiElement || variable.isUnnamed()) return null;
|
||||
PsiVariable oldVariable = null;
|
||||
PsiElement declarationScope = null;
|
||||
if (variable instanceof PsiLocalVariable || variable instanceof PsiPatternVariable ||
|
||||
@@ -838,12 +838,14 @@ public final class HighlightUtil {
|
||||
static HighlightInfo.Builder checkUnderscore(@NotNull PsiIdentifier identifier, @NotNull LanguageLevel languageLevel) {
|
||||
if ("_".equals(identifier.getText())) {
|
||||
PsiElement parent = identifier.getParent();
|
||||
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_9) && !(parent instanceof PsiUnnamedPattern)) {
|
||||
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_9) && !(parent instanceof PsiUnnamedPattern) &&
|
||||
!(parent instanceof PsiVariable var && var.isUnnamed())) {
|
||||
String text = JavaErrorBundle.message("underscore.identifier.error");
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(identifier).descriptionAndTooltip(text);
|
||||
}
|
||||
else if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
|
||||
if (parent instanceof PsiParameter parameter && parameter.getDeclarationScope() instanceof PsiLambdaExpression) {
|
||||
if (parent instanceof PsiParameter parameter && parameter.getDeclarationScope() instanceof PsiLambdaExpression &&
|
||||
!parameter.isUnnamed()) {
|
||||
String text = JavaErrorBundle.message("underscore.lambda.identifier");
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(identifier).descriptionAndTooltip(text);
|
||||
}
|
||||
@@ -853,6 +855,29 @@ public final class HighlightUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkAllowedUnnamedLocation(@NotNull PsiVariable variable) {
|
||||
if (variable instanceof PsiPatternVariable) return null;
|
||||
if (variable instanceof PsiResourceVariable) return null;
|
||||
String message;
|
||||
if (variable instanceof PsiLocalVariable local) {
|
||||
if (local.getParent() instanceof PsiDeclarationStatement decl && decl.getParent() instanceof PsiCodeBlock) return null;
|
||||
message = JavaAnalysisBundle.message("error.unnamed.local.variable.not.allowed.in.this.context");
|
||||
}
|
||||
else if (variable instanceof PsiParameter parameter) {
|
||||
PsiElement scope = parameter.getDeclarationScope();
|
||||
if (!(scope instanceof PsiMethod)) return null;
|
||||
message = JavaAnalysisBundle.message("error.unnamed.method.parameter.not.allowed");
|
||||
}
|
||||
else if (variable instanceof PsiField) {
|
||||
message = JavaAnalysisBundle.message("error.unnamed.field.not.allowed");
|
||||
}
|
||||
else {
|
||||
message = JavaAnalysisBundle.message("error.unnamed.variable.not.allowed");
|
||||
}
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(Objects.requireNonNull(variable.getNameIdentifier()))
|
||||
.descriptionAndTooltip(message);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static @NlsSafe String formatClass(@NotNull PsiClass aClass) {
|
||||
return formatClass(aClass, true);
|
||||
|
||||
@@ -775,6 +775,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
PsiElement parent = identifier.getParent();
|
||||
if (parent instanceof PsiVariable variable) {
|
||||
add(HighlightUtil.checkVariableAlreadyDefined(variable));
|
||||
if (variable.isUnnamed()) {
|
||||
HighlightInfo.Builder notAvailable = checkFeature(variable, HighlightingFeature.UNNAMED_PATTERNS_AND_VARIABLES);
|
||||
if (notAvailable != null) {
|
||||
add(notAvailable);
|
||||
} else {
|
||||
add(HighlightUtil.checkAllowedUnnamedLocation(variable));
|
||||
}
|
||||
}
|
||||
|
||||
if (variable.getInitializer() == null) {
|
||||
PsiElement child = variable.getLastChild();
|
||||
|
||||
@@ -222,7 +222,7 @@ class PostHighlightingVisitor {
|
||||
|
||||
private HighlightInfo.Builder processLocalVariable(@NotNull PsiLocalVariable variable,
|
||||
@NotNull PsiIdentifier identifier) {
|
||||
if (PsiUtil.isIgnoredName(variable.getName())) return null;
|
||||
if (variable.isUnnamed() || PsiUtil.isIgnoredName(variable.getName())) return null;
|
||||
if (UnusedSymbolUtil.isImplicitUsage(myProject, variable)) return null;
|
||||
|
||||
String message = null;
|
||||
@@ -366,7 +366,7 @@ class PostHighlightingVisitor {
|
||||
private HighlightInfo.Builder processParameter(@NotNull Project project,
|
||||
@NotNull PsiParameter parameter,
|
||||
@NotNull PsiIdentifier identifier) {
|
||||
if (PsiUtil.isIgnoredName(parameter.getName())) return null;
|
||||
if (parameter.isUnnamed() || PsiUtil.isIgnoredName(parameter.getName())) return null;
|
||||
PsiElement declarationScope = parameter.getDeclarationScope();
|
||||
QuickFixFactory quickFixFactory = QuickFixFactory.getInstance();
|
||||
if (declarationScope instanceof PsiMethod method) {
|
||||
@@ -426,7 +426,9 @@ class PostHighlightingVisitor {
|
||||
return highlightInfo;
|
||||
}
|
||||
}
|
||||
else if (myUnusedSymbolInspection.checkParameterExcludingHierarchy() && declarationScope instanceof PsiLambdaExpression) {
|
||||
else if ((myUnusedSymbolInspection.checkParameterExcludingHierarchy() ||
|
||||
HighlightingFeature.UNNAMED_PATTERNS_AND_VARIABLES.isAvailable(declarationScope))
|
||||
&& declarationScope instanceof PsiLambdaExpression) {
|
||||
HighlightInfo.Builder highlightInfo = checkUnusedParameter(parameter, identifier, null);
|
||||
if (highlightInfo != null) {
|
||||
IntentionAction action1 = quickFixFactory.createRenameToIgnoredFix(parameter, true);
|
||||
|
||||
@@ -15,29 +15,54 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.psiutils.VariableAccessUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class RenameToIgnoredFix extends RenameElementFix {
|
||||
private static final String PREFIX = "ignored";
|
||||
|
||||
private RenameToIgnoredFix(@NotNull PsiNamedElement place, @NotNull String suffix) {
|
||||
super(place, JavaCodeStyleManager.getInstance(place.getProject()).suggestUniqueVariableName(PREFIX + suffix, place, true));
|
||||
private RenameToIgnoredFix(@NotNull PsiNamedElement place, @NotNull String name) {
|
||||
super(place, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param useElementNameAsSuffix if true, let the fix suggest the variable name that consists of the "ignored" and name of the element
|
||||
* e.g. ignoredVar
|
||||
* <p>if false, let the fix suggest the variable that consists of the "ignored" and some number
|
||||
* e.g. ignored1
|
||||
* e.g. ignored1.
|
||||
* If variable can be unnamed (place and language level allows, and it's totally unused), renaming to unnamed
|
||||
* will be suggested instead.
|
||||
*/
|
||||
public static RenameToIgnoredFix createRenameToIgnoreFix(@NotNull PsiNamedElement element, boolean useElementNameAsSuffix) {
|
||||
if (element instanceof PsiVariable variable && canBeUnnamed(variable)
|
||||
&& !VariableAccessUtils.variableIsUsed(variable, PsiUtil.getVariableCodeBlock(variable, null))) {
|
||||
return new RenameToIgnoredFix(variable, "_");
|
||||
}
|
||||
String baseName = "";
|
||||
if (useElementNameAsSuffix) {
|
||||
String elementName = element.getName();
|
||||
if (elementName != null) return new RenameToIgnoredFix(element, StringUtil.capitalize(elementName));
|
||||
if (elementName != null) {
|
||||
baseName = StringUtil.capitalize(elementName);
|
||||
}
|
||||
}
|
||||
return new RenameToIgnoredFix(element, "");
|
||||
return new RenameToIgnoredFix(element, JavaCodeStyleManager.getInstance(element.getProject())
|
||||
.suggestUniqueVariableName(PREFIX + baseName, element, true));
|
||||
}
|
||||
|
||||
private static boolean canBeUnnamed(PsiVariable variable) {
|
||||
if (!HighlightingFeature.UNNAMED_PATTERNS_AND_VARIABLES.isAvailable(variable)) return false;
|
||||
if (variable instanceof PsiPatternVariable || variable instanceof PsiResourceVariable) return true;
|
||||
if (variable instanceof PsiLocalVariable) {
|
||||
return variable.getParent() instanceof PsiDeclarationStatement decl && decl.getParent() instanceof PsiCodeBlock;
|
||||
}
|
||||
if (variable instanceof PsiParameter parameter) {
|
||||
return !(parameter.getDeclarationScope() instanceof PsiMethod);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ public final class UnusedDeclarationInspection extends UnusedDeclarationInspecti
|
||||
for (DefUseUtil.Info varDefInfo : unusedDefs) {
|
||||
PsiElement parent = varDefInfo.getContext();
|
||||
PsiVariable variable = varDefInfo.getVariable();
|
||||
if (PsiUtil.isIgnoredName(variable.getName())) continue;
|
||||
if (variable.isUnnamed() || PsiUtil.isIgnoredName(variable.getName())) continue;
|
||||
if (parent instanceof PsiDeclarationStatement || parent instanceof PsiForeachStatement ||
|
||||
variable instanceof PsiResourceVariable || variable instanceof PsiPatternVariable) {
|
||||
if (!varDefInfo.isRead() && !SuppressionUtil.inspectionResultSuppressed(variable, UnusedDeclarationInspection.this)) {
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
package com.intellij.psi;
|
||||
|
||||
import com.intellij.pom.PomRenameableTarget;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
/**
|
||||
* Represents a Java local variable, method parameter or field.
|
||||
@@ -86,4 +89,16 @@ public interface PsiVariable extends PsiModifierListOwner, PsiNameIdentifierOwne
|
||||
|
||||
@Override
|
||||
PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException;
|
||||
|
||||
/**
|
||||
* @return true if the variable is an unnamed variable, according to the Java specification
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
default boolean isUnnamed() {
|
||||
return "_".equals(getName()) &&
|
||||
!(this instanceof PsiCompiledElement) &&
|
||||
// Treat _ as unsupported unnamed variable since JDK 1.9,
|
||||
// so we can get a proper suggestion to update language level
|
||||
PsiUtil.getLanguageLevel(this).isAtLeast(LanguageLevel.JDK_1_9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,6 +1014,7 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
}
|
||||
|
||||
public static boolean checkName(@NotNull PsiElement element, @NotNull String name, @NotNull PsiElement context) {
|
||||
if (element instanceof PsiVariable && ((PsiVariable)element).isUnnamed()) return false;
|
||||
if (element instanceof PsiMetaOwner) {
|
||||
PsiMetaData data = ((PsiMetaOwner) element).getMetaData();
|
||||
if (data != null) {
|
||||
|
||||
@@ -274,6 +274,9 @@ public class PsiParameterImpl extends JavaStubPsiElement<PsiParameterStub> imple
|
||||
|
||||
@Override
|
||||
public @NotNull SearchScope getUseScope() {
|
||||
if (isUnnamed()) {
|
||||
return LocalSearchScope.EMPTY;
|
||||
}
|
||||
PsiElement declarationScope = getDeclarationScope();
|
||||
return new LocalSearchScope(declarationScope);
|
||||
}
|
||||
|
||||
@@ -305,6 +305,9 @@ public class PsiLocalVariableImpl extends CompositePsiElement implements PsiLoca
|
||||
|
||||
@Override
|
||||
public @NotNull SearchScope getUseScope() {
|
||||
if (isUnnamed()) {
|
||||
return LocalSearchScope.EMPTY;
|
||||
}
|
||||
final PsiElement parentElement = getParent();
|
||||
if (parentElement instanceof PsiDeclarationStatement) {
|
||||
return new LocalSearchScope(parentElement.getParent());
|
||||
|
||||
@@ -415,7 +415,7 @@ cannot.resolve.package=Cannot resolve package {0}
|
||||
override.not.allowed.in.interfaces=@Override is not allowed when implementing interface method
|
||||
declaration.not.allowed=Declaration not allowed here
|
||||
|
||||
underscore.identifier.error=As of Java 9, '_' is a keyword, and may not be used as an identifier
|
||||
underscore.identifier.error=Since Java 9, '_' is a keyword, and may not be used as an identifier
|
||||
underscore.lambda.identifier=Use of '_' as a lambda parameter name is not allowed
|
||||
|
||||
assert.identifier.warn=Use of 'assert' as an identifier is not supported in releases since Java 1.4
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
class Undescore {
|
||||
void <error descr="As of Java 9, '_' is a keyword, and may not be used as an identifier">_</error>() { }
|
||||
void <error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>() { }
|
||||
}
|
||||
@@ -2,7 +2,7 @@ public class UnnamedPatterns {
|
||||
record R(int a, int b) {}
|
||||
|
||||
void test(Object obj) {
|
||||
if (obj instanceof <error descr="As of Java 9, '_' is a keyword, and may not be used as an identifier">_</error>) {}
|
||||
if (obj instanceof <error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>) {}
|
||||
|
||||
if (obj instanceof R(_, _)) {}
|
||||
if (obj instanceof R(int a, _)) {
|
||||
|
||||
@@ -2,7 +2,7 @@ public class UnnamedPatterns {
|
||||
record R(int a, int b) {}
|
||||
|
||||
void test(Object obj) {
|
||||
if (obj instanceof <error descr="As of Java 9, '_' is a keyword, and may not be used as an identifier">_</error>) {}
|
||||
if (obj instanceof <error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>) {}
|
||||
|
||||
if (obj instanceof R(<error descr="Unnamed patterns and variables are not supported at language level '20'">_</error>, <error descr="Unnamed patterns and variables are not supported at language level '20'">_</error>)) {}
|
||||
if (obj instanceof R(int a, <error descr="Unnamed patterns and variables are not supported at language level '20'">_</error>)) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import java.util.function.*;
|
||||
|
||||
public class UnnamedVariables {
|
||||
void testParameter(int <error descr="Unnamed method parameter is not allowed">_</error>, String <error descr="Unnamed method parameter is not allowed">_</error>) {
|
||||
System.out.println(<error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>);
|
||||
}
|
||||
|
||||
int <error descr="Unnamed field is not allowed">_</error> = 123;
|
||||
String s = <error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>;
|
||||
|
||||
void testLambda() {
|
||||
Consumer<String> consumer = _ -> System.out.println("Hello");
|
||||
Consumer<String> consumer2 = _ -> System.out.println(<error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>);
|
||||
Consumer<String> consumer3 = _ -> System.out.println(<error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>.trim());
|
||||
Consumer<String> consumer4 = _ -> {
|
||||
var v = <error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>;
|
||||
System.out.println(v.<error descr="Cannot resolve method 'trim()'">trim</error>());
|
||||
};
|
||||
BiConsumer<String, String> consumer5 = (_,_) -> {};
|
||||
}
|
||||
|
||||
void testLocal() {
|
||||
int _ = 10;
|
||||
int _ = 20;
|
||||
for (int <error descr="Unnamed local variable is not allowed in this context">_</error> = 1;;) {}
|
||||
}
|
||||
|
||||
void testCatch() {
|
||||
try {
|
||||
System.out.println();
|
||||
}
|
||||
catch (Exception _) {
|
||||
System.out.println("ignore");
|
||||
}
|
||||
catch (Error _) {
|
||||
int _ = 1;
|
||||
for(int _:new int[10]) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import java.util.function.*;
|
||||
|
||||
public class UnnamedVariables {
|
||||
void testParameter(int <error descr="Variable '_' is already defined in the scope">_</error>, String <error descr="Variable '_' is already defined in the scope">_</error>) {
|
||||
System.out.println(_);
|
||||
}
|
||||
|
||||
void testOneParam(String _) {
|
||||
System.out.println(_);
|
||||
}
|
||||
|
||||
int _ = 123;
|
||||
<error descr="Incompatible types. Found: 'int', required: 'java.lang.String'">String s = _;</error>
|
||||
|
||||
void testLocal() {
|
||||
int _ = 10;
|
||||
int <error descr="Variable '_' is already defined in the scope">_</error> = 20;
|
||||
for (int <error descr="Variable '_' is already defined in the scope">_</error> = 1;;) {}
|
||||
}
|
||||
|
||||
void testCatch() {
|
||||
try {
|
||||
System.out.println();
|
||||
}
|
||||
catch (Exception _) {
|
||||
System.out.println("ignore");
|
||||
}
|
||||
catch (Error _) {
|
||||
int <error descr="Variable '_' is already defined in the scope">_</error> = 1;
|
||||
for(int <error descr="Variable '_' is already defined in the scope">_</error>:new int[10]) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import java.util.function.*;
|
||||
|
||||
public class UnnamedVariables {
|
||||
void testParameter(int <error descr="Variable '_' is already defined in the scope">_</error>, String <error descr="Variable '_' is already defined in the scope">_</error>) {
|
||||
System.out.println(_);
|
||||
}
|
||||
|
||||
int _ = 123;
|
||||
<error descr="Incompatible types. Found: 'int', required: 'java.lang.String'">String s = _;</error>
|
||||
|
||||
void testLambda() {
|
||||
Consumer<String> consumer = <error descr="Use of '_' as a lambda parameter name is not allowed">_</error> -> System.out.println("Hello");
|
||||
Consumer<String> consumer2 = <error descr="Use of '_' as a lambda parameter name is not allowed">_</error> -> System.out.println(_);
|
||||
Consumer<String> consumer3 = <error descr="Use of '_' as a lambda parameter name is not allowed">_</error> -> System.out.println(_.trim());
|
||||
Consumer<String> consumer4 = <error descr="Use of '_' as a lambda parameter name is not allowed">_</error> -> {
|
||||
<error descr="Cannot resolve symbol 'var'" textAttributesKey="WRONG_REFERENCES_ATTRIBUTES">var</error> v = _;
|
||||
System.out.println(v.<error descr="Cannot resolve method 'trim()'" textAttributesKey="WRONG_REFERENCES_ATTRIBUTES">trim</error>());
|
||||
};
|
||||
BiConsumer<String, String> consumer5 = (<error descr="Use of '_' as a lambda parameter name is not allowed"><error descr="Variable '_' is already defined in the scope">_</error></error>,<error descr="Use of '_' as a lambda parameter name is not allowed"><error descr="Variable '_' is already defined in the scope">_</error></error>) -> {};
|
||||
}
|
||||
|
||||
void testLocal() {
|
||||
int _ = 10;
|
||||
int <error descr="Variable '_' is already defined in the scope">_</error> = 20;
|
||||
for (int <error descr="Variable '_' is already defined in the scope">_</error> = 1;;) {}
|
||||
}
|
||||
|
||||
void testCatch() {
|
||||
try {
|
||||
System.out.println();
|
||||
}
|
||||
catch (Exception _) {
|
||||
System.out.println("ignore");
|
||||
}
|
||||
catch (Error _) {
|
||||
int <error descr="Variable '_' is already defined in the scope">_</error> = 1;
|
||||
for(int <error descr="Variable '_' is already defined in the scope">_</error>:new int[10]) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import java.util.function.*;
|
||||
|
||||
public class UnnamedVariables {
|
||||
void testParameter(<error descr="Unnamed patterns and variables are not supported at language level '9'">int _</error>, <error descr="Unnamed patterns and variables are not supported at language level '9'">String _</error>) {
|
||||
System.out.println(<error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>);
|
||||
}
|
||||
|
||||
<error descr="Unnamed patterns and variables are not supported at language level '9'">int _ = 123;</error>
|
||||
String s = <error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>;
|
||||
|
||||
void testLambda() {
|
||||
Consumer<String> consumer = <error descr="Unnamed patterns and variables are not supported at language level '9'">_</error> -> System.out.println("Hello");
|
||||
Consumer<String> consumer2 = <error descr="Unnamed patterns and variables are not supported at language level '9'">_</error> -> System.out.println(<error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>);
|
||||
Consumer<String> consumer3 = <error descr="Unnamed patterns and variables are not supported at language level '9'">_</error> -> System.out.println(<error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>.trim());
|
||||
Consumer<String> consumer4 = <error descr="Unnamed patterns and variables are not supported at language level '9'">_</error> -> {
|
||||
<error descr="Cannot resolve symbol 'var'" textAttributesKey="WRONG_REFERENCES_ATTRIBUTES">var</error> v = <error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>;
|
||||
System.out.println(v.<error descr="Cannot resolve method 'trim()'" textAttributesKey="WRONG_REFERENCES_ATTRIBUTES">trim</error>());
|
||||
};
|
||||
BiConsumer<String, String> consumer5 = (<error descr="Unnamed patterns and variables are not supported at language level '9'">_</error>,<error descr="Unnamed patterns and variables are not supported at language level '9'">_</error>) -> {};
|
||||
}
|
||||
|
||||
void testLocal() {
|
||||
<error descr="Unnamed patterns and variables are not supported at language level '9'">int _ = 10;</error>
|
||||
<error descr="Unnamed patterns and variables are not supported at language level '9'">int _ = 20;</error>
|
||||
for (<error descr="Unnamed patterns and variables are not supported at language level '9'">int _ = 1;</error>;) {}
|
||||
}
|
||||
|
||||
void testCatch() {
|
||||
try {
|
||||
System.out.println();
|
||||
}
|
||||
catch (<error descr="Unnamed patterns and variables are not supported at language level '9'">Exception _</error>) {
|
||||
System.out.println("ignore");
|
||||
}
|
||||
catch (<error descr="Unnamed patterns and variables are not supported at language level '9'">Error _</error>) {
|
||||
<error descr="Unnamed patterns and variables are not supported at language level '9'">int _ = 1;</error>
|
||||
for(<error descr="Unnamed patterns and variables are not supported at language level '9'">int _</error>:new int[10]) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Rename 'vvv' to '_'" "true-preview"
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class Simple {
|
||||
void test() {
|
||||
Consumer<String> cons = _ -> {};
|
||||
cons.accept("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Rename 'x' to '_'" "true-preview"
|
||||
class Simple {
|
||||
void test() {
|
||||
for(int _ : new int[10]) {
|
||||
System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Rename 'vvv' to '_'" "true-preview"
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class Simple {
|
||||
void test() {
|
||||
Consumer<String> cons = v<caret>vv -> {};
|
||||
cons.accept("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Rename 'x' to '_'" "false"
|
||||
class Simple {
|
||||
void test() {
|
||||
// Do not suggest unnamed local, even if it's allowed; only remove variable is suggested
|
||||
int <caret>x = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Rename 'x' to '_'" "true-preview"
|
||||
class Simple {
|
||||
void test() {
|
||||
for(int <caret>x : new int[10]) {
|
||||
System.out.println("hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2000-2023 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.LightIntentionActionTestCase;
|
||||
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase.JAVA_21;
|
||||
|
||||
public class RenameToUnnamedVariableTest extends LightIntentionActionTestCase {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
enableInspectionTools(new UnusedDeclarationInspectionBase());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_21;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/renameToUnnamed";
|
||||
}
|
||||
}
|
||||
@@ -178,7 +178,7 @@ public class LightPatternsForSwitchHighlightingTest extends LightJavaCodeInsight
|
||||
public void testUnusedPatternVariable() {
|
||||
myFixture.enableInspections(new UnusedDeclarationInspection());
|
||||
doTest();
|
||||
assertNotNull(myFixture.getAvailableIntention("Rename 's' to 'ignored'"));
|
||||
assertNotNull(myFixture.getAvailableIntention("Rename 's' to '_'"));
|
||||
}
|
||||
|
||||
public void testMalformedReferenceExpression() {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.codeInsight.daemon;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LightUnnamedVariablesHighlightingTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advHighlightingUnnamed";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_21;
|
||||
}
|
||||
|
||||
public void testUnnamedVariables() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testUnnamedVariablesJava9() {
|
||||
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_1_9, () -> doTest());
|
||||
}
|
||||
|
||||
public void testUnnamedVariablesJava8() {
|
||||
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_1_8, () -> doTest());
|
||||
}
|
||||
|
||||
public void testUnnamedVariablesJava7() {
|
||||
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_1_7, () -> doTest());
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
myFixture.configureByFile(getTestName(false) + ".java");
|
||||
myFixture.checkHighlighting();
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public class ExceptionFromCatchWhichDoesntWrapInspection extends BaseInspection
|
||||
return;
|
||||
}
|
||||
final PsiParameter parameter = catchSection.getParameter();
|
||||
if (parameter == null) {
|
||||
if (parameter == null || parameter.isUnnamed()) {
|
||||
return;
|
||||
}
|
||||
@NonNls final String parameterName = parameter.getName();
|
||||
|
||||
@@ -117,7 +117,7 @@ public final class VariableAccessUtils {
|
||||
* @return true, if the specified variable was assigned in the specified context, false otherwise
|
||||
*/
|
||||
public static boolean variableIsAssigned(@NotNull PsiVariable variable, @Nullable PsiElement context) {
|
||||
if (context == null) {
|
||||
if (context == null || variable.isUnnamed()) {
|
||||
return false;
|
||||
}
|
||||
final VariableAssignedVisitor visitor = new VariableAssignedVisitor(variable);
|
||||
@@ -135,7 +135,7 @@ public final class VariableAccessUtils {
|
||||
*/
|
||||
public static boolean variableIsAssigned(@NotNull PsiVariable variable, @NotNull Predicate<? super PsiAssignmentExpression> skipFilter,
|
||||
@Nullable PsiElement context) {
|
||||
if (context == null) {
|
||||
if (context == null || variable.isUnnamed()) {
|
||||
return false;
|
||||
}
|
||||
final VariableAssignedVisitor visitor = new VariableAssignedVisitor(variable, skipFilter, true);
|
||||
@@ -150,7 +150,7 @@ public final class VariableAccessUtils {
|
||||
* @return true, if the specified variable was assigned in the specified context, false otherwise
|
||||
*/
|
||||
public static boolean variableIsAssigned(@NotNull PsiVariable variable, @Nullable PsiElement context, boolean recurseIntoClasses) {
|
||||
if (context == null) {
|
||||
if (context == null || variable.isUnnamed()) {
|
||||
return false;
|
||||
}
|
||||
final VariableAssignedVisitor visitor = new VariableAssignedVisitor(variable, recurseIntoClasses);
|
||||
@@ -163,7 +163,7 @@ public final class VariableAccessUtils {
|
||||
}
|
||||
|
||||
public static boolean variableIsReturned(@NotNull PsiVariable variable, @Nullable PsiElement context, boolean builderPattern) {
|
||||
if (context == null) {
|
||||
if (context == null || variable.isUnnamed()) {
|
||||
return false;
|
||||
}
|
||||
final VariableReturnedVisitor visitor = new VariableReturnedVisitor(variable, builderPattern);
|
||||
@@ -173,7 +173,7 @@ public final class VariableAccessUtils {
|
||||
|
||||
public static boolean variableValueIsUsed(
|
||||
@NotNull PsiVariable variable, @Nullable PsiElement context) {
|
||||
if (context == null) {
|
||||
if (context == null || variable.isUnnamed()) {
|
||||
return false;
|
||||
}
|
||||
final VariableValueUsedVisitor visitor =
|
||||
@@ -195,7 +195,7 @@ public final class VariableAccessUtils {
|
||||
|
||||
public static boolean variableIsUsedInInnerClass(
|
||||
@NotNull PsiVariable variable, @Nullable PsiElement context) {
|
||||
if (context == null) {
|
||||
if (context == null || variable.isUnnamed()) {
|
||||
return false;
|
||||
}
|
||||
final VariableUsedInInnerClassVisitor visitor =
|
||||
@@ -209,7 +209,7 @@ public final class VariableAccessUtils {
|
||||
}
|
||||
|
||||
static boolean mayEvaluateToVariable(@Nullable PsiExpression expression, @NotNull PsiVariable variable, boolean builderPattern) {
|
||||
if (expression == null) {
|
||||
if (expression == null || variable.isUnnamed()) {
|
||||
return false;
|
||||
}
|
||||
if (expression instanceof PsiParenthesizedExpression parenthesizedExpression) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.siyeh.ig.errorhandling;
|
||||
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction;
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory;
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
@@ -42,7 +43,6 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.fixes.RenameFix;
|
||||
import com.siyeh.ig.fixes.SuppressForTestsScopeFix;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import com.siyeh.ig.psiutils.TestUtils;
|
||||
@@ -92,7 +92,7 @@ public class CatchMayIgnoreExceptionInspection extends AbstractBaseJavaLocalInsp
|
||||
|
||||
private void checkCatchSection(PsiCatchSection section) {
|
||||
final PsiParameter parameter = section.getParameter();
|
||||
if (parameter == null) return;
|
||||
if (parameter == null || parameter.isUnnamed()) return;
|
||||
final PsiIdentifier identifier = parameter.getNameIdentifier();
|
||||
if (identifier == null) return;
|
||||
final String parameterName = parameter.getName();
|
||||
@@ -116,7 +116,7 @@ public class CatchMayIgnoreExceptionInspection extends AbstractBaseJavaLocalInsp
|
||||
if (block == null) return;
|
||||
SuppressForTestsScopeFix fix = SuppressForTestsScopeFix.build(CatchMayIgnoreExceptionInspection.this, section);
|
||||
if (ControlFlowUtils.isEmpty(block, m_ignoreCatchBlocksWithComments, true)) {
|
||||
RenameCatchParameterFix renameFix = new RenameCatchParameterFix(generateName(block));
|
||||
var renameFix = QuickFixFactory.getInstance().createRenameToIgnoredFix(parameter, false);
|
||||
AddCatchBodyFix addBodyFix = getAddBodyFix(block);
|
||||
holder.registerProblem(catchToken, InspectionGadgetsBundle.message("inspection.catch.ignores.exception.empty.message"),
|
||||
LocalQuickFix.notNullElements(renameFix, addBodyFix, fix));
|
||||
@@ -125,7 +125,8 @@ public class CatchMayIgnoreExceptionInspection extends AbstractBaseJavaLocalInsp
|
||||
if (!m_ignoreNonEmptyCatchBlock &&
|
||||
(!m_ignoreCatchBlocksWithComments || PsiTreeUtil.getChildOfType(block, PsiComment.class) == null)) {
|
||||
holder.registerProblem(identifier, InspectionGadgetsBundle.message("inspection.catch.ignores.exception.unused.message"),
|
||||
LocalQuickFix.notNullElements(new RenameFix(generateName(block), false, false), fix));
|
||||
LocalQuickFix.notNullElements(
|
||||
QuickFixFactory.getInstance().createRenameToIgnoredFix(parameter, false), fix));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -307,38 +308,4 @@ public class CatchMayIgnoreExceptionInspection extends AbstractBaseJavaLocalInsp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RenameCatchParameterFix extends PsiUpdateModCommandQuickFix {
|
||||
private final String myName;
|
||||
|
||||
private RenameCatchParameterFix(String name) {
|
||||
myName = name;
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return InspectionGadgetsBundle.message("rename.catch.parameter.to.ignored", myName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("rename.catch.parameter.to.ignored", IGNORED_PARAMETER_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
final PsiElement parent = element.getParent();
|
||||
if (!(parent instanceof PsiCatchSection catchSection)) return;
|
||||
final PsiParameter parameter = catchSection.getParameter();
|
||||
if (parameter == null) return;
|
||||
final PsiIdentifier identifier = parameter.getNameIdentifier();
|
||||
if (identifier == null) return;
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
final PsiIdentifier newIdentifier = factory.createIdentifier(myName);
|
||||
identifier.replace(newIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import java.io.IOException;
|
||||
|
||||
class AAA {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
System.out.println(System.in.read());
|
||||
} c<caret>atch (IOException _) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import java.io.IOException;
|
||||
|
||||
class AAA {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
System.out.println(System.in.read());
|
||||
} c<caret>atch (IOException ex) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.siyeh.ig.fixes.errorhandling;
|
||||
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.IGQuickFixesTestCase;
|
||||
import com.siyeh.ig.errorhandling.CatchMayIgnoreExceptionInspection;
|
||||
@@ -42,10 +44,14 @@ public class CatchMayIgnoreExceptionInspectionFixTest extends IGQuickFixesTestCa
|
||||
}
|
||||
|
||||
public void testRenameToIgnored() {
|
||||
doTest(InspectionGadgetsBundle.message("rename.catch.parameter.to.ignored", "ignored"));
|
||||
doTest("Rename 'ex' to 'ignored'");
|
||||
}
|
||||
|
||||
public void testRenameToIgnoredJava21() {
|
||||
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_21_PREVIEW, () -> doTest("Rename 'ex' to '_'"));
|
||||
}
|
||||
|
||||
public void testRenameToIgnoredNameConflict() {
|
||||
doTest(InspectionGadgetsBundle.message("rename.catch.parameter.to.ignored", "ignored1"));
|
||||
doTest("Rename 'ex' to 'ignored1'");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user