mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 05:10:22 +07:00
[java-intentions] VariableAccessFromInnerClassFix: adapt to 'when' expressions
IJ-CR-95276 IDEA-301356 GitOrigin-RevId: 7281c53c12f40840de36cc8d7e0c18e20ae8c463
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d572da2539
commit
8b99ea7e9c
@@ -644,12 +644,12 @@ public final class HighlightControlFlowUtil {
|
||||
String description = JavaErrorBundle.message("assignment.to.final.variable", name);
|
||||
HighlightInfo highlightInfo =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(reference).descriptionAndTooltip(description).create();
|
||||
PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression);
|
||||
if (innerClass == null || variable instanceof PsiField) {
|
||||
PsiElement scope = getElementVariableReferencedFrom(variable, expression);
|
||||
if (scope == null || variable instanceof PsiField) {
|
||||
HighlightFixUtil.registerMakeNotFinalAction(variable, highlightInfo);
|
||||
}
|
||||
else {
|
||||
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
|
||||
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, scope));
|
||||
}
|
||||
return highlightInfo;
|
||||
}
|
||||
@@ -660,13 +660,13 @@ public final class HighlightControlFlowUtil {
|
||||
@NotNull PsiFile containingFile) {
|
||||
if (variable.hasInitializer()) return false;
|
||||
if (variable instanceof PsiParameter) return false;
|
||||
PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression);
|
||||
PsiElement scope = getElementVariableReferencedFrom(variable, expression);
|
||||
if (variable instanceof PsiField) {
|
||||
// if inside some field initializer
|
||||
if (HighlightUtil.findEnclosingFieldInitializer(expression) != null) return true;
|
||||
// assignment from within inner class is illegal always
|
||||
PsiField field = (PsiField)variable;
|
||||
if (innerClass != null && !containingFile.getManager().areElementsEquivalent(innerClass, field.getContainingClass())) return false;
|
||||
if (scope != null && !containingFile.getManager().areElementsEquivalent(scope, field.getContainingClass())) return false;
|
||||
PsiMember enclosingCtrOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expression);
|
||||
return enclosingCtrOrInitializer != null &&
|
||||
!(enclosingCtrOrInitializer instanceof PsiMethod &&
|
||||
@@ -674,7 +674,7 @@ public final class HighlightControlFlowUtil {
|
||||
isSameField(enclosingCtrOrInitializer, field, reference, containingFile);
|
||||
}
|
||||
if (variable instanceof PsiLocalVariable) {
|
||||
boolean isAccessedFromOtherClass = innerClass != null;
|
||||
boolean isAccessedFromOtherClass = scope != null;
|
||||
return !isAccessedFromOtherClass;
|
||||
}
|
||||
return true;
|
||||
@@ -693,8 +693,8 @@ public final class HighlightControlFlowUtil {
|
||||
@NotNull PsiJavaCodeReferenceElement context,
|
||||
@NotNull LanguageLevel languageLevel) {
|
||||
if (variable.hasModifierProperty(PsiModifier.FINAL)) return null;
|
||||
PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, context);
|
||||
if (innerClass instanceof PsiClass) {
|
||||
PsiElement scope = getElementVariableReferencedFrom(variable, context);
|
||||
if (scope instanceof PsiClass) {
|
||||
if (variable instanceof PsiParameter) {
|
||||
PsiElement parent = variable.getParent();
|
||||
if (parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression &&
|
||||
@@ -703,7 +703,7 @@ public final class HighlightControlFlowUtil {
|
||||
}
|
||||
}
|
||||
boolean isToBeEffectivelyFinal = languageLevel.isAtLeast(LanguageLevel.JDK_1_8);
|
||||
if (isToBeEffectivelyFinal && isEffectivelyFinal(variable, innerClass, context)) {
|
||||
if (isToBeEffectivelyFinal && isEffectivelyFinal(variable, scope, context)) {
|
||||
return null;
|
||||
}
|
||||
String description = JavaErrorBundle
|
||||
@@ -711,7 +711,7 @@ public final class HighlightControlFlowUtil {
|
||||
|
||||
HighlightInfo highlightInfo =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(context).descriptionAndTooltip(description).create();
|
||||
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass));
|
||||
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, scope));
|
||||
return highlightInfo;
|
||||
}
|
||||
HighlightInfo finalInsideLambdaInfo = checkWriteToFinalInsideLambda(variable, context);
|
||||
@@ -754,11 +754,10 @@ public final class HighlightControlFlowUtil {
|
||||
if (refGuardedPattern == null) return null;
|
||||
PsiCaseLabelElement varGuardedPattern = PsiTreeUtil.getParentOfType(variable, PsiGuardedPattern.class, PsiPatternGuard.class);
|
||||
if (refGuardedPattern != varGuardedPattern && !isEffectivelyFinal(variable, refGuardedPattern, context)) {
|
||||
HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(context)
|
||||
.descriptionAndTooltip(JavaErrorBundle.message("guarded.pattern.variable.must.be.final")).create();
|
||||
// todo quick-fix may be registered here, but
|
||||
// todo com.intellij.codeInsight.intention.QuickFixFactory.createVariableAccessFromInnerClassFix should be fix beforehand
|
||||
return ErrorFixExtensionPoint.registerFixes(highlightInfo, context, "guarded.pattern.variable.must.be.final");
|
||||
String message = JavaErrorBundle.message("guarded.pattern.variable.must.be.final");
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(context).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, refGuardedPattern));
|
||||
return ErrorFixExtensionPoint.registerFixes(info, context, "guarded.pattern.variable.must.be.final");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -809,17 +808,22 @@ public final class HighlightControlFlowUtil {
|
||||
return effectivelyFinal;
|
||||
}
|
||||
|
||||
public static PsiElement getInnerClassVariableReferencedFrom(@NotNull PsiVariable variable, @NotNull PsiElement context) {
|
||||
/**
|
||||
* @param variable variable
|
||||
* @param context the context that reference to the variable
|
||||
* @return inner class, lambda expression, or guarded pattern that refers to the variable
|
||||
*/
|
||||
public static @Nullable PsiElement getElementVariableReferencedFrom(@NotNull PsiVariable variable, @NotNull PsiElement context) {
|
||||
PsiElement[] scope;
|
||||
if (variable instanceof PsiResourceVariable) {
|
||||
scope = ((PsiResourceVariable)variable).getDeclarationScope();
|
||||
if (variable instanceof PsiResourceVariable resourceVariable) {
|
||||
scope = resourceVariable.getDeclarationScope();
|
||||
}
|
||||
else if (variable instanceof PsiLocalVariable) {
|
||||
PsiElement parent = variable.getParent();
|
||||
scope = new PsiElement[]{parent != null ? parent.getParent() : null}; // code block or for statement
|
||||
}
|
||||
else if (variable instanceof PsiParameter) {
|
||||
scope = new PsiElement[]{((PsiParameter)variable).getDeclarationScope()};
|
||||
else if (variable instanceof PsiParameter parameter) {
|
||||
scope = new PsiElement[]{parameter.getDeclarationScope()};
|
||||
}
|
||||
else {
|
||||
scope = new PsiElement[]{variable.getParent()};
|
||||
@@ -838,6 +842,9 @@ public final class HighlightControlFlowUtil {
|
||||
if (parent instanceof PsiLambdaExpression) {
|
||||
return parent;
|
||||
}
|
||||
if (parent instanceof PsiGuardedPattern || parent instanceof PsiPatternGuard) {
|
||||
return parent;
|
||||
}
|
||||
prevParent = parent;
|
||||
parent = parent.getParent();
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public final class ReassignVariableUtil {
|
||||
PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null);
|
||||
if (outerCodeBlock == null) continue;
|
||||
if (ReferencesSearch.search(variable, new LocalSearchScope(outerCodeBlock))
|
||||
.allMatch(reference -> HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, reference.getElement()) == null)) {
|
||||
.allMatch(reference -> HighlightControlFlowUtil.getElementVariableReferencedFrom(variable, reference.getElement()) == null)) {
|
||||
vars.add(variable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
|
||||
@FixType
|
||||
private final int myFixType;
|
||||
private static final int MAKE_FINAL = 0;
|
||||
private static final int MAKE_ARRAY = 1;
|
||||
private static final int COPY_TO_FINAL = 2;
|
||||
private static final int COPY_TO_FINAL = 1;
|
||||
private static final int MAKE_ARRAY = 2;
|
||||
private static final int UNKNOWN = -1;
|
||||
@MagicConstant(intValues = {MAKE_FINAL, COPY_TO_FINAL, MAKE_ARRAY, UNKNOWN})
|
||||
@interface FixType { }
|
||||
@@ -235,7 +235,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
|
||||
}
|
||||
PsiElement element = statement;
|
||||
while (element != declarationScope && !(element instanceof PsiFile)) {
|
||||
if (element instanceof PsiClass || element instanceof PsiLambdaExpression) {
|
||||
if (element instanceof PsiClass || element instanceof PsiLambdaExpression || element instanceof PsiSwitchLabelStatementBase) {
|
||||
statement = statement.getParent();
|
||||
continue nextInnerClass;
|
||||
}
|
||||
@@ -299,7 +299,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
|
||||
int type = MAKE_FINAL;
|
||||
for (PsiReferenceExpression expression : outerReferences) {
|
||||
// if it happens that variable referenced from another inner class, make sure it can be make final from there
|
||||
PsiElement innerScope = HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, expression);
|
||||
PsiElement innerScope = HighlightControlFlowUtil.getElementVariableReferencedFrom(variable, expression);
|
||||
|
||||
if (innerScope != null) {
|
||||
@FixType int thisType = MAKE_FINAL;
|
||||
|
||||
@@ -295,7 +295,7 @@ public class VariableLookupItem extends LookupItem<PsiVariable> implements Typed
|
||||
return;
|
||||
}
|
||||
|
||||
if (HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, place) != null &&
|
||||
if (HighlightControlFlowUtil.getElementVariableReferencedFrom(variable, place) != null &&
|
||||
!HighlightControlFlowUtil.isReassigned(variable, new HashMap<>())) {
|
||||
PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Make 'i' not final" "false"
|
||||
class Main {
|
||||
void foo(Object obj) {
|
||||
final int i = 41;
|
||||
switch (obj) {
|
||||
case String s when s.length() == ++i<caret> -> {}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Transform 'i' into final one element array" "true-preview"
|
||||
class Main {
|
||||
void test() {
|
||||
final int[] i = {42};
|
||||
Runnable runnable1 = () -> System.out.println(i[0]);
|
||||
Runnable runnable2 = () -> System.out.println(i[0]++);
|
||||
i[0] = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// "Copy 'i' to effectively final temp variable" "true-preview"
|
||||
class Main {
|
||||
void foo(Object obj) {
|
||||
int i = 42;
|
||||
int finalI = i;
|
||||
switch (obj) {
|
||||
case String s when switch ((Object) s.length()) {
|
||||
case Integer integer when integer == finalI -> 0;
|
||||
default -> 42;
|
||||
} == 42 -> {}
|
||||
default -> {}
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Transform 'i' into final one element array" "true-preview"
|
||||
class Main {
|
||||
void foo(Object obj) {
|
||||
final int[] i = {42};
|
||||
switch (obj) {
|
||||
case String s when switch ((Object) s.length()) {
|
||||
case Integer integer when integer == ++i[0] -> 0;
|
||||
default -> 42;
|
||||
} == 42 -> {}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Copy 'i' to effectively final temp variable" "true-preview"
|
||||
class Main {
|
||||
void foo(int i) {
|
||||
int finalI = i;
|
||||
switch (i) {
|
||||
case 42 -> () -> System.out.println(finalI)
|
||||
default -> {}
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Transform 'i' into final one element array" "true-preview"
|
||||
class Main {
|
||||
void test() {
|
||||
int i = 42;
|
||||
Runnable runnable1 = () -> System.out.println(i);
|
||||
Runnable runnable2 = () -> System.out.println(i<caret>++);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Copy 'i' to effectively final temp variable" "true-preview"
|
||||
class Main {
|
||||
void foo(Object obj) {
|
||||
int i = 42;
|
||||
switch (obj) {
|
||||
case String s when switch ((Object) s.length()) {
|
||||
case Integer integer when integer == i<caret> -> 0;
|
||||
default -> 42;
|
||||
} == 42 -> {}
|
||||
default -> {}
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Transform 'i' into final one element array" "true-preview"
|
||||
class Main {
|
||||
void foo(Object obj) {
|
||||
int i = 42;
|
||||
switch (obj) {
|
||||
case String s when switch ((Object) s.length()) {
|
||||
case Integer integer when integer == ++i<caret> -> 0;
|
||||
default -> 42;
|
||||
} == 42 -> {}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Copy 'i' to effectively final temp variable" "true-preview"
|
||||
class Main {
|
||||
void foo(int i) {
|
||||
switch (i) {
|
||||
case 42 -> () -> System.out.println(i<caret>)
|
||||
default -> {}
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
@@ -58,8 +58,8 @@ public final class FinalUtils {
|
||||
if (ControlFlowUtil.isVariableAssignedInLoop(ref, variable)) return false;
|
||||
if (variable instanceof PsiField) {
|
||||
if (PsiUtil.findEnclosingConstructorOrInitializer(ref) == null) return false;
|
||||
PsiElement innerClass = HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, ref);
|
||||
if (innerClass != null && innerClass != ((PsiField)variable).getContainingClass()) return false;
|
||||
PsiElement innerScope = HighlightControlFlowUtil.getElementVariableReferencedFrom(variable, ref);
|
||||
if (innerScope != null && innerScope != ((PsiField)variable).getContainingClass()) return false;
|
||||
}
|
||||
return HighlightControlFlowUtil.checkFinalVariableMightAlreadyHaveBeenAssignedTo(variable, ref, finalVarProblems) == null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user