[java-highlighting] More control-flow-related stuff migrated

Public methods from HighlightControlFlowUtil moved to ControlFlowUtil
Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: 64fa78391b02f9dec3d9f2410e764c8b8e479ea6
This commit is contained in:
Tagir Valeev
2025-02-05 11:59:05 +00:00
committed by intellij-monorepo-bot
parent be8d051ff5
commit a98ba9ff92
43 changed files with 345 additions and 290 deletions
@@ -118,6 +118,7 @@ class.inheritance.raw.and.generic=''{0}'' cannot be inherited as a raw type and
class.inheritance.method.clash=Methods {0} from {1} and {2} from {3} are inherited with the same signature
class.not.accessible=Cannot access {0}
class.generic.extends.exception=Generic class may not extend 'java.lang.Throwable'
class.initializer.must.complete.normally=Initializer must be able to complete normally
class.implicit.no.main.method=Implicitly declared class contains no 'main' method
class.implicit.invalid.file.name=The file name of an implicitly declared class is not a valid identifier
@@ -393,6 +394,7 @@ expression.class.type.parameter=Cannot access class object of a type parameter
expression.class.parameterized.type=Cannot access class object of parameterized type
assignment.declared.outside.guard=Cannot assign a value to variable ''{0}'', because it is declared outside the guard
assignment.to.final.variable=Cannot assign a value to final variable ''{0}''
lvalue.variable.expected=Variable expected
binary.operator.not.applicable=Operator ''{0}'' cannot be applied to ''{1}'', ''{2}''
@@ -4,7 +4,13 @@ package com.intellij.java.codeserver.highlighting;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.util.JavaPsiRecordUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.BitUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -71,4 +77,115 @@ final class ControlFlowChecker {
// incomplete code
}
}
void checkInitializerCompleteNormally(@NotNull PsiClassInitializer initializer) {
PsiCodeBlock body = initializer.getBody();
// unhandled exceptions already reported
try {
ControlFlow controlFlow = ControlFlowFactory.getControlFlowNoConstantEvaluate(body);
int completionReasons = ControlFlowUtil.getCompletionReasons(controlFlow, 0, controlFlow.getSize());
if (!BitUtil.isSet(completionReasons, ControlFlowUtil.NORMAL_COMPLETION_REASON)) {
myVisitor.report(JavaErrorKinds.CLASS_INITIALIZER_MUST_COMPLETE_NORMALLY.create(body));
}
}
catch (AnalysisCanceledException e) {
// incomplete code
}
}
/**
* @return field that has initializer with this element as subexpression or null if not found
*/
static PsiField findEnclosingFieldInitializer(@NotNull PsiElement entry) {
PsiElement element = entry;
while (element != null) {
PsiElement parent = element.getParent();
if (parent instanceof PsiField field) {
if (element == field.getInitializer()) return field;
if (field instanceof PsiEnumConstant enumConstant && element == enumConstant.getArgumentList()) return field;
}
if (element instanceof PsiClass || element instanceof PsiMethod) return null;
element = parent;
}
return null;
}
private static boolean isSameField(@NotNull PsiMember enclosingCtrOrInitializer,
@NotNull PsiField field,
@NotNull PsiReferenceExpression reference,
@NotNull PsiFile containingFile) {
if (!containingFile.getManager().areElementsEquivalent(enclosingCtrOrInitializer.getContainingClass(), field.getContainingClass())) return false;
return LocalsOrMyInstanceFieldsControlFlowPolicy.isLocalOrMyInstanceReference(reference);
}
private static boolean canWriteToFinal(@NotNull PsiVariable variable,
@NotNull PsiExpression expression,
@NotNull PsiReferenceExpression reference,
@NotNull PsiFile containingFile) {
if (variable.hasInitializer()) {
return variable instanceof PsiField field && !PsiAugmentProvider.canTrustFieldInitializer(field);
}
if (variable instanceof PsiParameter) return false;
PsiElement scope = ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, expression);
if (variable instanceof PsiField field) {
// if inside some field initializer
if (findEnclosingFieldInitializer(expression) != null) return true;
PsiClass containingClass = field.getContainingClass();
if (containingClass == null) return true;
// assignment from within inner class is illegal always
if (scope != null && !containingFile.getManager().areElementsEquivalent(scope, containingClass)) return false;
PsiMember enclosingCtrOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expression);
return enclosingCtrOrInitializer != null &&
!(enclosingCtrOrInitializer instanceof PsiMethod method &&
JavaPsiRecordUtil.isCompactConstructor(method) &&
containingClass.isRecord()) &&
isSameField(enclosingCtrOrInitializer, field, reference, containingFile);
}
if (variable instanceof PsiLocalVariable) {
boolean isAccessedFromOtherClass = scope != null;
return !isAccessedFromOtherClass;
}
return true;
}
private static boolean hasWriteToFinalInsideLambda(@NotNull PsiVariable variable, @NotNull PsiJavaCodeReferenceElement context) {
return hasWriteToFinalInsideLambda(variable, PsiTreeUtil.getParentOfType(context, PsiLambdaExpression.class), context);
}
@Contract("_, null, _ -> false")
private static boolean hasWriteToFinalInsideLambda(@NotNull PsiVariable variable,
@Nullable PsiLambdaExpression lambdaExpression,
@NotNull PsiJavaCodeReferenceElement context) {
if (lambdaExpression == null) return false;
if (!PsiTreeUtil.isAncestor(lambdaExpression, variable, true)) {
PsiElement parent = variable.getParent();
if (parent instanceof PsiParameterList && parent.getParent() == lambdaExpression) {
return false;
}
PsiSwitchLabelStatementBase label =
PsiTreeUtil.getParentOfType(context, PsiSwitchLabelStatementBase.class, true, PsiLambdaExpression.class);
if (label != null && PsiTreeUtil.isAncestor(label.getGuardExpression(), context, false)) {
return false;
}
return !ControlFlowUtil.isEffectivelyFinal(variable, lambdaExpression, context);
}
return false;
}
void checkCannotWriteToFinal(@NotNull PsiExpression expression) {
PsiExpression operand = null;
if (expression instanceof PsiAssignmentExpression assignment) {
operand = assignment.getLExpression();
}
else if (PsiUtil.isIncrementDecrementOperation(expression)) {
operand = ((PsiUnaryExpression)expression).getOperand();
}
if (!(PsiUtil.skipParenthesizedExprDown(operand) instanceof PsiReferenceExpression reference)) return;
if (!(reference.resolve() instanceof PsiVariable variable)) return;
if (!variable.hasModifierProperty(PsiModifier.FINAL)) return;
boolean canWrite = canWriteToFinal(variable, expression, reference, myVisitor.file()) &&
!hasWriteToFinalInsideLambda(variable, reference);
if (canWrite) return;
myVisitor.report(JavaErrorKinds.ASSIGNMENT_TO_FINAL_VARIABLE.create(reference, variable));
}
}
@@ -486,6 +486,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) myClassChecker.checkThingNotAllowedInInterface(initializer);
if (!hasErrorResults()) myClassChecker.checkInitializersInImplicitClass(initializer);
if (!hasErrorResults()) myControlFlowChecker.checkUnreachableStatement(initializer.getBody());
if (!hasErrorResults()) myControlFlowChecker.checkInitializerCompleteNormally(initializer);
}
@Override
@@ -1059,6 +1060,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) myStatementChecker.checkForeachExpressionTypeIsIterable(expression);
if (!hasErrorResults()) myExpressionChecker.checkVariableExpected(expression);
if (!hasErrorResults()) myExpressionChecker.checkConditionalExpressionBranchTypesMatch(expression, type);
if (!hasErrorResults()) myControlFlowChecker.checkCannotWriteToFinal(expression);
}
@Override
@@ -390,6 +390,8 @@ public final class JavaErrorKinds {
parameterized(PsiElement.class, PsiClass.class, "class.not.accessible")
.withRange((psi, cls) -> psi instanceof PsiMember member ? getMemberDeclarationTextRange(member) : null)
.withRawDescription((psi, cls) -> message("class.not.accessible", formatClass(cls)));
public static final Simple<PsiCodeBlock> CLASS_INITIALIZER_MUST_COMPLETE_NORMALLY =
error("class.initializer.must.complete.normally");
public static final Simple<PsiJavaCodeReferenceElement> VALUE_CLASS_EXTENDS_NON_ABSTRACT = error("value.class.extends.non.abstract");
@@ -864,6 +866,9 @@ public final class JavaErrorKinds {
public static final Parameterized<PsiExpression, PsiVariable> ASSIGNMENT_DECLARED_OUTSIDE_GUARD =
parameterized(PsiExpression.class, PsiVariable.class, "assignment.declared.outside.guard")
.withRawDescription((expr, variable) -> message("assignment.declared.outside.guard", variable.getName()));
public static final Parameterized<PsiReferenceExpression, PsiVariable> ASSIGNMENT_TO_FINAL_VARIABLE =
parameterized(PsiReferenceExpression.class, PsiVariable.class, "assignment.to.final.variable")
.withRawDescription((expr, variable) -> message("assignment.to.final.variable", variable.getName()));
public static final Simple<PsiExpression> LVALUE_VARIABLE_EXPECTED = error("lvalue.variable.expected");
public static final Parameterized<PsiJavaToken, JavaIncompatibleTypeErrorContext> BINARY_OPERATOR_NOT_APPLICABLE =
@@ -13,7 +13,6 @@ import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.util.Predicates;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
@@ -25,9 +24,7 @@ import com.intellij.psi.impl.light.LightRecordField;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.*;
import com.intellij.util.BitUtil;
import com.intellij.util.JavaPsiConstructorUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.VariableAccessUtils;
@@ -131,7 +128,7 @@ public final class HighlightControlFlowUtil {
/**
* @return field that has initializer with this element as subexpression or null if not found
*/
static PsiField findEnclosingFieldInitializer(@NotNull PsiElement entry) {
private static PsiField findEnclosingFieldInitializer(@NotNull PsiElement entry) {
PsiElement element = entry;
while (element != null) {
PsiElement parent = element.getParent();
@@ -579,78 +576,11 @@ public final class HighlightControlFlowUtil {
}
static HighlightInfo.Builder checkCannotWriteToFinal(@NotNull PsiExpression expression, @NotNull PsiFile containingFile) {
PsiExpression operand = null;
if (expression instanceof PsiAssignmentExpression assignment) {
operand = assignment.getLExpression();
}
else if (PsiUtil.isIncrementDecrementOperation(expression)) {
operand = ((PsiUnaryExpression)expression).getOperand();
}
PsiReferenceExpression reference = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(operand), PsiReferenceExpression.class);
PsiVariable variable = reference == null ? null : ObjectUtils.tryCast(reference.resolve(), PsiVariable.class);
if (variable == null || !variable.hasModifierProperty(PsiModifier.FINAL)) return null;
boolean canWrite = canWriteToFinal(variable, expression, reference, containingFile) && checkWriteToFinalInsideLambda(variable, reference) == null;
if (canWrite) return null;
String name = variable.getName();
String description = JavaErrorBundle.message("assignment.to.final.variable", name);
HighlightInfo.Builder highlightInfo =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(reference).descriptionAndTooltip(description);
PsiElement scope = getElementVariableReferencedFrom(variable, expression);
if (scope == null || variable instanceof PsiField) {
HighlightFixUtil.registerMakeNotFinalAction(variable, highlightInfo);
}
else {
IntentionAction action = getQuickFixFactory().createVariableAccessFromInnerClassFix(variable, scope);
highlightInfo.registerFix(action, null, null, null, null);
}
return highlightInfo;
}
private static boolean canWriteToFinal(@NotNull PsiVariable variable,
@NotNull PsiExpression expression,
@NotNull PsiReferenceExpression reference,
@NotNull PsiFile containingFile) {
if (variable.hasInitializer()) {
return variable instanceof PsiField field && !PsiAugmentProvider.canTrustFieldInitializer(field);
}
if (variable instanceof PsiParameter) return false;
PsiElement scope = getElementVariableReferencedFrom(variable, expression);
if (variable instanceof PsiField field) {
// if inside some field initializer
if (findEnclosingFieldInitializer(expression) != null) return true;
PsiClass containingClass = field.getContainingClass();
if (containingClass == null) return true;
// assignment from within inner class is illegal always
if (scope != null && !containingFile.getManager().areElementsEquivalent(scope, containingClass)) return false;
PsiMember enclosingCtrOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expression);
return enclosingCtrOrInitializer != null &&
!(enclosingCtrOrInitializer instanceof PsiMethod method &&
JavaPsiRecordUtil.isCompactConstructor(method) &&
containingClass.isRecord()) &&
isSameField(enclosingCtrOrInitializer, field, reference, containingFile);
}
if (variable instanceof PsiLocalVariable) {
boolean isAccessedFromOtherClass = scope != null;
return !isAccessedFromOtherClass;
}
return true;
}
private static boolean isSameField(@NotNull PsiMember enclosingCtrOrInitializer,
@NotNull PsiField field,
@NotNull PsiReferenceExpression reference,
@NotNull PsiFile containingFile) {
if (!containingFile.getManager().areElementsEquivalent(enclosingCtrOrInitializer.getContainingClass(), field.getContainingClass())) return false;
return LocalsOrMyInstanceFieldsControlFlowPolicy.isLocalOrMyInstanceReference(reference);
}
static HighlightInfo.Builder checkVariableMustBeFinal(@NotNull PsiVariable variable,
@NotNull PsiJavaCodeReferenceElement context,
@NotNull LanguageLevel languageLevel) {
if (variable.hasModifierProperty(PsiModifier.FINAL)) return null;
PsiElement scope = getElementVariableReferencedFrom(variable, context);
PsiElement scope = ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, context);
if (scope instanceof PsiClass) {
if (variable instanceof PsiParameter parameter) {
PsiElement parent = variable.getParent();
@@ -660,7 +590,7 @@ public final class HighlightControlFlowUtil {
}
}
boolean isToBeEffectivelyFinal = JavaFeature.EFFECTIVELY_FINAL.isSufficient(languageLevel);
if (isToBeEffectivelyFinal && isEffectivelyFinal(variable, scope, context)) {
if (isToBeEffectivelyFinal && ControlFlowUtil.isEffectivelyFinal(variable, scope, context)) {
return null;
}
String description = JavaErrorBundle
@@ -701,7 +631,7 @@ public final class HighlightControlFlowUtil {
@NotNull PsiJavaCodeReferenceElement context,
@NotNull PsiElement scope,
@NotNull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String messageKey) {
if (!isEffectivelyFinal(variable, scope, context)) {
if (!ControlFlowUtil.isEffectivelyFinal(variable, scope, context)) {
String text = JavaErrorBundle.message(messageKey);
HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(context).descriptionAndTooltip(text);
IntentionAction action1 = getQuickFixFactory().createVariableAccessFromInnerClassFix(variable, scope);
@@ -735,111 +665,6 @@ public final class HighlightControlFlowUtil {
return null;
}
public static boolean isEffectivelyFinal(@NotNull PsiVariable variable, @NotNull PsiElement scope, @Nullable PsiJavaCodeReferenceElement context) {
boolean effectivelyFinal;
if (variable instanceof PsiParameter parameter) {
effectivelyFinal = !VariableAccessUtils.variableIsAssigned(variable, parameter.getDeclarationScope());
}
else {
PsiElement codeBlock = PsiUtil.getVariableCodeBlock(variable, context);
ControlFlow controlFlow;
try {
if (codeBlock == null) return true;
controlFlow = getControlFlow(codeBlock);
}
catch (AnalysisCanceledException e) {
return true;
}
Collection<ControlFlowUtil.VariableInfo> initializedTwice = ControlFlowUtil.getInitializedTwice(controlFlow);
effectivelyFinal = !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null));
if (effectivelyFinal) {
for (PsiReferenceExpression expression : ControlFlowUtil.getReadBeforeWriteLocals(controlFlow)) {
if (expression.resolve() == variable) {
return PsiUtil.isAccessedForReading(expression);
}
}
effectivelyFinal = !VariableAccessUtils.variableIsAssigned(variable, scope);
if (effectivelyFinal) {
Ref<Boolean> stopped = new Ref<>(false);
codeBlock.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
if (expression.isReferenceTo(variable) &&
PsiUtil.isAccessedForWriting(expression) &&
ControlFlowUtil.isVariableAssignedInLoop(expression, variable)) {
stopWalking();
stopped.set(true);
}
}
});
return !stopped.get();
}
}
}
return effectivelyFinal;
}
/**
* @param variable variable
* @param context the context that reference to the variable
* @return inner class, lambda expression, or switch label that refers to the variable
*/
public static @Nullable PsiElement getElementVariableReferencedFrom(@NotNull PsiVariable variable, @NotNull PsiElement context) {
PsiElement[] scope;
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 parameter) {
scope = new PsiElement[]{parameter.getDeclarationScope()};
}
else {
scope = new PsiElement[]{variable.getParent()};
}
if (scope.length < 1 || scope[0] == null || scope[0].getContainingFile() != context.getContainingFile()) return null;
PsiElement parent = context.getParent();
PsiElement prevParent = context;
outer:
while (parent != null) {
for (PsiElement scopeElement : scope) {
if (parent.equals(scopeElement)) break outer;
}
if (parent instanceof PsiClass && !(prevParent instanceof PsiExpressionList && parent instanceof PsiAnonymousClass)) {
return parent;
}
if (parent instanceof PsiLambdaExpression) {
return parent;
}
if (parent instanceof PsiSwitchLabelStatementBase label && label.getGuardExpression() == prevParent) {
return parent;
}
prevParent = parent;
parent = parent.getParent();
}
return null;
}
static HighlightInfo.Builder checkInitializerCompleteNormally(@NotNull PsiClassInitializer initializer) {
PsiCodeBlock body = initializer.getBody();
// unhandled exceptions already reported
try {
ControlFlow controlFlow = ControlFlowFactory.getControlFlowNoConstantEvaluate(body);
int completionReasons = ControlFlowUtil.getCompletionReasons(controlFlow, 0, controlFlow.getSize());
if (!BitUtil.isSet(completionReasons, ControlFlowUtil.NORMAL_COMPLETION_REASON)) {
String description = JavaErrorBundle.message("initializer.must.be.able.to.complete.normally");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(body).descriptionAndTooltip(description);
}
}
catch (AnalysisCanceledException e) {
// incomplete code
}
return null;
}
/**
* A kind of final variable problem returned from {@link #getFinalVariableProblemsInBlock(Map, PsiElement)}
* which designates a final variable which is initialized in a loop.
@@ -31,6 +31,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.IncompleteModelUtil;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.scope.PatternResolveState;
@@ -417,7 +418,7 @@ public final class HighlightUtil {
PsiModifierList modifierList = variable.getModifierList();
if (modifierList != null && modifierList.hasModifierProperty(PsiModifier.FINAL)) return null;
if (!(variable instanceof PsiField) && HighlightControlFlowUtil.isEffectivelyFinal(variable, resource, ref)) {
if (!(variable instanceof PsiField) && ControlFlowUtil.isEffectivelyFinal(variable, resource)) {
return null;
}
}
@@ -275,18 +275,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!hasErrorResults()) GenericsHighlightUtil.checkTypeParameterOverrideEquivalentMethods(aClass, myLanguageLevel, myErrorSink, myOverrideEquivalentMethodsVisitedClasses, myOverrideEquivalentMethodsErrors);
}
@Override
public void visitClassInitializer(@NotNull PsiClassInitializer initializer) {
super.visitClassInitializer(initializer);
if (!hasErrorResults()) add(HighlightControlFlowUtil.checkInitializerCompleteNormally(initializer));
}
@Override
public void visitExpression(@NotNull PsiExpression expression) {
super.visitExpression(expression);
if (!hasErrorResults()) add(HighlightControlFlowUtil.checkCannotWriteToFinal(expression, myFile));
}
@Override
public void visitField(@NotNull PsiField field) {
super.visitField(field);
@@ -27,6 +27,7 @@ import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.light.LightRecordMethod;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.infos.MethodCandidateInfo;
@@ -550,6 +551,13 @@ final class JavaErrorFixProvider {
fix(METHOD_REFERENCE_STATIC_METHOD_NON_STATIC_QUALIFIER, error -> removeModifierFix(error.context(), PsiModifier.STATIC));
fix(METHOD_REFERENCE_STATIC_METHOD_RECEIVER, error -> removeModifierFix(error.context(), PsiModifier.STATIC));
fix(METHOD_REFERENCE_NON_STATIC_METHOD_IN_STATIC_CONTEXT, error -> addModifierFix(error.context(), PsiModifier.STATIC));
fix(ASSIGNMENT_TO_FINAL_VARIABLE, error -> {
PsiVariable variable = error.context();
PsiElement scope = ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, error.psi());
return scope == null || variable instanceof PsiField
? removeModifierFix(variable, PsiModifier.FINAL)
: myFactory.createVariableAccessFromInnerClassFix(variable, scope);
});
}
private void createAccessFixes() {
@@ -5,12 +5,12 @@ import com.intellij.codeInsight.JavaPsiEquivalenceUtil;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
@@ -141,7 +141,7 @@ public final class NullabilityUtil {
PsiElement block = PsiUtil.getVariableCodeBlock((PsiVariable)target, null);
// Do not trust the declared nullability of local variable/parameter if it's reassigned as nullability designates
// only initial nullability
if (block == null || !HighlightControlFlowUtil.isEffectivelyFinal((PsiVariable)target, block, ref)) return Nullability.UNKNOWN;
if (block == null || !ControlFlowUtil.isEffectivelyFinal((PsiVariable)target, block)) return Nullability.UNKNOWN;
}
return DfaPsiUtil.getElementNullabilityIgnoringParameterInference(expression.getType(), (PsiModifierListOwner)target);
}
@@ -2,8 +2,8 @@
package com.intellij.codeInspection.util;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Contract;
@@ -102,7 +102,7 @@ public final class LambdaGenerationUtil {
if (variable instanceof PsiField) {
return !variable.hasModifierProperty(PsiModifier.FINAL) || !PsiUtil.isAccessedForWriting(expression);
}
return !PsiUtil.isAccessedForWriting(expression) && HighlightControlFlowUtil.isEffectivelyFinal(variable, myRoot, null);
return !PsiUtil.isAccessedForWriting(expression) && ControlFlowUtil.isEffectivelyFinal(variable, myRoot);
}
@Override
@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.siyeh.ig.bugs;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.dataFlow.ContractReturnValue;
import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
@@ -88,7 +87,7 @@ public final class NewObjectEqualityInspection extends BaseInspection {
PsiElement block = ControlFlowUtil.findCodeFragment(variable);
PsiElement expressionContext = PsiTreeUtil.getParentOfType(expression, PsiMember.class, PsiLambdaExpression.class);
if (expressionContext == null || PsiTreeUtil.isAncestor(block, expressionContext, true)) return expression;
if (!HighlightControlFlowUtil.isEffectivelyFinal(variable, block, null)) return expression;
if (!ControlFlowUtil.isEffectivelyFinal(variable, block)) return expression;
ControlFlow flow;
try {
flow = ControlFlowFactory.getInstance(block.getProject()).getControlFlow(block, new LocalsControlFlowPolicy(block), false);
@@ -34,8 +34,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import static com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil.isEffectivelyFinal;
import static com.intellij.psi.JavaTokenType.*;
import static com.intellij.psi.controlFlow.ControlFlowUtil.isEffectivelyFinal;
import static com.intellij.psi.util.PsiUtil.skipParenthesizedExprDown;
import static com.intellij.psi.util.PsiUtil.skipParenthesizedExprUp;
import static com.intellij.util.ObjectUtils.tryCast;
@@ -185,8 +185,8 @@ public final class NumberEqualityInspection extends BaseInspection {
final PsiVariable rVariable = tryCast(rReference.resolve(), PsiVariable.class);
if (rVariable == null) return false;
final boolean isEffectivelyFinal = isEffectivelyFinal(lVariable, elementContainsNumberEquality, null) &&
isEffectivelyFinal(rVariable, elementContainsNumberEquality, null);
final boolean isEffectivelyFinal = isEffectivelyFinal(lVariable, elementContainsNumberEquality) &&
isEffectivelyFinal(rVariable, elementContainsNumberEquality);
if (!isEffectivelyFinal) return false;
return (checker.expressionsMatch(lhs, lValue).isExactMatch() && checker.expressionsMatch(rhs, rValue).isExactMatch()) ||
@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.siyeh.ig.migration;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.SuppressionUtilCore;
@@ -13,6 +12,7 @@ import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.search.LocalSearchScope;
@@ -318,7 +318,7 @@ public final class TryWithIdenticalCatchesInspection extends BaseInspection {
final PsiCodeBlock codeBlock = catchSection.getCatchBlock();
if (parameter != null && codeBlock != null) {
final List<PsiClassType> types = getClassTypes(parameter.getType());
if (types != null && HighlightControlFlowUtil.isEffectivelyFinal(parameter, codeBlock, null)) {
if (types != null && ControlFlowUtil.isEffectivelyFinal(parameter, codeBlock)) {
final DuplicatesFinder finder = buildDuplicatesFinder(codeBlock, parameter);
return new CatchSectionWrapper(catchSection, codeBlock, parameter, types, finder);
}
@@ -15,10 +15,10 @@
*/
package com.siyeh.ig.numeric;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.dataFlow.CommonDataflow;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.ConstantExpressionUtil;
import com.intellij.psi.util.PsiTreeUtil;
@@ -92,7 +92,7 @@ public final class BadOddnessInspection extends BaseInspection {
if (variable == null) return false;
PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
if (codeBlock == null) return false;
return !HighlightControlFlowUtil.isEffectivelyFinal(variable, codeBlock, null);
return !ControlFlowUtil.isEffectivelyFinal(variable, codeBlock);
}
private static boolean hasValue(PsiExpression expression, int testValue) {
@@ -2,7 +2,6 @@
package com.siyeh.ig.performance;
import com.intellij.codeInsight.BlockUtils;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.CleanupLocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.modcommand.ModPsiUpdater;
@@ -10,6 +9,7 @@ import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
@@ -184,7 +184,7 @@ public final class IfStatementMissingBreakInLoopInspection extends BaseInspectio
for (PsiElement element : declaration.getDeclaredElements()) {
if (!(element instanceof PsiVariable variable)) continue;
declaredVariables.add(variable);
if (hasNonFinalVariables || !HighlightControlFlowUtil.isEffectivelyFinal(variable, scope, null)) {
if (hasNonFinalVariables || !ControlFlowUtil.isEffectivelyFinal(variable, scope)) {
nonFinalVariables.add(variable);
}
}
@@ -3,7 +3,6 @@ package com.siyeh.ig.psiutils;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.CodeInsightUtilCore;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.dataFlow.ContractReturnValue;
import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
import com.intellij.codeInspection.dataFlow.MutationSignature;
@@ -12,6 +11,7 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.PsiFieldImpl;
import com.intellij.psi.impl.source.tree.Factory;
import com.intellij.psi.impl.source.tree.TreeElement;
@@ -1683,7 +1683,7 @@ public final class ExpressionUtils {
if (target instanceof PsiLocalVariable || target instanceof PsiParameter) {
PsiVariable var = (PsiVariable)target;
if (var.hasModifierProperty(PsiModifier.FINAL) ||
HighlightControlFlowUtil.isEffectivelyFinal(var, PsiUtil.getVariableCodeBlock(var, null), null)) {
ControlFlowUtil.isEffectivelyFinal(var, PsiUtil.getVariableCodeBlock(var, null))) {
continue;
}
}
@@ -65,7 +65,7 @@ public final class FinalUtils {
if (ControlFlowUtil.isVariableAssignedInLoop(ref, variable)) return false;
if (variable instanceof PsiField) {
if (PsiUtil.findEnclosingConstructorOrInitializer(ref) == null) return false;
PsiElement innerScope = HighlightControlFlowUtil.getElementVariableReferencedFrom(variable, ref);
PsiElement innerScope = ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, ref);
if (innerScope != null && innerScope != ((PsiField)variable).getContainingClass()) return false;
}
HighlightInfo.Builder random =
@@ -15,11 +15,11 @@
*/
package com.siyeh.ig.psiutils;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.daemon.impl.analysis.LocalRefUseInfo;
import com.intellij.openapi.util.Comparing;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
@@ -526,8 +526,8 @@ public final class VariableAccessUtils {
final boolean finalVariableIntroduction =
!initialization.hasModifierProperty(PsiModifier.FINAL) && variable.hasModifierProperty(PsiModifier.FINAL) ||
PsiUtil.isAvailable(JavaFeature.EFFECTIVELY_FINAL, initialization) &&
!HighlightControlFlowUtil.isEffectivelyFinal(initialization, containingScope, null) &&
HighlightControlFlowUtil.isEffectivelyFinal(variable, containingScope, null);
!ControlFlowUtil.isEffectivelyFinal(initialization, containingScope) &&
ControlFlowUtil.isEffectivelyFinal(variable, containingScope);
final boolean canCaptureThis = initialization instanceof PsiField && !initialization.hasModifierProperty(PsiModifier.STATIC);
final PsiType variableType = variable.getType();
@@ -1,9 +1,9 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.siyeh.ig.serialization;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.InspectionGadgetsBundle;
@@ -94,7 +94,7 @@ public final class SerializableStoresNonSerializableInspection extends BaseInspe
final PsiVariable variable = (PsiVariable)target;
if (!variable.hasModifierProperty(PsiModifier.FINAL)) {
if (!PsiUtil.isAvailable(JavaFeature.EFFECTIVELY_FINAL, variable) ||
!HighlightControlFlowUtil.isEffectivelyFinal(variable, myClassOrLambda, expression)) {
!ControlFlowUtil.isEffectivelyFinal(variable, myClassOrLambda)) {
// don't warn on uncompilable code.
return;
}
@@ -2,7 +2,6 @@
package com.intellij.codeInspection;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
@@ -12,6 +11,7 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
@@ -158,8 +158,7 @@ public final class BulkFileAttributesReadInspection extends AbstractBaseJavaLoca
super.visitMethodCallExpression(call);
if (!FILE_ATTR_CALL_MATCHER.test(call)) return;
PsiVariable variable = getFileVariable(call);
if (variable == null) return;
if (!HighlightControlFlowUtil.isEffectivelyFinal(variable, myScope, null)) return;
if (variable == null || !ControlFlowUtil.isEffectivelyFinal(variable, myScope)) return;
List<PsiMethodCallExpression> varCalls = myCalls.computeIfAbsent(variable, __ -> new SmartList<>());
varCalls.add(call);
}
@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
@@ -163,9 +162,7 @@ public final class CastCanBeReplacedWithVariableInspection extends AbstractBaseJ
final @NotNull PsiElement scope,
final @NotNull PsiElement start,
final @NotNull PsiElement end) {
if (variable.hasModifierProperty(PsiModifier.FINAL) || HighlightControlFlowUtil.isEffectivelyFinal(variable, scope, null)) {
return false;
}
if (variable.hasModifierProperty(PsiModifier.FINAL) || ControlFlowUtil.isEffectivelyFinal(variable, scope)) return false;
PsiElement broadEnd = getBroadEnd(scope, start, end);
@@ -3,7 +3,6 @@ package com.intellij.codeInspection;
import com.intellij.codeInsight.CodeInsightUtil;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
@@ -224,7 +223,7 @@ public final class IOStreamConstructorInspection extends AbstractBaseJavaLocalIn
if (occurrences.length < 2) return;
// maybe we can reuse already created file.toPath() / Path.of(...) variable
List<PsiVariable> pathVars = Arrays.stream(occurrences).map(o -> findVariableAssignedTo(o))
.filter(var -> var != null && HighlightControlFlowUtil.isEffectivelyFinal(var, toPathConversion, null))
.filter(var -> var != null && ControlFlowUtil.isEffectivelyFinal(var, toPathConversion))
.toList();
if (!pathVars.isEmpty()) {
PsiCodeBlock body = containingMethod.getBody();
@@ -278,7 +277,7 @@ public final class IOStreamConstructorInspection extends AbstractBaseJavaLocalIn
if (!(child instanceof PsiReferenceExpression)) return false;
PsiVariable target = ObjectUtils.tryCast(((PsiReferenceExpression)child).resolve(), PsiVariable.class);
if (!PsiUtil.isJvmLocalVariable(target)) return false;
return HighlightControlFlowUtil.isEffectivelyFinal(target, context, null);
return ControlFlowUtil.isEffectivelyFinal(target, context);
}
private static @Nullable ControlFlow createControlFlow(@NotNull PsiCodeBlock block) {
@@ -3,10 +3,10 @@ package com.intellij.codeInspection;
import com.intellij.codeInsight.ExpressionUtil;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.util.InspectionMessage;
import com.intellij.java.JavaBundle;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
@@ -218,7 +218,7 @@ public final class OverwrittenKeyInspection extends AbstractBaseJavaLocalInspect
}
if (PsiUtil.isJvmLocalVariable(var)) {
PsiElement scope = PsiUtil.getVariableCodeBlock(var, null);
if (scope != null && HighlightControlFlowUtil.isEffectivelyFinal(var, scope, null)) {
if (scope != null && ControlFlowUtil.isEffectivelyFinal(var, scope)) {
return var;
}
}
@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.options.OptPane;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
@@ -11,6 +10,7 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.light.LightRecordMethod;
import com.intellij.psi.impl.source.tree.JavaSharedImplUtil;
import com.intellij.psi.util.*;
@@ -91,7 +91,7 @@ public final class PatternVariableCanBeUsedInspection extends AbstractBaseJavaLo
if (scope == null) return false;
return localVariable.hasModifierProperty(PsiModifier.FINAL) ||
!patternVariable.hasModifierProperty(PsiModifier.FINAL) ||
HighlightControlFlowUtil.isEffectivelyFinal(localVariable, scope, null);
ControlFlowUtil.isEffectivelyFinal(localVariable, scope);
}
private static @Nullable PsiTypeCastExpression getQualifierReferenceExpression(@NotNull PsiMethodCallExpression call) {
@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.intermediaryVariable;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.java.JavaBundle;
@@ -222,7 +221,7 @@ public final class ReturnSeparatedFromComputationInspection extends AbstractBase
if (expression instanceof PsiReferenceExpression) {
PsiVariable variable = ObjectUtils.tryCast(((PsiReferenceExpression)expression).resolve(), PsiVariable.class);
return variable != null && (variable.hasModifierProperty(PsiModifier.FINAL) ||
HighlightControlFlowUtil.isEffectivelyFinal(variable, scope, null));
ControlFlowUtil.isEffectivelyFinal(variable, scope));
}
if (expression instanceof PsiUnaryExpression) {
return ((PsiUnaryExpression)expression).getOperand() instanceof PsiLiteralExpression; // "-1" and "!true"
@@ -3,7 +3,6 @@ package com.intellij.codeInspection.java18api;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
import com.intellij.codeInspection.LambdaCanBeMethodReferenceInspection;
import com.intellij.codeInspection.ProblemsHolder;
@@ -18,6 +17,7 @@ import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.CommonJavaInlineUtil;
@@ -69,7 +69,7 @@ public final class Java8ListReplaceAllInspection extends AbstractBaseJavaLocalIn
if (variable == null) return false;
PsiExpression initializer = variable.getInitializer();
PsiExpression index = container.extractIndexFromGetExpression(initializer);
return ExpressionUtils.isReferenceTo(index, counter) && HighlightControlFlowUtil.isEffectivelyFinal(variable, body, null);
return ExpressionUtils.isReferenceTo(index, counter) && ControlFlowUtil.isEffectivelyFinal(variable, body);
}
private static boolean isMultilineLambda(PsiStatement body, PsiStatement[] statements) {
@@ -172,7 +172,7 @@ public final class Java8ListReplaceAllInspection extends AbstractBaseJavaLocalIn
private static void inlineVariable(PsiLocalVariable variable, PsiStatement body) {
if (variable == null) return;
if (!HighlightControlFlowUtil.isEffectivelyFinal(variable, body, null)) return;
if (!ControlFlowUtil.isEffectivelyFinal(variable, body)) return;
List<PsiReferenceExpression> references = VariableAccessUtils.getVariableReferences(variable, body);
PsiExpression initializer = variable.getInitializer();
if (initializer == null) return;
@@ -26,9 +26,9 @@ import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import static com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil.isEffectivelyFinal;
import static com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.isCallOf;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_CHAR_SEQUENCE;
import static com.intellij.psi.controlFlow.ControlFlowUtil.isEffectivelyFinal;
import static com.intellij.util.ObjectUtils.tryCast;
import static com.siyeh.ig.psiutils.ControlFlowUtils.getInitializerUsageStatus;
import static com.siyeh.ig.psiutils.ExpressionUtils.resolveLocalVariable;
@@ -276,7 +276,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
PsiElement parent = variable.getParent();
PsiExpression initializer = variable.getInitializer();
if(parent == null || initializer == null) return null;
if(!isEffectivelyFinal(variable, parent, null)) return null;
if(!isEffectivelyFinal(variable, parent)) return null;
Object initializerConstant = ExpressionUtils.computeConstantExpression(initializer);
if(initializerConstant == null) return null;
return String.valueOf(initializerConstant);
@@ -2,7 +2,6 @@
package com.intellij.codeInspection.streamMigration;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.intention.impl.StreamRefactoringUtil;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.options.OptPane;
@@ -285,7 +284,7 @@ public final class StreamApiMigrationInspection extends AbstractBaseJavaLocalIns
tb.operations().noneMatch(op -> op.isWriteAllowed(variable, ref));
if (PsiTreeUtil.processElements(block, notAllowedWrite.negate()::test)) return true;
}
return HighlightControlFlowUtil.isEffectivelyFinal(variable, statement, null);
return ControlFlowUtil.isEffectivelyFinal(variable, statement);
}
static String tryUnbox(PsiVariable variable) {
@@ -3,7 +3,6 @@ package com.intellij.refactoring.introduceVariable;
import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.codeInsight.FunctionalInterfaceSuggester;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.navigation.PsiTargetNavigator;
import com.intellij.codeInspection.AnonymousCanBeLambdaInspection;
import com.intellij.java.JavaBundle;
@@ -18,6 +17,7 @@ import com.intellij.openapi.util.NlsContexts;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiUtil;
@@ -278,7 +278,7 @@ public class IntroduceFunctionalVariableHandler extends IntroduceVariableHandler
if (!data.passAsParameter) {
PsiElement scope = PsiUtil.getVariableCodeBlock(data.variable, null);
if (PsiUtil.isAvailable(JavaFeature.EFFECTIVELY_FINAL, data.variable)
? scope != null && !HighlightControlFlowUtil.isEffectivelyFinal(data.variable, scope, null)
? scope != null && !ControlFlowUtil.isEffectivelyFinal(data.variable, scope)
: data.variable.hasModifierProperty(PsiModifier.FINAL)) {
conflicts.putValue(null, JavaBundle.message("introduce.functional.variable.accessibility.conflict", data.name));
}
@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.refactoring.introduceVariable;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
import com.intellij.codeInsight.template.impl.TemplateState;
import com.intellij.java.refactoring.JavaRefactoringBundle;
@@ -17,6 +16,7 @@ import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.scope.processor.VariablesProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
@@ -64,7 +64,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.getElementVariableReferencedFrom(variable, reference.getElement()) == null)) {
.allMatch(reference -> ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, reference.getElement()) == null)) {
vars.add(variable);
}
}
@@ -3,7 +3,6 @@ package com.intellij.refactoring.util;
import com.intellij.codeInsight.BlockUtils;
import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.SimplifyBooleanExpressionFix;
import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
import com.intellij.codeInspection.redundantCast.RemoveRedundantCastUtil;
@@ -848,7 +847,7 @@ public final class InlineUtil implements CommonJavaInlineUtil {
if (declaration == null || declaration.getDeclaredElements().length != 1) return;
PsiModifierList modifiers = target.getModifierList();
if (modifiers != null && modifiers.getAnnotations().length != 0) return;
boolean effectivelyFinal = HighlightControlFlowUtil.isEffectivelyFinal(variable, context, null);
boolean effectivelyFinal = ControlFlowUtil.isEffectivelyFinal(variable, context);
if (!effectivelyFinal && !VariableAccessUtils.canUseAsNonFinal(target)) return;
for (PsiReferenceExpression reference : references) {
@@ -2,13 +2,13 @@
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiUpdateModCommandAction;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.scope.processor.VariablesNotProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.util.PsiUtil;
@@ -16,6 +16,8 @@ import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static java.util.Objects.requireNonNull;
public class ReuseVariableDeclarationFix extends PsiUpdateModCommandAction<PsiLocalVariable> {
public ReuseVariableDeclarationFix(@NotNull PsiLocalVariable variable) {
super(variable);
@@ -50,10 +52,10 @@ public class ReuseVariableDeclarationFix extends PsiUpdateModCommandAction<PsiLo
final PsiElement statement = factory.createStatementFromText(
variable.getName() + " = " +
ExpressionUtils.convertInitializerToExpression(initializer, factory, variable.getType()).getText() + ";", null);
variable.getParent().replace(statement);
PsiExpressionStatement replacement = (PsiExpressionStatement)variable.getParent().replace(statement);
PsiExpression rValue = requireNonNull(((PsiAssignmentExpression)replacement.getExpression()).getRExpression());
if (wasFinal &&
refVariable instanceof PsiLocalVariable &&
HighlightControlFlowUtil.isEffectivelyFinal(refVariable, initializer, null)) {
refVariable instanceof PsiLocalVariable && ControlFlowUtil.isEffectivelyFinal(refVariable, rValue)) {
PsiUtil.setModifierProperty(refVariable, PsiModifier.FINAL, true);
}
}
@@ -293,7 +293,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.getElementVariableReferencedFrom(variable, expression);
PsiElement innerScope = ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, expression);
if (innerScope != null) {
@FixType int thisType = MAKE_FINAL;
@@ -2,9 +2,9 @@
package com.intellij.codeInsight.daemon.impl.quickfix.makefinal;
import com.intellij.codeInsight.BlockUtils;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.java.JavaBundle;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
@@ -83,7 +83,7 @@ final class MoveInitializerToIfBranchFixer implements EffectivelyFinalFixer {
if (initializer instanceof PsiReferenceExpression ref && ref.resolve() instanceof PsiVariable refTarget) {
if (PsiUtil.isJvmLocalVariable(refTarget) && !refTarget.hasModifierProperty(PsiModifier.FINAL)) {
PsiElement block = PsiUtil.getVariableCodeBlock(refTarget, null);
return block == null || !HighlightControlFlowUtil.isEffectivelyFinal(refTarget, block, null);
return block == null || !ControlFlowUtil.isEffectivelyFinal(refTarget, block);
}
}
return false;
@@ -2,12 +2,12 @@
package com.intellij.codeInsight.intention.impl.singlereturn;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.dataFlow.CommonDataflow;
import com.intellij.codeInspection.dataFlow.NullabilityUtil;
import com.intellij.codeInspection.dataFlow.jvm.JvmPsiRangeSetUtil;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
@@ -23,7 +23,7 @@ import java.util.stream.Collectors;
import static com.intellij.util.ObjectUtils.NULL;
import static com.intellij.util.ObjectUtils.tryCast;
import static java.util.Objects.requireNonNull;
import static java.util.Objects.*;
/**
* Represents a way to indicate whether method execution is already finished
@@ -256,7 +256,7 @@ public final class FinishMarker {
if (target instanceof PsiLocalVariable) return false;
if (target instanceof PsiParameter) {
PsiElement block = ((PsiParameter)target).getDeclarationScope();
return block instanceof PsiMethod && HighlightControlFlowUtil.isEffectivelyFinal(target, block, null);
return block instanceof PsiMethod && ControlFlowUtil.isEffectivelyFinal(target, block);
}
}
return true;
@@ -19,6 +19,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.impl.source.PsiFieldImpl;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
@@ -304,7 +305,7 @@ public class VariableLookupItem extends LookupItem<PsiVariable> implements Typed
return;
}
if (HighlightControlFlowUtil.getElementVariableReferencedFrom(variable, place) != null &&
if (ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, place) != null &&
!HighlightControlFlowUtil.isReassigned(variable, new HashMap<>())) {
PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, true);
}
@@ -1,13 +1,13 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.psiutils.CommentTracker;
@@ -73,7 +73,7 @@ public class WrapWithMutableCollectionFix extends PsiUpdateModCommandQuickFix {
if (wrapper == null) return null;
PsiElement block = PsiUtil.getVariableCodeBlock(variable, null);
if (block == null) return null;
if (!HighlightControlFlowUtil.isEffectivelyFinal(variable, block, null)) return null;
if (!ControlFlowUtil.isEffectivelyFinal(variable, block)) return null;
return new WrapWithMutableCollectionFix(variable.getName(), wrapper);
}
@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.siyeh.ig.controlflow;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.dataFlow.CommonDataflow;
@@ -30,6 +29,7 @@ import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiExpressionTrimRenderer;
import com.intellij.psi.util.PsiPrecedenceUtil;
@@ -247,7 +247,7 @@ public final class ConditionCoveredByFurtherConditionInspection extends Abstract
if (psi instanceof PsiPatternVariable) return true;
if (psi instanceof PsiLocalVariable || psi instanceof PsiParameter) {
PsiElement block = PsiUtil.getVariableCodeBlock(psi, null);
return block == null || !HighlightControlFlowUtil.isEffectivelyFinal(psi, block, null);
return block == null || !ControlFlowUtil.isEffectivelyFinal(psi, block);
}
return true;
})
@@ -15,7 +15,6 @@
*/
package com.siyeh.ig.dataflow;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.generation.GenerateMembersUtil;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemHighlightType;
@@ -25,6 +24,7 @@ import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.FileTypeUtils;
@@ -152,8 +152,7 @@ public final class TooBroadScopeInspection extends BaseInspection {
return true;
}
final PsiElement context = PsiUtil.getVariableCodeBlock(variable, referenceExpression);
return context != null && !(variable instanceof PsiField) &&
HighlightControlFlowUtil.isEffectivelyFinal(variable, context, referenceExpression);
return context != null && !(variable instanceof PsiField) && ControlFlowUtil.isEffectivelyFinal(variable, context);
}
if (expression instanceof PsiPolyadicExpression polyadicExpression) {
for (PsiExpression operand : polyadicExpression.getOperands()) {
@@ -2,7 +2,6 @@
package com.siyeh.ig.migration;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.CommonQuickFixBundle;
import com.intellij.codeInspection.EnhancedSwitchMigrationInspection;
import com.intellij.codeInspection.LocalQuickFix;
@@ -18,6 +17,7 @@ import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.tree.java.PsiEmptyStatementImpl;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.JavaPsiPatternUtil;
@@ -257,7 +257,7 @@ public final class IfCanBeSwitchInspection extends BaseInspection {
if (initializer != castExpression) return null;
PsiElement scope = PsiUtil.getVariableCodeBlock(variable, null);
if (scope == null) return null;
if (!HighlightControlFlowUtil.isEffectivelyFinal(variable, scope, null)) return null;
if (!ControlFlowUtil.isEffectivelyFinal(variable, scope)) return null;
return variable;
}
@@ -4,16 +4,14 @@ package com.intellij.psi.controlFlow;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.ExpressionUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -23,7 +21,123 @@ import java.util.function.IntFunction;
public final class ControlFlowUtil {
private static final Logger LOG = Logger.getInstance(ControlFlowUtil.class);
private static class SSAInstructionState implements Cloneable {
/**
* @param variable variable
* @param context the context that references to the variable
* @return the scope around context that enforces variable to be effectively final. Currently, it could be
* an inner class, lambda expression, or switch guard. Returns null if there's no such scope,
* or the variable declaration is within the same scope, so it should not be effectively final.
* Note that if null is returned, it doesn't mean that the variable could be modified, as another reference from
* another place might exist.
*/
public static @Nullable PsiElement getScopeEnforcingEffectiveFinality(@NotNull PsiVariable variable, @NotNull PsiElement context) {
PsiElement[] scope;
if (variable instanceof PsiResourceVariable) {
scope = ((PsiResourceVariable)variable).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 {
scope = new PsiElement[]{variable.getParent()};
}
if (scope.length < 1 || scope[0] == null || scope[0].getContainingFile() != context.getContainingFile()) return null;
PsiElement parent = context.getParent();
PsiElement prevParent = context;
outer:
while (parent != null) {
for (PsiElement scopeElement : scope) {
if (parent.equals(scopeElement)) break outer;
}
if (parent instanceof PsiClass && !(prevParent instanceof PsiExpressionList && parent instanceof PsiAnonymousClass)) {
return parent;
}
if (parent instanceof PsiLambdaExpression) {
return parent;
}
if (parent instanceof PsiSwitchLabelStatementBase && ((PsiSwitchLabelStatementBase)parent).getGuardExpression() == prevParent) {
return parent;
}
prevParent = parent;
parent = parent.getParent();
}
return null;
}
/**
* @param variable variable to check
* @param scope variable scope
* @return true if the variable is effectively final
*/
public static boolean isEffectivelyFinal(@NotNull PsiVariable variable, @NotNull PsiElement scope) {
return isEffectivelyFinal(variable, scope, null);
}
/**
* @param variable variable to check
* @param scope variable scope
* @param context context element (actual for fields, ignored for local variables and parameters)
* @return true if the variable is effectively final
*/
public static boolean isEffectivelyFinal(@NotNull PsiVariable variable, @NotNull PsiElement scope, @Nullable PsiJavaCodeReferenceElement context) {
boolean effectivelyFinal;
if (variable instanceof PsiParameter) {
effectivelyFinal = !variableIsAssigned(variable, ((PsiParameter)variable).getDeclarationScope());
}
else {
PsiElement codeBlock = PsiUtil.getVariableCodeBlock(variable, context);
ControlFlow controlFlow;
try {
if (codeBlock == null) return true;
LocalsOrMyInstanceFieldsControlFlowPolicy policy = LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance();
controlFlow = ControlFlowFactory.getControlFlow(
codeBlock, policy, ControlFlowOptions.create(true, true, true));
}
catch (AnalysisCanceledException e) {
return true;
}
Collection<VariableInfo> initializedTwice = getInitializedTwice(controlFlow);
effectivelyFinal = !initializedTwice.contains(new VariableInfo(variable, null));
if (effectivelyFinal) {
for (PsiReferenceExpression expression : getReadBeforeWriteLocals(controlFlow)) {
if (expression.resolve() == variable) {
return PsiUtil.isAccessedForReading(expression);
}
}
effectivelyFinal = !variableIsAssigned(variable, scope);
if (effectivelyFinal) {
// TODO: check; probably this traversal is redundant
Ref<Boolean> stopped = new Ref<>(false);
codeBlock.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
if (expression.isReferenceTo(variable) &&
PsiUtil.isAccessedForWriting(expression) &&
isVariableAssignedInLoop(expression, variable)) {
stopWalking();
stopped.set(true);
}
}
});
return !stopped.get();
}
}
}
return effectivelyFinal;
}
private static boolean variableIsAssigned(@NotNull PsiVariable variable, @NotNull PsiElement scope) {
return !PsiTreeUtil.processElements(scope, PsiReferenceExpression.class, e -> {
return !(PsiUtil.isAccessedForWriting(e) && e.isReferenceTo(variable));
});
}
private static class SSAInstructionState {
private final int myWriteCount;
private final int myInstructionIdx;
@@ -476,10 +590,10 @@ public final class ControlFlowUtil {
/**
* Detect throw instructions which might affect observable control flow via side effects with local variables.
*
* The side effect of exception thrown occurs when a local variable is written in the try block, and then accessed
* in the finally section or in/after a catch section.
*
* <p>
* The side effect of exception thrown occurs when a local variable is written in the try block and then accessed
* in the {@code finally} section or in/after a catch section.
* <p>
* Example:
* <pre>
* { // --- start of theOuterBlock ---
@@ -492,9 +606,13 @@ public final class ControlFlowUtil {
* status = FINISHED;
* } // --- end of theTryBlock ---
* catch (Exception e) {
* LOG.error("Failed when " + status, e); // can get PREPARING or WORKING here
* // can get PREPARING or WORKING here
* LOG.error("Failed when " + status, e);
* }
* // can get PREPARING or WORKING here in the case of exception
* if (status == FINISHED) {
* LOG.info("Finished");
* }
* if (status == FINISHED) LOG.info("Finished"); // can get PREPARING or WORKING here in the case of exception
* } // --- end of theOuterBlock ---
* </pre>
* In the example above {@code hasObservableThrowExitPoints(theTryBlock) == true},
@@ -751,11 +869,11 @@ public final class ControlFlowUtil {
parent = resolveResult.getCurrentFileResolveScope();
}
if (parent instanceof PsiClass) {
final PsiClass clss = (PsiClass)parent;
if (PsiTreeUtil.isAncestor(targetClassMember, clss, false)) return false;
final PsiClass psiClass = (PsiClass)parent;
if (PsiTreeUtil.isAncestor(targetClassMember, psiClass, false)) return false;
PsiClass containingClass = PsiTreeUtil.getParentOfType(ref, PsiClass.class);
while (containingClass != null) {
if (containingClass.isInheritor(clss, true) &&
if (containingClass.isInheritor(psiClass, true) &&
PsiTreeUtil.isAncestor(targetClassMember, containingClass, false)) {
return false;
}
@@ -775,7 +893,7 @@ public final class ControlFlowUtil {
* @param scope scope to be scanned (part of code fragment to be extracted)
* @param member member containing the code to be extracted
* @param targetClassMember member in target class containing code fragment
* @return true if code fragment can be extracted outside
* @return true if a code fragment can be extracted outside
*/
public static boolean collectOuterLocals(@NotNull List<? super PsiVariable> array, @NotNull PsiElement scope, @NotNull PsiElement member,
@NotNull PsiElement targetClassMember) {
@@ -845,7 +963,7 @@ public final class ControlFlowUtil {
/**
* @return true if each control flow path results in return statement or exception thrown
* @return true if each control flow path results in reaching a return statement or exception thrown
*/
public static boolean returnPresent(@NotNull ControlFlow flow) {
InstructionClientVisitor<Boolean> visitor = new ReturnPresentClientVisitor(flow);
@@ -1904,16 +2022,12 @@ public final class ControlFlowUtil {
}
public static @NotNull List<PsiReferenceExpression> getReadBeforeWrite(@NotNull ControlFlow flow) {
return getReadBeforeWrite(flow, 0);
}
private static @NotNull List<PsiReferenceExpression> getReadBeforeWrite(@NotNull ControlFlow flow, int startOffset) {
if (startOffset < 0 || startOffset >= flow.getSize()) {
if (flow.getSize() == 0) {
return Collections.emptyList();
}
final ReadBeforeWriteClientVisitor visitor = new ReadBeforeWriteClientVisitor(flow, false);
depthFirstSearch(flow, visitor);
return visitor.getResult(startOffset);
return visitor.getResult(0);
}
private static class ReadBeforeWriteClientVisitor extends InstructionClientVisitor<List<PsiReferenceExpression>> {
@@ -2314,7 +2428,7 @@ public final class ControlFlowUtil {
boolean depthFirstSearch(final int startOffset, @NotNull BitSet visitedOffsets) {
// traverse the graph starting with the startOffset
IntArrayList walkThroughStack = new IntArrayList(Math.max(size() / 2, 2));
IntStack walkThroughStack = new IntArrayList(Math.max(size() / 2, 2));
visitedOffsets.clear();
walkThroughStack.push(startOffset);
while (!walkThroughStack.isEmpty()) {
@@ -55,7 +55,7 @@ abstract class C {
try { f(); } catch (E1 | E2 e) { E2 ee = <error descr="Incompatible types. Found: 'C.E1 | C.E2', required: 'C.E2'">e</error>; }
try { f(); } catch (E1 | E2 e) { <error descr="Cannot assign a value to final variable 'e'">e</error> = new E1(); }
try { f(); } catch (E1 | E2 e) { <error descr="Incompatible types. Found: 'C.E', required: 'C.E1 | C.E2'">e = new E()</error>; }
try { f(); } catch (E1 | E2 e) { <error descr="Cannot assign a value to final variable 'e'">e</error> = new E(); }
try { g(); }
catch (IE1 | IE2 e) {
@@ -1,12 +1,12 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.refactoring.typeMigration.rules;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.VariableAccessFromInnerClassFix;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiPrecedenceUtil;
import com.intellij.psi.util.PsiUtil;
@@ -272,7 +272,7 @@ public final class ThreadLocalConversionRule extends TypeConversionRule {
PsiExpression replaced = super.replace(expression, evaluator);
boolean effectivelyFinalSupported = PsiUtil.isAvailable(JavaFeature.EFFECTIVELY_FINAL, replaced);
for (PsiVariable var : myVariablesToMakeFinal) {
if (!effectivelyFinalSupported || !HighlightControlFlowUtil.isEffectivelyFinal(var, replaced, null)) {
if (!effectivelyFinalSupported || !ControlFlowUtil.isEffectivelyFinal(var, replaced)) {
VariableAccessFromInnerClassFix.fixAccess(var, replaced);
}
}