IDEA-277338 - fixed the description for malformed ref expressions which can be replaced with type pattern in switch, added the quick-fix

GitOrigin-RevId: c34d26c8dd7f3dfb079adeb49811d64e1bd093fa
This commit is contained in:
Ilyas Selimov
2021-09-08 17:43:18 +07:00
committed by intellij-monorepo-bot
parent 986f038427
commit 6e19d89001
16 changed files with 226 additions and 1 deletions

View File

@@ -320,6 +320,11 @@ public abstract class QuickFixFactory {
return Collections.emptyList();
}
@NotNull
public abstract IntentionAction createReplaceWithTypePatternFix(@NotNull PsiReferenceExpression exprToReplace,
@NotNull PsiClass resolvedExprClass,
@NotNull String patternVarName);
@NotNull
public abstract IntentionAction createStaticImportMethodFix(@NotNull PsiMethodCallExpression call);

View File

@@ -37,6 +37,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
import com.intellij.psi.impl.light.LightRecordMethod;
@@ -66,6 +67,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import com.siyeh.ig.psiutils.VariableNameGenerator;
import org.jetbrains.annotations.*;
import java.util.*;
@@ -2056,6 +2058,21 @@ public final class HighlightUtil {
return checkAssignability(componentType, initializerType, expression, initializer);
}
@Nullable
static HighlightInfo checkPatternVariableRequired(@NotNull PsiReferenceExpression expression,
@NotNull JavaResolveResult resultForIncompleteCode) {
if (!(expression.getParent() instanceof PsiCaseLabelElementList)) return null;
PsiClass resolved = tryCast(resultForIncompleteCode.getElement(), PsiClass.class);
if (resolved == null) return null;
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression)
.descriptionAndTooltip(JavaErrorBundle.message("type.pattern.expected")).create();
if (info != null) {
String patternVarName = new VariableNameGenerator(expression, VariableKind.LOCAL_VARIABLE).byName("ignored").generate(true);
QuickFixAction.registerQuickFixAction(info, getFixFactory().createReplaceWithTypePatternFix(expression, resolved, patternVarName));
}
return info;
}
static HighlightInfo checkExpressionRequired(@NotNull PsiReferenceExpression expression,
@NotNull JavaResolveResult resultForIncompleteCode) {
if (expression.getNextSibling() instanceof PsiErrorElement) return null;

View File

@@ -1449,6 +1449,10 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
if (!myHolder.hasErrorResults() && resultForIncompleteCode != null && HighlightingFeature.PATTERNS_IN_SWITCH.isAvailable(expression)) {
myHolder.add(HighlightUtil.checkPatternVariableRequired(expression, resultForIncompleteCode));
}
if (!myHolder.hasErrorResults() && resultForIncompleteCode != null) {
myHolder.add(HighlightUtil.checkExpressionRequired(expression, resultForIncompleteCode));
}

View File

@@ -409,4 +409,5 @@ implement.or.extend.fix.extend.text=Extend ''{0}''
seal.class.from.permits.list.fix=Seal inheritor
unwrap.array.initializer.fix=Replace array initializer with its element
unwrap.array.initializer.fix=Replace array initializer with its element
replace.with.type.pattern.fix=Replace with type pattern

View File

@@ -0,0 +1,71 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInspection.CommonQuickFixBundle;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class ReplaceWithTypePatternFix extends BaseIntentionAction {
@NotNull private final SmartPsiElementPointer<PsiReferenceExpression> myExprToReplace;
@NotNull private final SmartPsiElementPointer<PsiClass> myResolvedExprClass;
@NlsSafe private final String myPatternVarName;
public ReplaceWithTypePatternFix(@NotNull PsiReferenceExpression exprToReplace, @NotNull PsiClass resolvedExprClass,
@NotNull String patternVarName) {
myExprToReplace = SmartPointerManager.createPointer(exprToReplace);
myResolvedExprClass = SmartPointerManager.createPointer(resolvedExprClass);
myPatternVarName = patternVarName;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
PsiReferenceExpression expression = getExprToReplace();
if (expression == null) return false;
return HighlightingFeature.PATTERNS_IN_SWITCH.isAvailable(expression) && expression.getParent() instanceof PsiCaseLabelElementList;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
PsiReferenceExpression exprToReplace = getExprToReplace();
if (exprToReplace == null) return;
PsiClass resolvedExprClass = getResolvedExprClass();
if (resolvedExprClass == null) return;
PsiExpression newExpr = JavaPsiFacade.getElementFactory(resolvedExprClass.getProject())
.createExpressionFromText("x instanceof " + resolvedExprClass.getName() + " " + myPatternVarName, resolvedExprClass);
if (newExpr instanceof PsiInstanceOfExpression) {
exprToReplace.replace(Objects.requireNonNull(((PsiInstanceOfExpression)newExpr).getPattern()));
}
}
@Override
public @NotNull String getText() {
PsiClass resolvedExprClass = getResolvedExprClass();
if (resolvedExprClass == null) return getFamilyName();
return CommonQuickFixBundle.message("fix.replace.with.x", resolvedExprClass.getName() + " " + myPatternVarName);
}
@Override
public @NotNull String getFamilyName() {
return QuickFixBundle.message("replace.with.type.pattern.fix");
}
@Nullable
private PsiReferenceExpression getExprToReplace() {
return myExprToReplace.getElement();
}
@Nullable
private PsiClass getResolvedExprClass() {
return myResolvedExprClass.getElement();
}
}

View File

@@ -534,6 +534,13 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
return CreateConstructorFromUsage.generateConstructorActions(call);
}
@Override
public @NotNull IntentionAction createReplaceWithTypePatternFix(@NotNull PsiReferenceExpression exprToReplace,
@NotNull PsiClass resolvedExprClass,
@NotNull String patternVarName) {
return new ReplaceWithTypePatternFix(exprToReplace, resolvedExprClass, patternVarName);
}
@NotNull
@Override
public IntentionAction createStaticImportMethodFix(@NotNull PsiMethodCallExpression call) {

View File

@@ -245,6 +245,7 @@ package.local.symbol=''{0}'' is not public in ''{1}''. Cannot be accessed from o
visibility.access.problem=Cannot access ''{0}'' in ''{1}''
visibility.module.access.problem=Access to ''{0}'' in ''{1}'' is prevented by {2}
array.type.expected=Array type expected; found: ''{0}''
type.pattern.expected=Type pattern expected
expression.expected=Expression expected
array.initializer.not.allowed=Array initializer is not allowed here
case.statement.outside.switch=Case statement outside switch

View File

@@ -0,0 +1,42 @@
class Test {
void test(Number n) {
class MyNumber extends Number {
@Override
public int intValue() {
return 0;
}
@Override
public long longValue() {
return 0;
}
@Override
public float floatValue() {
return 0;
}
@Override
public double doubleValue() {
return 0;
}
}
int result;
switch (n) {
case <error descr="Type pattern expected">MyNumber</error>: break;
case <error descr="Type pattern expected">Integer</error><error descr="':' expected"> </error>break;
case default: break;
}
result = switch (n) {
case <error descr="Type pattern expected">MyNumber</error>: yield 1;
case Float ignored: yield 2;
case default: yield 3;
};
result = switch (n) {
case <error descr="Type pattern expected">MyNumber</error> -> 1;
case Float ignored -> 2;
case default -> 3;
};
}
}

View File

@@ -0,0 +1,8 @@
// "Replace with 'Integer ignored1'" "true"
class Test {
void test(Integer n, int ignored) {
switch (n) {
case Integer ignored1: break;
}
}
}

View File

@@ -0,0 +1,10 @@
// "Replace with 'Integer ignored1'" "true"
class Test {
void test(Integer n) {
switch (n) {
case Integer ignored1:
int ignored = 1;
break;
}
}
}

View File

@@ -0,0 +1,8 @@
// "Replace with 'Integer ignored'" "true"
class Test {
void test(Integer n) {
switch (n) {
case Integer ignored: break;
}
}
}

View File

@@ -0,0 +1,8 @@
// "Replace with 'Integer ignored1'" "true"
class Test {
void test(Integer n, int ignored) {
switch (n) {
case Integer<caret>: break;
}
}
}

View File

@@ -0,0 +1,10 @@
// "Replace with 'Integer ignored1'" "true"
class Test {
void test(Integer n) {
switch (n) {
case Integer<caret>:
int ignored = 1;
break;
}
}
}

View File

@@ -0,0 +1,8 @@
// "Replace with 'Integer ignored'" "true"
class Test {
void test(Integer n) {
switch (n) {
case Integer<caret>: break;
}
}
}

View File

@@ -109,6 +109,10 @@ public class LightPatternsForSwitchHighlightingTest extends LightJavaCodeInsight
assertNotNull(myFixture.getAvailableIntention("Rename 's' to 'ignored'"));
}
public void testMalformedReferenceExpression() {
doTest();
}
private void doTest() {
myFixture.configureByFile(getTestName(false) + ".java");
myFixture.checkHighlighting();

View File

@@ -0,0 +1,21 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight.daemon.quickFix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
public class ReplaceWithTypePatternFixTest extends LightQuickFixParameterizedTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithPattern";
}
@Override
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
return LightJavaCodeInsightFixtureTestCase.JAVA_17;
}
}