[java] IDEA-326473 Implement unnamed patterns

GitOrigin-RevId: 1b5db700434306be23d07e38905537025e2cc892
This commit is contained in:
Tagir Valeev
2023-07-26 19:21:21 +02:00
committed by intellij-monorepo-bot
parent 1c2bbc906a
commit ab550ac4c0
34 changed files with 425 additions and 19 deletions

View File

@@ -132,6 +132,7 @@ feature.pattern.guard.and.record.patterns=Pattern guards and record patterns
feature.record.patterns.in.for.each=Record patterns in for-each loops
feature.enum.qualified.name.in.switch=Qualified enum as a constant in switch
feature.string.templates=String templates
feature.unnamed.vars=Unnamed patterns and variables
find.searching.for.references.to.class.progress=Searching for references to class {0}...
find.usages.panel.title.derived.classes.cap=Derived classes
@@ -644,3 +645,4 @@ inspection.extract.method.dont.suggest.parameters=Don''t suggest extracting meth
inspection.extract.method.dont.suggest.length=Don\u2019t suggest extracting methods as short as this
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

View File

@@ -837,13 +837,13 @@ public final class HighlightUtil {
static HighlightInfo.Builder checkUnderscore(@NotNull PsiIdentifier identifier, @NotNull LanguageLevel languageLevel) {
if ("_".equals(identifier.getText())) {
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_9)) {
PsiElement parent = identifier.getParent();
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_9) && !(parent instanceof PsiUnnamedPattern)) {
String text = JavaErrorBundle.message("underscore.identifier.error");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(identifier).descriptionAndTooltip(text);
}
else if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
PsiElement parent = identifier.getParent();
if (parent instanceof PsiParameter && ((PsiParameter)parent).getDeclarationScope() instanceof PsiLambdaExpression) {
if (parent instanceof PsiParameter parameter && parameter.getDeclarationScope() instanceof PsiLambdaExpression) {
String text = JavaErrorBundle.message("underscore.lambda.identifier");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(identifier).descriptionAndTooltip(text);
}

View File

@@ -2122,6 +2122,12 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
@Override
public void visitUnnamedPattern(@NotNull PsiUnnamedPattern pattern) {
super.visitUnnamedPattern(pattern);
add(checkFeature(pattern, HighlightingFeature.UNNAMED_PATTERNS_AND_VARIABLES));
}
private HighlightInfo.Builder checkFeature(@NotNull PsiElement element, @NotNull HighlightingFeature feature) {
return HighlightUtil.checkFeature(element, feature, myLanguageLevel, myFile);
}

View File

@@ -77,14 +77,14 @@ public enum HighlightingFeature {
return until == useSiteLevel;
}
@Override
boolean isLimited() {
return true;
}
},
ENUM_QUALIFIED_NAME_IN_SWITCH(LanguageLevel.JDK_21, "feature.enum.qualified.name.in.switch"),
STRING_TEMPLATES(LanguageLevel.JDK_21_PREVIEW, "feature.string.templates");
STRING_TEMPLATES(LanguageLevel.JDK_21_PREVIEW, "feature.string.templates"),
UNNAMED_PATTERNS_AND_VARIABLES(LanguageLevel.JDK_21_PREVIEW, "feature.unnamed.vars");
public static final @NonNls String JDK_INTERNAL_PREVIEW_FEATURE = "jdk.internal.PreviewFeature";
public static final @NonNls String JDK_INTERNAL_JAVAC_PREVIEW_FEATURE = "jdk.internal.javac.PreviewFeature";

View File

@@ -6,6 +6,7 @@ import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.UnusedImportProvider;
import com.intellij.codeInsight.daemon.impl.*;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceWithUnnamedPatternFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper;
@@ -399,15 +400,27 @@ class PostHighlightingVisitor {
}
else if (parameter instanceof PsiPatternVariable variable) {
HighlightInfo.Builder highlightInfo = checkUnusedParameter(parameter, identifier, null);
PsiPattern pattern = variable.getPattern();
if (highlightInfo != null) {
if (declarationScope.getParent() instanceof PsiSwitchBlock) {
IntentionAction action = variable.getParent() instanceof PsiDeconstructionPattern
IntentionAction action = null;
if (HighlightingFeature.UNNAMED_PATTERNS_AND_VARIABLES.isAvailable(parameter)) {
if (pattern instanceof PsiTypeTestPattern && pattern.getParent() instanceof PsiDeconstructionList) {
PsiRecordComponent component = JavaPsiPatternUtil.getRecordComponentForPattern(pattern);
PsiTypeElement checkType = ((PsiTypeTestPattern)pattern).getCheckType();
if (component != null && checkType != null && checkType.getType().isAssignableFrom(component.getType())) {
action = new ReplaceWithUnnamedPatternFix(pattern).asIntention();
}
}
}
if (action == null && declarationScope.getParent() instanceof PsiSwitchBlock) {
action = variable.getParent() instanceof PsiDeconstructionPattern
? quickFixFactory.createDeleteFix(parameter)
: quickFixFactory.createRenameToIgnoredFix(parameter, false);
highlightInfo.registerFix(action, null, null, null, null);
}
else if (!(variable.getPattern() instanceof PsiTypeTestPattern pattern && pattern.getParent() instanceof PsiDeconstructionList)) {
IntentionAction action = quickFixFactory.createDeleteFix(parameter);
else if (!(pattern instanceof PsiTypeTestPattern && pattern.getParent() instanceof PsiDeconstructionList)) {
action = quickFixFactory.createDeleteFix(parameter);
}
if (action != null) {
highlightInfo.registerFix(action, null, null, null, null);
}
return highlightInfo;

View File

@@ -0,0 +1,40 @@
// 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.java.analysis.JavaAnalysisBundle;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandAction;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class ReplaceWithUnnamedPatternFix extends PsiUpdateModCommandAction<PsiPattern> {
public ReplaceWithUnnamedPatternFix(@NotNull PsiPattern element) {
super(element);
}
@Override
protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiPattern element) {
return Presentation.of(getFamilyName()).withFixAllOption(this);
}
@Override
public @NotNull String getFamilyName() {
return JavaAnalysisBundle.message("intention.family.name.replace.with.unnamed.pattern");
}
@Override
protected void invoke(@NotNull ActionContext context, @NotNull PsiPattern pattern, @NotNull ModPsiUpdater updater) {
PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.project());
pattern.replace(createUnnamedPattern(factory));
}
private static PsiUnnamedPattern createUnnamedPattern(PsiElementFactory factory) {
PsiInstanceOfExpression expr = (PsiInstanceOfExpression)factory
.createExpressionFromText("x instanceof R(_)", null);
PsiDeconstructionPattern pattern = (PsiDeconstructionPattern)Objects.requireNonNull(expr.getPattern());
return ((PsiUnnamedPattern)pattern.getDeconstructionList().getDeconstructionComponents()[0]);
}
}

View File

@@ -482,6 +482,10 @@ public abstract class JavaElementVisitor extends PsiElementVisitor {
visitExpression(expression);
}
public void visitUnnamedPattern(@NotNull PsiUnnamedPattern pattern) {
visitPattern(pattern);
}
public void visitUsesStatement(@NotNull PsiUsesStatement statement) {
visitModuleStatement(statement);
}

View File

@@ -0,0 +1,17 @@
// 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.psi;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Represents an unnamed pattern (single '_' inside deconstruction pattern, like {@code R(_)}).
* Not to be confused with type pattern with unnamed variable (like {@code R(int _)})
*/
@ApiStatus.Experimental
public interface PsiUnnamedPattern extends PsiPrimaryPattern {
/**
* @return implicit type element (empty)
*/
@NotNull PsiTypeElement getTypeElement();
}

View File

@@ -37,6 +37,20 @@ public class PatternParser {
return true;
}
private static boolean parseUnnamedPattern(final PsiBuilder builder) {
PsiBuilder.Marker patternStart = builder.mark();
if (builder.getTokenType() == JavaTokenType.IDENTIFIER &&
"_".equals(builder.getTokenText())) {
emptyElement(builder, JavaElementType.TYPE);
builder.advanceLexer();
done(patternStart, JavaElementType.UNNAMED_PATTERN);
return true;
}
patternStart.rollbackTo();
return false;
}
@Nullable("when not pattern")
PsiBuilder.Marker preParsePattern(final PsiBuilder builder, boolean parensAllowed) {
PsiBuilder.Marker patternStart = builder.mark();
@@ -100,6 +114,9 @@ public class PatternParser {
parsePattern(builder, true);
isFirst = false;
}
else if (parseUnnamedPattern(builder)) {
isFirst = false;
}
else {
int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD | ReferenceParser.VAR_TYPE;
myParser.getReferenceParser().parseType(builder, flags);

View File

@@ -63,7 +63,11 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
List<TypeAnnotationProvider> arrayComponentAnnotations = new SmartList<>();
PsiElement parent = getParent();
for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) {
PsiElement firstChild = getFirstChild();
if (firstChild == null && parent instanceof PsiUnnamedPattern) {
type = JavaPsiPatternUtil.getDeconstructedImplicitPatternType((PsiPattern)parent);
}
for (PsiElement child = firstChild; child != null; child = child.getNextSibling()) {
if (child instanceof PsiComment || child instanceof PsiWhiteSpace) continue;
if (child instanceof PsiAnnotation) {

View File

@@ -57,7 +57,7 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, JavaElemen
CONTINUE_STATEMENT, RETURN_STATEMENT, THROW_STATEMENT, SYNCHRONIZED_STATEMENT, TRY_STATEMENT, LABELED_STATEMENT, ASSERT_STATEMENT,
YIELD_STATEMENT);
TokenSet JAVA_PATTERN_BIT_SET = TokenSet.create(TYPE_TEST_PATTERN, PARENTHESIZED_PATTERN, DECONSTRUCTION_PATTERN);
TokenSet JAVA_PATTERN_BIT_SET = TokenSet.create(TYPE_TEST_PATTERN, PARENTHESIZED_PATTERN, DECONSTRUCTION_PATTERN, UNNAMED_PATTERN);
TokenSet JAVA_CASE_LABEL_ELEMENT_BIT_SET = TokenSet.orSet(JAVA_PATTERN_BIT_SET, EXPRESSION_BIT_SET, TokenSet.create(
DEFAULT_CASE_LABEL_ELEMENT, PATTERN_GUARD));

View File

@@ -139,6 +139,7 @@ public interface JavaElementType {
IElementType ANNOTATION_ARRAY_INITIALIZER = new JavaCompositeElementType("ANNOTATION_ARRAY_INITIALIZER", () -> new PsiArrayInitializerMemberValueImpl());
IElementType RECEIVER_PARAMETER = new JavaCompositeElementType("RECEIVER", () -> new PsiReceiverParameterImpl());
IElementType MODULE_REFERENCE = new JavaCompositeElementType("MODULE_REFERENCE", () -> new PsiJavaModuleReferenceElementImpl());
IElementType UNNAMED_PATTERN = new JavaCompositeElementType("UNNAMED_PATTERN", () -> new PsiUnnamedPatternImpl());
IElementType TYPE_TEST_PATTERN = new JavaCompositeElementType("TYPE_TEST_PATTERN", () -> new PsiTypeTestPatternImpl());
IElementType PATTERN_VARIABLE = new JavaCompositeElementType("PATTERN_VARIABLE", () -> new PsiPatternVariableImpl());
IElementType DECONSTRUCTION_PATTERN = new JavaCompositeElementType("DECONSTRUCTION_PATTERN", () -> new PsiDeconstructionPatternImpl());

View File

@@ -15,7 +15,7 @@ import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.tree.JavaElementType.*;
public class PsiDeconstructionListImpl extends CompositePsiElement implements PsiDeconstructionList {
private final TokenSet PRIMARY_PATTERN_SET = TokenSet.create(TYPE_TEST_PATTERN, DECONSTRUCTION_PATTERN, PARENTHESIZED_PATTERN);
private final TokenSet PRIMARY_PATTERN_SET = TokenSet.create(TYPE_TEST_PATTERN, DECONSTRUCTION_PATTERN, PARENTHESIZED_PATTERN, UNNAMED_PATTERN);
public PsiDeconstructionListImpl() {
super(DECONSTRUCTION_LIST);

View File

@@ -0,0 +1,41 @@
// 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.psi.impl.source.tree.java;
import com.intellij.psi.JavaElementVisitor;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiTypeElement;
import com.intellij.psi.PsiUnnamedPattern;
import com.intellij.psi.impl.source.Constants;
import com.intellij.psi.impl.source.tree.CompositePsiElement;
import com.intellij.psi.impl.source.tree.JavaElementType;
import org.jetbrains.annotations.NotNull;
public class PsiUnnamedPatternImpl extends CompositePsiElement implements PsiUnnamedPattern, Constants {
public PsiUnnamedPatternImpl() {
super(UNNAMED_PATTERN);
}
@Override
public @NotNull PsiTypeElement getTypeElement() {
PsiTypeElement type = (PsiTypeElement)findPsiChildByType(JavaElementType.TYPE);
assert type != null; // guaranteed by parser
return type;
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitUnnamedPattern(this);
}
else {
visitor.visitElement(this);
}
}
@Override
public String toString() {
return "PsiUnnamedPattern";
}
}

View File

@@ -209,6 +209,9 @@ public final class JavaPsiPatternUtil {
else if (pattern instanceof PsiTypeTestPattern) {
return ((PsiTypeTestPattern)pattern).getCheckType();
}
else if (pattern instanceof PsiUnnamedPattern) {
return ((PsiUnnamedPattern)pattern).getTypeElement();
}
return null;
}
@@ -229,11 +232,8 @@ public final class JavaPsiPatternUtil {
else if (pattern instanceof PsiParenthesizedPattern) {
return findUnconditionalPattern(((PsiParenthesizedPattern)pattern).getPattern());
}
else if (pattern instanceof PsiDeconstructionPattern) {
return (PsiDeconstructionPattern)pattern;
}
else if (pattern instanceof PsiTypeTestPattern) {
return (PsiTypeTestPattern)pattern;
else if (pattern instanceof PsiDeconstructionPattern || pattern instanceof PsiTypeTestPattern || pattern instanceof PsiUnnamedPattern) {
return (PsiPrimaryPattern)pattern;
}
return null;
}
@@ -243,7 +243,7 @@ public final class JavaPsiPatternUtil {
if (unconditionalPattern instanceof PsiDeconstructionPattern) {
return forDomination && dominates(getPatternType(unconditionalPattern), type);
}
else if (unconditionalPattern instanceof PsiTypeTestPattern) {
else if (unconditionalPattern instanceof PsiTypeTestPattern || unconditionalPattern instanceof PsiUnnamedPattern) {
return dominates(getPatternType(unconditionalPattern), type);
}
return false;

View File

@@ -0,0 +1,30 @@
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 R(_, _)) {}
if (obj instanceof R(int a, _)) {
System.out.println(a);
}
if (obj instanceof R(_, int b)) {
System.out.println(b);
}
if (obj instanceof R(_, _, <error descr="Incorrect number of nested patterns: expected 2 but found 3">_)</error>) {
}
}
void testSwitch(Object obj) {
switch (obj) {
case R(_, var b) -> {
}
case <error descr="Label is dominated by a preceding case label 'R(_, var b)'">R(var c, var b)</error> -> {
}
case <error descr="Label is dominated by a preceding case label 'R(_, var b)'">R(int a, _)</error> -> {
}
default -> {
}
}
}
}

View File

@@ -0,0 +1,17 @@
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 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>)) {
System.out.println(a);
}
if (obj instanceof R(<error descr="Unnamed patterns and variables are not supported at language level '20'">_</error>, int b)) {
System.out.println(b);
}
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>, <error descr="Unnamed patterns and variables are not supported at language level '20'">_</error>)) {
}
}
}

View File

@@ -0,0 +1,9 @@
// "Apply all 'Replace with unnamed pattern' fixes in file" "true"
public class Test {
record R(int x, int y) {}
void test(Object obj) {
if (obj instanceof R(_, _)) {
}
}
}

View File

@@ -0,0 +1,9 @@
// "Replace with unnamed pattern" "true-preview"
public class Test {
record R(int x, int y) {}
void test(Object obj) {
if (obj instanceof R(_, int b)) {
}
}
}

View File

@@ -0,0 +1,9 @@
// "Replace with unnamed pattern" "true-preview"
public class Test {
record R(int x, int y) {}
void test(Object obj) {
switch(obj) {
case R(_, int b) {}
}
}
}

View File

@@ -0,0 +1,9 @@
// "Apply all 'Replace with unnamed pattern' fixes in file" "true"
public class Test {
record R(int x, int y) {}
void test(Object obj) {
if (obj instanceof R(int <caret>a, int b)) {
}
}
}

View File

@@ -0,0 +1,9 @@
// "Replace with unnamed pattern" "false"
public class Test {
record R(Object cmp) {}
void test(Object obj) {
if (obj instanceof R(String <caret>a)) {
}
}
}

View File

@@ -0,0 +1,9 @@
// "Replace with unnamed pattern" "true-preview"
public class Test {
record R(int x, int y) {}
void test(Object obj) {
if (obj instanceof R(int <caret>a, int b)) {
}
}
}

View File

@@ -0,0 +1,9 @@
// "Replace with unnamed pattern" "true-preview"
public class Test {
record R(int x, int y) {}
void test(Object obj) {
switch(obj) {
case R(int <caret>a, int b) {}
}
}
}

View File

@@ -0,0 +1,14 @@
class Test {
record R(int x, int y) {}
void test(Object obj) {
if (obj instanceof R(_, var b)) {
return;
}
if (<warning descr="Condition 'obj instanceof R(var a, var b)' is always 'false'">obj instanceof R(var a, var b)</warning>) {
return;
}
if (<warning descr="Condition 'obj instanceof R(int a, _)' is always 'false'">obj instanceof R(int a, _)</warning>) {
return;
}
}
}

View File

@@ -0,0 +1,16 @@
PsiJavaFile:RecordUnnamed0.java
PsiDeconstructionPattern
PsiModifierList:
<empty list>
PsiTypeElement:Point
PsiJavaCodeReferenceElement:Point
PsiIdentifier:Point('Point')
PsiReferenceParameterList
<empty list>
PsiDeconstructionList
PsiJavaToken:LPARENTH('(')
PsiUnnamedPattern
PsiTypeElement:
<empty list>
PsiIdentifier:_('_')
PsiJavaToken:RPARENTH(')')

View File

@@ -0,0 +1,26 @@
PsiJavaFile:RecordUnnamed1.java
PsiDeconstructionPattern
PsiModifierList:
<empty list>
PsiTypeElement:Point
PsiJavaCodeReferenceElement:Point
PsiIdentifier:Point('Point')
PsiReferenceParameterList
<empty list>
PsiDeconstructionList
PsiJavaToken:LPARENTH('(')
PsiTypeTestPattern
PsiPatternVariable:x
PsiModifierList:
<empty list>
PsiTypeElement:int
PsiKeyword:int('int')
PsiWhiteSpace(' ')
PsiIdentifier:x('x')
PsiJavaToken:COMMA(',')
PsiWhiteSpace(' ')
PsiUnnamedPattern
PsiTypeElement:
<empty list>
PsiIdentifier:_('_')
PsiJavaToken:RPARENTH(')')

View File

@@ -0,0 +1,26 @@
PsiJavaFile:RecordUnnamed2.java
PsiDeconstructionPattern
PsiModifierList:
<empty list>
PsiTypeElement:Point
PsiJavaCodeReferenceElement:Point
PsiIdentifier:Point('Point')
PsiReferenceParameterList
<empty list>
PsiDeconstructionList
PsiJavaToken:LPARENTH('(')
PsiUnnamedPattern
PsiTypeElement:
<empty list>
PsiIdentifier:_('_')
PsiJavaToken:COMMA(',')
PsiWhiteSpace(' ')
PsiTypeTestPattern
PsiPatternVariable:y
PsiModifierList:
<empty list>
PsiTypeElement:int
PsiKeyword:int('int')
PsiWhiteSpace(' ')
PsiIdentifier:y('y')
PsiJavaToken:RPARENTH(')')

View File

@@ -0,0 +1,22 @@
PsiJavaFile:RecordUnnamed3.java
PsiDeconstructionPattern
PsiModifierList:
<empty list>
PsiTypeElement:Point
PsiJavaCodeReferenceElement:Point
PsiIdentifier:Point('Point')
PsiReferenceParameterList
<empty list>
PsiDeconstructionList
PsiJavaToken:LPARENTH('(')
PsiUnnamedPattern
PsiTypeElement:
<empty list>
PsiIdentifier:_('_')
PsiJavaToken:COMMA(',')
PsiWhiteSpace(' ')
PsiUnnamedPattern
PsiTypeElement:
<empty list>
PsiIdentifier:_('_')
PsiJavaToken:RPARENTH(')')

View File

@@ -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 ChangeToUnnamedPatternTest extends LightIntentionActionTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
enableInspectionTool(new UnusedDeclarationInspectionBase());
}
@NotNull
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return JAVA_21;
}
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/changeToUnnamed";
}
}

View File

@@ -96,6 +96,14 @@ public class LightPatternsHighlightingTest extends LightJavaCodeInsightFixtureTe
public void testNotAnnotationsInDeconstructionType() {
doTest();
}
public void testUnnamedPatterns() {
doTest();
}
public void testUnnamedPatternsUnavailable() {
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_20, this::doTest);
}
private void doTest() {
myFixture.configureByFile(getTestName(false) + ".java");

View File

@@ -52,6 +52,10 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase {
doTest();
}
public void testUnnamedPatterns() {
doTest();
}
public void testPatternInStreamNotComplex() {
doTest();
}

View File

@@ -23,6 +23,10 @@ public class PatternParserTest extends JavaParsingTestCase {
public void testRecord10() { doParserTest("Rec r"); }
public void testRecord11() { doParserTest("Rec(var x) r"); }
public void testRecord12() { doParserTest("Rec(String)"); }
public void testRecordUnnamed0() { doParserTest("Point(_)"); }
public void testRecordUnnamed1() { doParserTest("Point(int x, _)"); }
public void testRecordUnnamed2() { doParserTest("Point(_, int y)"); }
public void testRecordUnnamed3() { doParserTest("Point(_, _)"); }
private void doParserTest(String text) {
doParserTest(text, builder -> {

View File

@@ -491,6 +491,9 @@ public class EquivalenceChecker {
}
pattern1 = JavaPsiPatternUtil.skipParenthesizedPatternDown(pattern1);
pattern2 = JavaPsiPatternUtil.skipParenthesizedPatternDown(pattern2);
if (pattern1 instanceof PsiUnnamedPattern && pattern2 instanceof PsiUnnamedPattern) {
return EXACT_MATCH;
}
if (pattern1 instanceof PsiTypeTestPattern && pattern2 instanceof PsiTypeTestPattern) {
return Match.exact(primaryPatternsMatch((PsiTypeTestPattern)pattern1, (PsiTypeTestPattern)pattern2));
}