[java-highlighting] Fixes in unnamed variables highlighting (IDEA-323960)

1. Highlight C-style arrays
2. Do not highlight variables inside for initializer
3. Highlight variables without initializer
4. Better message for underscore references when unnamed variables are allowed

GitOrigin-RevId: 5bca18969cf8fb0ea6e052b0aef71323bbfa69b5
This commit is contained in:
Tagir Valeev
2023-08-02 21:04:54 +00:00
committed by intellij-monorepo-bot
parent 75f3bf6de4
commit a828a34968
6 changed files with 59 additions and 20 deletions
@@ -650,7 +650,8 @@ 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
error.unnamed.variable.not.allowed.in.this.context=Unnamed variable declaration is not allowed in this context
error.unnamed.variable.brackets=Brackets are not allowed after unnamed variable declaration
error.unnamed.variable.without.initializer=Unnamed variable declaration must have an initializer
@@ -68,6 +68,7 @@ import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.InstanceOfUtils;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import com.siyeh.ig.psiutils.VariableNameGenerator;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.*;
import java.awt.*;
@@ -840,7 +841,9 @@ public final class HighlightUtil {
PsiElement parent = identifier.getParent();
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_9) && !(parent instanceof PsiUnnamedPattern) &&
!(parent instanceof PsiVariable var && var.isUnnamed())) {
String text = JavaErrorBundle.message("underscore.identifier.error");
String text = HighlightingFeature.UNNAMED_PATTERNS_AND_VARIABLES.isSufficient(languageLevel) ?
JavaErrorBundle.message("underscore.identifier.error.unnamed")
: JavaErrorBundle.message("underscore.identifier.error");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(identifier).descriptionAndTooltip(text);
}
else if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
@@ -855,13 +858,27 @@ public final class HighlightUtil {
return null;
}
static HighlightInfo.Builder checkAllowedUnnamedLocation(@NotNull PsiVariable variable) {
static HighlightInfo.Builder checkUnnamedVariableDeclaration(@NotNull PsiVariable variable) {
if (isArrayDeclaration(variable)) {
IntentionAction fix = new NormalizeBracketsFix(variable).asIntention();
TokenSet brackets = TokenSet.create(JavaTokenType.LBRACKET, JavaTokenType.RBRACKET);
TextRange range = StreamEx.of(variable.getChildren())
.filter(t -> PsiUtil.isJavaToken(t, brackets))
.map(PsiElement::getTextRangeInParent)
.reduce(TextRange::union)
.orElseThrow()
.shiftRight(variable.getTextRange().getStartOffset());// Must have at least one
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(
JavaAnalysisBundle.message("error.unnamed.variable.brackets")).registerFix(fix, null, null, null, null);
}
if (variable instanceof PsiPatternVariable) return null;
if (variable instanceof PsiResourceVariable) return null;
String message;
IntentionAction fix = null;
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");
if (local.getInitializer() != null) return null;
message = JavaAnalysisBundle.message("error.unnamed.variable.without.initializer");
fix = getFixFactory().createAddVariableInitializerFix(local);
}
else if (variable instanceof PsiParameter parameter) {
PsiElement scope = parameter.getDeclarationScope();
@@ -872,10 +889,15 @@ public final class HighlightUtil {
message = JavaAnalysisBundle.message("error.unnamed.field.not.allowed");
}
else {
message = JavaAnalysisBundle.message("error.unnamed.variable.not.allowed");
message = JavaAnalysisBundle.message("error.unnamed.variable.not.allowed.in.this.context");
}
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(Objects.requireNonNull(variable.getNameIdentifier()))
.descriptionAndTooltip(message);
TextRange range = TextRange.create(variable.getTextRange().getStartOffset(),
Objects.requireNonNull(variable.getNameIdentifier()).getTextRange().getEndOffset());
HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(message);
if (fix != null) {
builder.registerFix(fix, null, null, null, null);
}
return builder;
}
@NotNull
@@ -780,7 +780,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (notAvailable != null) {
add(notAvailable);
} else {
add(HighlightUtil.checkAllowedUnnamedLocation(variable));
add(HighlightUtil.checkUnnamedVariableDeclaration(variable));
}
}
@@ -416,6 +416,7 @@ override.not.allowed.in.interfaces=@Override is not allowed when implementing in
declaration.not.allowed=Declaration not allowed here
underscore.identifier.error=Since Java 9, '_' is a keyword, and may not be used as an identifier
underscore.identifier.error.unnamed=Using '_' as a reference is not allowed
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
@@ -2,7 +2,7 @@ public class UnnamedPatterns {
record R(int a, int b) {}
void test(Object obj) {
if (obj instanceof <error descr="Since Java 9, '_' is a keyword, and may not be used as an identifier">_</error>) {}
if (obj instanceof <error descr="Using '_' as a reference is not allowed">_</error>) {}
if (obj instanceof R(_, _)) {}
if (obj instanceof R(int a, _)) {
@@ -1,28 +1,43 @@
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>);
void testParameter(<error descr="Unnamed method parameter is not allowed">int _</error>, <error descr="Unnamed method parameter is not allowed">String _</error>) {
System.out.println(<error descr="Using '_' as a reference is not allowed">_</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>;
<error descr="Unnamed field is not allowed">int _</error> = 123;
String s = <error descr="Using '_' as a reference is not allowed">_</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> consumer2 = _ -> System.out.println(<error descr="Using '_' as a reference is not allowed">_</error>);
Consumer<String> consumer3 = _ -> System.out.println(<error descr="Using '_' as a reference is not allowed">_</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>());
var v = <error descr="Using '_' as a reference is not allowed">_</error>;
System.out.println(v.<error descr="Cannot resolve method 'trim()'" textAttributesKey="WRONG_REFERENCES_ATTRIBUTES">trim</error>());
};
BiConsumer<String, String> consumer5 = (_,_) -> {};
}
void testWhen(Object obj) {
switch (obj) {
case String _ when <error descr="Using '_' as a reference is not allowed">_</error>.isEmpty() -> {}
}
}
void testLocal() {
int _ = 10;
int _ = 20;
for (int <error descr="Unnamed local variable is not allowed in this context">_</error> = 1;;) {}
int _<error descr="Brackets are not allowed after unnamed variable declaration ">[]</error> = {30};
int[] _ = {40};
var _ = "string";
for (int _ = 1;;) {}
}
void testNoInitializer() {
<error descr="Unnamed variable declaration must have an initializer">int _</error>;
for(<error descr="Unnamed variable declaration must have an initializer">int _</error>;;) {}
}
void testCatch() {