mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 05:10:22 +07:00
[java] IDEA-257411 Update pattern matching for instanceof support for Java 16
1. Do not report error if non-final variable is reassigned 2. Report error in Java 15 if 'final' modifier is used 3. Allow specifying 'final' modifier on introduce variable 4. Support non-final variables in PatternVariableCanBeUsed inspection 5. Copy modifiers in PatternVariableCanBeUsed quick-fix GitOrigin-RevId: d7b82261a018c9a48bcdcf237ade0d8c08f5978d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
bc640063cd
commit
6fe2638869
@@ -611,23 +611,19 @@ public final class HighlightControlFlowUtil {
|
||||
}
|
||||
PsiReferenceExpression reference = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(operand), PsiReferenceExpression.class);
|
||||
PsiVariable variable = reference == null ? null : ObjectUtils.tryCast(reference.resolve(), PsiVariable.class);
|
||||
if (!(variable instanceof PsiPatternVariable)) {
|
||||
if (variable == null || !variable.hasModifierProperty(PsiModifier.FINAL)) return null;
|
||||
final boolean canWrite = canWriteToFinal(variable, expression, reference, containingFile) && checkWriteToFinalInsideLambda(variable, reference) == null;
|
||||
if (canWrite) return null;
|
||||
}
|
||||
if (variable == null || !variable.hasModifierProperty(PsiModifier.FINAL)) return null;
|
||||
final boolean canWrite = canWriteToFinal(variable, expression, reference, containingFile) && checkWriteToFinalInsideLambda(variable, reference) == null;
|
||||
if (canWrite) return null;
|
||||
final String name = variable.getName();
|
||||
String description = JavaErrorBundle.message("assignment.to.final.variable", name);
|
||||
final HighlightInfo highlightInfo =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(reference).descriptionAndTooltip(description).create();
|
||||
if (!(variable instanceof PsiPatternVariable)) {
|
||||
final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression);
|
||||
if (innerClass == null || variable instanceof PsiField) {
|
||||
HighlightFixUtil.registerMakeNotFinalAction(variable, highlightInfo);
|
||||
}
|
||||
else {
|
||||
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
|
||||
}
|
||||
final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression);
|
||||
if (innerClass == null || variable instanceof PsiField) {
|
||||
HighlightFixUtil.registerMakeNotFinalAction(variable, highlightInfo);
|
||||
}
|
||||
else {
|
||||
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
|
||||
}
|
||||
return highlightInfo;
|
||||
}
|
||||
|
||||
@@ -1065,7 +1065,8 @@ public final class HighlightUtil {
|
||||
isAllowed = PsiModifier.STATIC.equals(modifier);
|
||||
}
|
||||
else if (modifierOwner instanceof PsiLocalVariable || modifierOwner instanceof PsiParameter) {
|
||||
isAllowed = PsiModifier.FINAL.equals(modifier);
|
||||
isAllowed = PsiModifier.FINAL.equals(modifier) &&
|
||||
(!(modifierOwner instanceof PsiPatternVariable) || PsiUtil.isLanguageLevel16OrHigher(modifierOwner));
|
||||
}
|
||||
else if (modifierOwner instanceof PsiReceiverParameter || modifierOwner instanceof PsiRecordComponent) {
|
||||
isAllowed = false;
|
||||
|
||||
@@ -1381,7 +1381,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (resolved instanceof PsiVariable && resolved.getContainingFile() == expression.getContainingFile()) {
|
||||
PsiVariable variable = (PsiVariable)resolved;
|
||||
boolean isFinal = variable.hasModifierProperty(PsiModifier.FINAL);
|
||||
if (isFinal && !variable.hasInitializer()) {
|
||||
if (isFinal && !variable.hasInitializer() && !(variable instanceof PsiPatternVariable)) {
|
||||
if (!myHolder.hasErrorResults()) {
|
||||
myHolder.add(HighlightControlFlowUtil.checkFinalVariableMightAlreadyHaveBeenAssignedTo(variable, expression, myFinalVarProblems));
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ public class PatternVariableCanBeUsedInspection extends AbstractBaseJavaLocalIns
|
||||
if (scope == null) return;
|
||||
PsiDeclarationStatement declaration = ObjectUtils.tryCast(variable.getParent(), PsiDeclarationStatement.class);
|
||||
if (declaration == null) return;
|
||||
if (!variable.hasModifierProperty(PsiModifier.FINAL) &&
|
||||
if (!PsiUtil.isLanguageLevel16OrHigher(holder.getFile()) &&
|
||||
!variable.hasModifierProperty(PsiModifier.FINAL) &&
|
||||
!HighlightControlFlowUtil.isEffectivelyFinal(variable, scope, null)) return;
|
||||
PsiInstanceOfExpression instanceOf = InstanceOfUtils.findPatternCandidate(cast);
|
||||
if (instanceOf != null) {
|
||||
@@ -54,7 +55,7 @@ public class PatternVariableCanBeUsedInspection extends AbstractBaseJavaLocalIns
|
||||
private final SmartPsiElementPointer<PsiInstanceOfExpression> myInstanceOfPointer;
|
||||
private final String myName;
|
||||
|
||||
public PatternVariableCanBeUsedFix(@NotNull String name, @NotNull PsiInstanceOfExpression instanceOf) {
|
||||
private PatternVariableCanBeUsedFix(@NotNull String name, @NotNull PsiInstanceOfExpression instanceOf) {
|
||||
myName = name;
|
||||
myInstanceOfPointer = SmartPointerManager.createPointer(instanceOf);
|
||||
}
|
||||
@@ -85,7 +86,11 @@ public class PatternVariableCanBeUsedInspection extends AbstractBaseJavaLocalIns
|
||||
PsiInstanceOfExpression instanceOf = myInstanceOfPointer.getElement();
|
||||
if (instanceOf == null) return;
|
||||
CommentTracker ct = new CommentTracker();
|
||||
ct.replace(instanceOf, ct.text(instanceOf.getOperand()) + " instanceof " + typeElement.getText() + " " + variable.getName());
|
||||
PsiModifierList modifierList = variable.getModifierList();
|
||||
String modifiers = modifierList == null || modifierList.getTextLength() == 0 || !PsiUtil.isLanguageLevel16OrHigher(variable) ?
|
||||
"" : modifierList.getText() + " ";
|
||||
ct.replace(instanceOf, ct.text(instanceOf.getOperand()) +
|
||||
" instanceof " + modifiers + typeElement.getText() + " " + variable.getName());
|
||||
ct.deleteAndRestoreComments(variable);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.intellij.psi.scope.processor.VariablesProcessor;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.refactoring.JavaRefactoringSettings;
|
||||
import com.intellij.refactoring.RefactoringActionHandler;
|
||||
@@ -249,7 +250,7 @@ public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer
|
||||
@Override
|
||||
@Nullable
|
||||
protected JComponent getComponent() {
|
||||
if (getVariable() instanceof PsiPatternVariable) return null;
|
||||
if (getVariable() instanceof PsiPatternVariable && !PsiUtil.isLanguageLevel16OrHigher(getVariable())) return null;
|
||||
if (myCantChangeFinalModifier && !(myCanBeVarType && getVariable() instanceof PsiLocalVariable)) return null;
|
||||
if (!myCantChangeFinalModifier) {
|
||||
myCanBeFinalCb = new NonFocusableCheckBox(JavaRefactoringBundle.message("declare.final"));
|
||||
|
||||
@@ -112,8 +112,10 @@ final class VariableExtractor {
|
||||
|
||||
highlight(var);
|
||||
|
||||
if (!(var instanceof PsiPatternVariable)) {
|
||||
if (!(var instanceof PsiPatternVariable) || PsiUtil.isLanguageLevel16OrHigher(myContainer.getContainingFile())) {
|
||||
PsiUtil.setModifierProperty(var, PsiModifier.FINAL, mySettings.isDeclareFinal());
|
||||
}
|
||||
if (!(var instanceof PsiPatternVariable)) {
|
||||
if (mySettings.isDeclareVarType()) {
|
||||
PsiTypeElement typeElement = var.getTypeElement();
|
||||
LOG.assertTrue(typeElement != null);
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.intellij.psi.impl.source.tree.CompositeElement;
|
||||
import com.intellij.psi.impl.source.tree.Factory;
|
||||
import com.intellij.psi.impl.source.tree.TreeElement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.BitUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
@@ -186,6 +187,9 @@ public class PsiModifierListImpl extends JavaStubPsiElement<PsiModifierListStub>
|
||||
else if (parent instanceof PsiResourceVariable) {
|
||||
Collections.addAll(implicitModifiers, FINAL);
|
||||
}
|
||||
else if (parent instanceof PsiPatternVariable && !PsiUtil.isLanguageLevel16OrHigher(parent)) {
|
||||
Collections.addAll(implicitModifiers, FINAL);
|
||||
}
|
||||
return implicitModifiers;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
class X {
|
||||
void expressions(Object obj) {
|
||||
if (obj instanceof String s) {
|
||||
s = "foo";
|
||||
}
|
||||
if (obj instanceof final String s) {
|
||||
<error descr="Cannot assign a value to final variable 's'">s</error> = "foo";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
class X {
|
||||
void expressions(Object obj) {
|
||||
if (obj instanceof String s) {
|
||||
<error descr="Cannot assign a value to final variable 's'">s</error> = "foo";
|
||||
}
|
||||
if (obj instanceof <error descr="Modifier 'final' not allowed here">final</error> String s) {
|
||||
<error descr="Cannot assign a value to final variable 's'">s</error> = "foo";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof @Foo String s) {
|
||||
}
|
||||
}
|
||||
|
||||
@interface Foo {}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof final String s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof String s) {
|
||||
System.out.println(s);
|
||||
s = s.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof String) {
|
||||
@Foo String <caret>s = (String)obj;
|
||||
}
|
||||
}
|
||||
|
||||
@interface Foo {}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof String) {
|
||||
final String <caret>s = (String)obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// "Replace 's' with pattern variable" "false"
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof String) {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof String s) {
|
||||
}
|
||||
}
|
||||
|
||||
@interface Foo {}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof String s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof String) {
|
||||
@Foo String <caret>s = (String)obj;
|
||||
}
|
||||
}
|
||||
|
||||
@interface Foo {}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace 's' with pattern variable" "true"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof String) {
|
||||
final String <caret>s = (String)obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Replace 's' with pattern variable" "false"
|
||||
class X {
|
||||
void test(Object obj) {
|
||||
if (obj instanceof String) {
|
||||
String <caret>s = (String)obj;
|
||||
System.out.println(s);
|
||||
s = s.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,9 +36,12 @@ public class LightPatternsHighlightingTest extends LightJavaCodeInsightFixtureTe
|
||||
public void testInstanceOfInSwitch() {
|
||||
doTest();
|
||||
}
|
||||
public void testReassignPatternVariable() {
|
||||
public void testReassignPatternVariableJava15() {
|
||||
doTest();
|
||||
}
|
||||
public void testReassignPatternVariable() {
|
||||
IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_16, this::doTest);
|
||||
}
|
||||
public void testUnusedPatternVariable() {
|
||||
myFixture.enableInspections(new UnusedDeclarationInspection());
|
||||
doTest();
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. 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.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.PatternVariableCanBeUsedInspection;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PatternVariableCanBeUsedInspection15Test extends LightQuickFixParameterizedTestCase {
|
||||
@Override
|
||||
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new PatternVariableCanBeUsedInspection()};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return LightJavaCodeInsightFixtureTestCase.JAVA_15;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/inspection/patternVariableCanBeUsed15";
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public class PatternVariableCanBeUsedInspectionTest extends LightQuickFixParamet
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return LightJavaCodeInsightFixtureTestCase.JAVA_15;
|
||||
return LightJavaCodeInsightFixtureTestCase.JAVA_16;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user