mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-highlighting] The rest of control-flow-related stuff (checkFinalVariableMightAlreadyHaveBeenAssignedTo) migrated
More utility methods inside ControlFlowUtil Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: d4c294ce18da2032a0686f66794a7f377549edd2
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cc643a5ae4
commit
1cd294e40d
@@ -379,6 +379,11 @@ variable.must.be.effectively.final=Variable ''{0}'' is accessed from within inne
|
||||
variable.must.be.effectively.final.lambda=Variable used in lambda expression should be final or effectively final
|
||||
variable.must.be.effectively.final.guard=Variable used in guard expression should be final or effectively final
|
||||
variable.not.initialized=Variable ''{0}'' might not have been initialized
|
||||
variable.already.assigned=Variable ''{0}'' might already have been assigned to
|
||||
variable.already.assigned.constructor=Cannot assign final field ''{0}'' after chained constructor call
|
||||
variable.already.assigned.field=Final field ''{0}'' is already initialized in another field initializer
|
||||
variable.already.assigned.initializer=Final field ''{0}'' is already initialized in a class initializer
|
||||
variable.assigned.in.loop=Variable ''{0}'' might be assigned in loop
|
||||
field.not.initialized=Field ''{0}'' might not have been initialized
|
||||
|
||||
instanceof.type.parameter=Class or array expected
|
||||
|
||||
+16
-2
@@ -120,7 +120,6 @@ final class ControlFlowChecker {
|
||||
if (isToBeEffectivelyFinal && ControlFlowUtil.isEffectivelyFinal(variable, scope, context)) return;
|
||||
var kind = isToBeEffectivelyFinal ? JavaErrorKinds.VARIABLE_MUST_BE_EFFECTIVELY_FINAL : JavaErrorKinds.VARIABLE_MUST_BE_FINAL;
|
||||
myVisitor.report(kind.create(context, variable));
|
||||
return;
|
||||
} else if (scope instanceof PsiLambdaExpression) {
|
||||
if (ControlFlowUtil.isEffectivelyFinal(variable, scope, context)) return;
|
||||
myVisitor.report(JavaErrorKinds.VARIABLE_MUST_BE_EFFECTIVELY_FINAL_LAMBDA.create(context, variable));
|
||||
@@ -139,13 +138,28 @@ final class ControlFlowChecker {
|
||||
myVisitor.report(JavaErrorKinds.FIELD_NOT_INITIALIZED.create(field));
|
||||
}
|
||||
|
||||
void checkVariableInitializedBeforeUsage(@NotNull PsiReferenceExpression expression, @NotNull PsiVariable variable) {
|
||||
void checkVariableInitializedBeforeUsage(@NotNull PsiVariable variable, @NotNull PsiReferenceExpression expression) {
|
||||
if (ControlFlowUtil.isInitializedBeforeUsage(expression, variable, myUninitializedVarProblems, false)) {
|
||||
return;
|
||||
}
|
||||
myVisitor.report(JavaErrorKinds.VARIABLE_NOT_INITIALIZED.create(expression, variable));
|
||||
}
|
||||
|
||||
void checkFinalVariableMightAlreadyHaveBeenAssignedTo(@NotNull PsiVariable variable, @NotNull PsiReferenceExpression expression) {
|
||||
ControlFlowUtil.DoubleInitializationProblem
|
||||
problem = ControlFlowUtil.findFinalVariableAlreadyInitializedProblem(variable, expression, myFinalVarProblems);
|
||||
var kind = switch (problem) {
|
||||
case NORMAL -> JavaErrorKinds.VARIABLE_ALREADY_ASSIGNED;
|
||||
case IN_LOOP -> JavaErrorKinds.VARIABLE_ASSIGNED_IN_LOOP;
|
||||
case IN_CONSTRUCTOR -> JavaErrorKinds.VARIABLE_ALREADY_ASSIGNED_CONSTRUCTOR;
|
||||
case IN_FIELD_INITIALIZER -> JavaErrorKinds.VARIABLE_ALREADY_ASSIGNED_FIELD;
|
||||
case IN_INITIALIZER -> JavaErrorKinds.VARIABLE_ALREADY_ASSIGNED_INITIALIZER;
|
||||
case NO_PROBLEM -> null;
|
||||
};
|
||||
if (kind == null) return;
|
||||
myVisitor.report(kind.create(expression, variable));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return field that has initializer with this element as subexpression or null if not found
|
||||
*/
|
||||
|
||||
+5
-1
@@ -706,7 +706,11 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
if (!hasErrorResults() && resolved instanceof PsiLocalVariable localVariable) {
|
||||
myExpressionChecker.checkVarTypeSelfReferencing(localVariable, expression);
|
||||
}
|
||||
if (!hasErrorResults()) myControlFlowChecker.checkVariableInitializedBeforeUsage(expression, variable);
|
||||
boolean isFinal = variable.hasModifierProperty(PsiModifier.FINAL);
|
||||
if (isFinal && !variable.hasInitializer() && !(variable instanceof PsiPatternVariable)) {
|
||||
if (!hasErrorResults()) myControlFlowChecker.checkFinalVariableMightAlreadyHaveBeenAssignedTo(variable, expression);
|
||||
}
|
||||
if (!hasErrorResults()) myControlFlowChecker.checkVariableInitializedBeforeUsage(variable, expression);
|
||||
}
|
||||
if (parent instanceof PsiMethodCallExpression methodCallExpression &&
|
||||
methodCallExpression.getMethodExpression() == expression &&
|
||||
|
||||
+15
@@ -1243,6 +1243,21 @@ public final class JavaErrorKinds {
|
||||
public static final Parameterized<PsiReferenceExpression, PsiVariable> VARIABLE_NOT_INITIALIZED =
|
||||
parameterized(PsiReferenceExpression.class, PsiVariable.class, "variable.not.initialized")
|
||||
.withRawDescription((ref, var) -> message("variable.not.initialized", var.getName()));
|
||||
public static final Parameterized<PsiReferenceExpression, PsiVariable> VARIABLE_ALREADY_ASSIGNED =
|
||||
parameterized(PsiReferenceExpression.class, PsiVariable.class, "variable.already.assigned")
|
||||
.withRawDescription((ref, var) -> message("variable.already.assigned", var.getName()));
|
||||
public static final Parameterized<PsiReferenceExpression, PsiVariable> VARIABLE_ALREADY_ASSIGNED_CONSTRUCTOR =
|
||||
parameterized(PsiReferenceExpression.class, PsiVariable.class, "variable.already.assigned.constructor")
|
||||
.withRawDescription((ref, var) -> message("variable.already.assigned.constructor", var.getName()));
|
||||
public static final Parameterized<PsiReferenceExpression, PsiVariable> VARIABLE_ALREADY_ASSIGNED_FIELD =
|
||||
parameterized(PsiReferenceExpression.class, PsiVariable.class, "variable.already.assigned.field")
|
||||
.withRawDescription((ref, var) -> message("variable.already.assigned.field", var.getName()));
|
||||
public static final Parameterized<PsiReferenceExpression, PsiVariable> VARIABLE_ALREADY_ASSIGNED_INITIALIZER =
|
||||
parameterized(PsiReferenceExpression.class, PsiVariable.class, "variable.already.assigned.initializer")
|
||||
.withRawDescription((ref, var) -> message("variable.already.assigned.initializer", var.getName()));
|
||||
public static final Parameterized<PsiReferenceExpression, PsiVariable> VARIABLE_ASSIGNED_IN_LOOP =
|
||||
parameterized(PsiReferenceExpression.class, PsiVariable.class, "variable.assigned.in.loop")
|
||||
.withRawDescription((ref, var) -> message("variable.assigned.in.loop", var.getName()));
|
||||
public static final Parameterized<PsiJavaCodeReferenceElement, PsiVariable> VARIABLE_MUST_BE_FINAL =
|
||||
parameterized(PsiJavaCodeReferenceElement.class, PsiVariable.class, "variable.must.be.final")
|
||||
.withRawDescription((ref, var) -> message("variable.must.be.final", var.getName()));
|
||||
|
||||
+10
-208
@@ -1,30 +1,20 @@
|
||||
// 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.codeInsight.daemon.impl.analysis;
|
||||
|
||||
import com.intellij.codeInsight.daemon.JavaErrorBundle;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.JavaPsiConstructorUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiVariable;
|
||||
import com.intellij.psi.controlFlow.AnalysisCanceledException;
|
||||
import com.intellij.psi.controlFlow.ControlFlow;
|
||||
import com.intellij.psi.controlFlow.ControlFlowFactory;
|
||||
import com.intellij.psi.controlFlow.ControlFlowUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @deprecated all the methods are deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public final class HighlightControlFlowUtil {
|
||||
|
||||
private static QuickFixFactory getQuickFixFactory() {
|
||||
return QuickFixFactory.getInstance();
|
||||
}
|
||||
|
||||
private HighlightControlFlowUtil() { }
|
||||
|
||||
/**
|
||||
@@ -42,192 +32,4 @@ public final class HighlightControlFlowUtil {
|
||||
public static boolean variableDefinitelyAssignedIn(@NotNull PsiVariable variable, @NotNull PsiElement context) {
|
||||
return ControlFlowUtil.variableDefinitelyAssignedIn(variable, context);
|
||||
}
|
||||
|
||||
private static @NotNull ControlFlow getControlFlow(@NotNull PsiElement context) throws AnalysisCanceledException {
|
||||
LocalsOrMyInstanceFieldsControlFlowPolicy policy = LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance();
|
||||
return ControlFlowFactory.getControlFlow(context, policy, ControlFlowOptions.create(true, true, true));
|
||||
}
|
||||
|
||||
public static boolean isAssigned(@NotNull PsiParameter parameter) {
|
||||
ParamWriteProcessor processor = new ParamWriteProcessor();
|
||||
ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), true).forEach(processor);
|
||||
return processor.isWriteRefFound();
|
||||
}
|
||||
|
||||
private static class ParamWriteProcessor implements Processor<PsiReference> {
|
||||
private volatile boolean myIsWriteRefFound;
|
||||
@Override
|
||||
public boolean process(@NotNull PsiReference reference) {
|
||||
PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiReferenceExpression ref && PsiUtil.isAccessedForWriting(ref)) {
|
||||
myIsWriteRefFound = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isWriteRefFound() {
|
||||
return myIsWriteRefFound;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean variableDefinitelyNotAssignedIn(@NotNull PsiVariable variable, @NotNull PsiElement context) {
|
||||
try {
|
||||
return ControlFlowUtil.isVariableDefinitelyNotAssigned(variable, getControlFlow(context));
|
||||
}
|
||||
catch (AnalysisCanceledException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param variable variable to check
|
||||
* @param finalVarProblems cache map to reuse information
|
||||
* @return true if variable is reassigned
|
||||
*/
|
||||
public static boolean isReassigned(@NotNull PsiVariable variable,
|
||||
@NotNull Map<? super PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems) {
|
||||
if (variable instanceof PsiLocalVariable) {
|
||||
PsiElement parent = variable.getParent();
|
||||
if (parent == null) return false;
|
||||
PsiElement declarationScope = parent.getParent();
|
||||
if (declarationScope == null) return false;
|
||||
Collection<ControlFlowUtil.VariableInfo> codeBlockProblems = getFinalVariableProblemsInBlock(finalVarProblems, declarationScope);
|
||||
return codeBlockProblems.contains(new ControlFlowUtil.VariableInfo(variable, null));
|
||||
}
|
||||
if (variable instanceof PsiParameter parameter) {
|
||||
return isAssigned(parameter);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static HighlightInfo.Builder checkFinalVariableMightAlreadyHaveBeenAssignedTo(@NotNull PsiVariable variable,
|
||||
@NotNull PsiReferenceExpression expression,
|
||||
@NotNull Map<? super PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems) {
|
||||
if (!PsiUtil.isAccessedForWriting(expression)) return null;
|
||||
|
||||
PsiElement scope = variable instanceof PsiField
|
||||
? variable.getParent()
|
||||
: variable.getParent() == null ? null : variable.getParent().getParent();
|
||||
PsiElement codeBlock = PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope);
|
||||
if (codeBlock == null) return null;
|
||||
Collection<ControlFlowUtil.VariableInfo> codeBlockProblems = getFinalVariableProblemsInBlock(finalVarProblems, codeBlock);
|
||||
|
||||
boolean inLoop = false;
|
||||
boolean canDefer = false;
|
||||
ControlFlowUtil.VariableInfo variableInfo = ContainerUtil.find(codeBlockProblems, vi -> vi.expression == expression);
|
||||
if (variableInfo != null) {
|
||||
inLoop = variableInfo instanceof InitializedInLoopProblemInfo;
|
||||
canDefer = !inLoop;
|
||||
}
|
||||
else if (!(variable instanceof PsiField field && isFieldInitializedInAnotherMember(field, expression, codeBlock))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String description =
|
||||
JavaErrorBundle.message(inLoop ? "variable.assigned.in.loop" : "variable.already.assigned", variable.getName());
|
||||
HighlightInfo.Builder highlightInfo =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description);
|
||||
if (canDefer) {
|
||||
IntentionAction action = getQuickFixFactory().createDeferFinalAssignmentFix(variable, expression);
|
||||
highlightInfo.registerFix(action, null, null, null, null);
|
||||
}
|
||||
HighlightFixUtil.registerMakeNotFinalAction(variable, highlightInfo);
|
||||
return highlightInfo;
|
||||
}
|
||||
|
||||
private static boolean isFieldInitializedInAnotherMember(@NotNull PsiField field,
|
||||
@NotNull PsiReferenceExpression expression,
|
||||
@NotNull PsiElement codeBlock) {
|
||||
PsiClass aClass = field.getContainingClass();
|
||||
if (aClass == null) return false;
|
||||
boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC);
|
||||
PsiMember enclosingConstructorOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expression);
|
||||
|
||||
if (!isFieldStatic) {
|
||||
// constructor that delegates to another constructor cannot assign final fields
|
||||
if (enclosingConstructorOrInitializer instanceof PsiMethod method) {
|
||||
PsiMethodCallExpression chainedCall = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(method);
|
||||
if (JavaPsiConstructorUtil.isChainedConstructorCall(chainedCall)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// field can get assigned in other field initializers or in class initializers
|
||||
List<PsiMember> members = new ArrayList<>(Arrays.asList(aClass.getFields()));
|
||||
if (enclosingConstructorOrInitializer != null
|
||||
&& aClass.getManager().areElementsEquivalent(enclosingConstructorOrInitializer.getContainingClass(), aClass)) {
|
||||
members.addAll(Arrays.asList(aClass.getInitializers()));
|
||||
members.sort(PsiUtil.BY_POSITION);
|
||||
}
|
||||
|
||||
for (PsiMember member : members) {
|
||||
if (member == field) continue;
|
||||
PsiElement context = member instanceof PsiField f ? f.getInitializer() : ((PsiClassInitializer)member).getBody();
|
||||
|
||||
if (context != null
|
||||
&& member.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic
|
||||
&& !variableDefinitelyNotAssignedIn(field, context)) {
|
||||
return context != codeBlock;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static @NotNull Collection<ControlFlowUtil.VariableInfo> getFinalVariableProblemsInBlock(@NotNull Map<? super PsiElement, Collection<ControlFlowUtil.VariableInfo>> finalVarProblems,
|
||||
@NotNull PsiElement codeBlock) {
|
||||
Collection<ControlFlowUtil.VariableInfo> codeBlockProblems = finalVarProblems.get(codeBlock);
|
||||
if (codeBlockProblems == null) {
|
||||
try {
|
||||
ControlFlow controlFlow = getControlFlow(codeBlock);
|
||||
codeBlockProblems = ControlFlowUtil.getInitializedTwice(controlFlow);
|
||||
codeBlockProblems = addReassignedInLoopProblems(codeBlockProblems, controlFlow);
|
||||
}
|
||||
catch (AnalysisCanceledException e) {
|
||||
codeBlockProblems = Collections.emptyList();
|
||||
}
|
||||
finalVarProblems.put(codeBlock, codeBlockProblems);
|
||||
}
|
||||
return codeBlockProblems;
|
||||
}
|
||||
|
||||
private static Collection<ControlFlowUtil.VariableInfo> addReassignedInLoopProblems(
|
||||
@NotNull Collection<ControlFlowUtil.VariableInfo> codeBlockProblems,
|
||||
@NotNull ControlFlow controlFlow) {
|
||||
List<Instruction> instructions = controlFlow.getInstructions();
|
||||
for (int index = 0; index < instructions.size(); index++) {
|
||||
Instruction instruction = instructions.get(index);
|
||||
if (instruction instanceof WriteVariableInstruction wvi) {
|
||||
PsiVariable variable = wvi.variable;
|
||||
if (variable instanceof PsiLocalVariable || variable instanceof PsiField) {
|
||||
PsiElement anchor = controlFlow.getElement(index);
|
||||
if (anchor instanceof PsiAssignmentExpression assignment) {
|
||||
PsiExpression ref = PsiUtil.skipParenthesizedExprDown(assignment.getLExpression());
|
||||
if (ref instanceof PsiReferenceExpression) {
|
||||
ControlFlowUtil.VariableInfo varInfo = new InitializedInLoopProblemInfo(variable, ref);
|
||||
if (!codeBlockProblems.contains(varInfo) && ControlFlowUtil.isInstructionReachable(controlFlow, index, index)) {
|
||||
if (!(codeBlockProblems instanceof HashSet)) {
|
||||
codeBlockProblems = new HashSet<>(codeBlockProblems);
|
||||
}
|
||||
codeBlockProblems.add(varInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return codeBlockProblems;
|
||||
}
|
||||
|
||||
/**
|
||||
* A kind of final variable problem returned from {@link #getFinalVariableProblemsInBlock(Map, PsiElement)}
|
||||
* which designates a final variable which is initialized in a loop.
|
||||
*/
|
||||
private static class InitializedInLoopProblemInfo extends ControlFlowUtil.VariableInfo {
|
||||
InitializedInLoopProblemInfo(@NotNull PsiVariable variable, @Nullable PsiElement expression) {
|
||||
super(variable, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-23
@@ -2,7 +2,6 @@
|
||||
package com.intellij.codeInsight.daemon.impl.analysis;
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.*;
|
||||
import com.intellij.codeInsight.intention.CommonIntentionAction;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
@@ -41,7 +40,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.util.Objects.*;
|
||||
import static java.util.Objects.requireNonNullElse;
|
||||
|
||||
public final class HighlightFixUtil {
|
||||
private static final Logger LOG = Logger.getInstance(HighlightFixUtil.class);
|
||||
@@ -184,12 +183,6 @@ public final class HighlightFixUtil {
|
||||
return qname == null || !Character.isLowerCase(qname.charAt(0));
|
||||
}
|
||||
|
||||
static void registerChangeVariableTypeFixes(@NotNull PsiVariable parameter,
|
||||
@Nullable PsiType itemType,
|
||||
@Nullable HighlightInfo.Builder highlightInfo) {
|
||||
registerChangeVariableTypeFixes(parameter, itemType, HighlightUtil.asConsumer(highlightInfo));
|
||||
}
|
||||
|
||||
static void registerChangeVariableTypeFixes(@NotNull PsiVariable parameter,
|
||||
@Nullable PsiType itemType,
|
||||
@NotNull Consumer<? super CommonIntentionAction> info) {
|
||||
@@ -351,21 +344,6 @@ public final class HighlightFixUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
static void registerMakeNotFinalAction(@NotNull PsiVariable var, @Nullable HighlightInfo.Builder highlightInfo) {
|
||||
if (var instanceof PsiField) {
|
||||
QuickFixAction.registerQuickFixActions(
|
||||
highlightInfo, null,
|
||||
JvmElementActionFactories.createModifierActions((PsiField)var, MemberRequestsKt.modifierRequest(JvmModifier.FINAL, false))
|
||||
);
|
||||
}
|
||||
else {
|
||||
IntentionAction action = QuickFixFactory.getInstance().createModifierListFix(var, PsiModifier.FINAL, false, false);
|
||||
if (highlightInfo != null) {
|
||||
highlightInfo.registerFix(action, null, null, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerFixesForExpressionStatement(@NotNull PsiElement statement, @NotNull Consumer<? super CommonIntentionAction> info) {
|
||||
if (!(statement instanceof PsiExpressionStatement)) return;
|
||||
PsiCodeBlock block = ObjectUtils.tryCast(statement.getParent(), PsiCodeBlock.class);
|
||||
|
||||
-9
@@ -475,15 +475,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
JavaResolveResult result = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
|
||||
|
||||
PsiElement resolved = result.getElement();
|
||||
if (resolved instanceof PsiVariable variable && resolved.getContainingFile() == expression.getContainingFile()) {
|
||||
boolean isFinal = variable.hasModifierProperty(PsiModifier.FINAL);
|
||||
if (isFinal && !variable.hasInitializer() && !(variable instanceof PsiPatternVariable)) {
|
||||
if (!hasErrorResults()) {
|
||||
add(HighlightControlFlowUtil.checkFinalVariableMightAlreadyHaveBeenAssignedTo(variable, expression, myFinalVarProblems));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasErrorResults()) add(HighlightUtil.checkClassReferenceAfterQualifier(expression, resolved));
|
||||
PsiExpression qualifierExpression = expression.getQualifierExpression();
|
||||
if (!hasErrorResults() && myJavaModule == null && qualifierExpression != null) {
|
||||
|
||||
+7
@@ -388,6 +388,13 @@ final class JavaErrorFixProvider {
|
||||
fix(VARIABLE_MUST_BE_EFFECTIVELY_FINAL, error -> myFactory.createMakeVariableEffectivelyFinalFix(error.context()));
|
||||
fix(VARIABLE_MUST_BE_EFFECTIVELY_FINAL_LAMBDA, error -> myFactory.createMakeVariableEffectivelyFinalFix(error.context()));
|
||||
fix(VARIABLE_MUST_BE_EFFECTIVELY_FINAL_GUARD, error -> myFactory.createMakeVariableEffectivelyFinalFix(error.context()));
|
||||
fix(VARIABLE_ALREADY_ASSIGNED, error -> myFactory.createDeferFinalAssignmentFix(error.context(), error.psi()));
|
||||
fix(VARIABLE_ALREADY_ASSIGNED, error -> removeModifierFix(error.context(), PsiModifier.FINAL));
|
||||
fix(VARIABLE_ALREADY_ASSIGNED_FIELD, error -> removeModifierFix(error.context(), PsiModifier.FINAL));
|
||||
fix(VARIABLE_ALREADY_ASSIGNED_CONSTRUCTOR, error -> removeModifierFix(error.context(), PsiModifier.FINAL));
|
||||
fix(VARIABLE_ALREADY_ASSIGNED_INITIALIZER, error -> removeModifierFix(error.context(), PsiModifier.FINAL));
|
||||
fix(VARIABLE_ASSIGNED_IN_LOOP, error -> removeModifierFix(error.context(), PsiModifier.FINAL));
|
||||
|
||||
fixes(FIELD_NOT_INITIALIZED, (error, sink) -> {
|
||||
PsiField field = error.psi();
|
||||
sink.accept(myFactory.createCreateConstructorParameterFromFieldFix(field));
|
||||
|
||||
+4
-5
@@ -1,10 +1,10 @@
|
||||
// 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.psi.*;
|
||||
import com.intellij.psi.controlFlow.ControlFlowUtil;
|
||||
import com.siyeh.ig.psiutils.VariableAccessUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -42,7 +42,7 @@ public final class ReassignedVariableInspection extends AbstractBaseJavaLocalIns
|
||||
PsiIdentifier nameIdentifier = variable.getNameIdentifier();
|
||||
if (nameIdentifier != null &&
|
||||
!variable.hasModifierProperty(PsiModifier.FINAL) &&
|
||||
HighlightControlFlowUtil.isReassigned(variable, myLocalVariableProblems)) {
|
||||
ControlFlowUtil.isReassigned(variable, myLocalVariableProblems)) {
|
||||
myHolder.registerProblem(nameIdentifier, getReassignedMessage(variable));
|
||||
return true;
|
||||
}
|
||||
@@ -60,13 +60,12 @@ public final class ReassignedVariableInspection extends AbstractBaseJavaLocalIns
|
||||
!((PsiVariable)resolved).hasModifierProperty(PsiModifier.FINAL) &&
|
||||
!SuppressionUtil.inspectionResultSuppressed(resolved, ReassignedVariableInspection.this)) {
|
||||
if (resolved instanceof PsiLocalVariable) {
|
||||
if (HighlightControlFlowUtil.isReassigned((PsiVariable)resolved, myLocalVariableProblems)) {
|
||||
if (ControlFlowUtil.isReassigned((PsiVariable)resolved, myLocalVariableProblems)) {
|
||||
myHolder.registerProblem(referenceNameElement, getReassignedMessage((PsiVariable)resolved));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Boolean isReassigned = myParameterIsReassigned.computeIfAbsent((PsiParameter)resolved,
|
||||
HighlightControlFlowUtil::isAssigned);
|
||||
Boolean isReassigned = myParameterIsReassigned.computeIfAbsent((PsiParameter)resolved, VariableAccessUtils::variableIsAssigned);
|
||||
if (isReassigned) {
|
||||
myHolder.registerProblem(referenceNameElement, getReassignedMessage((PsiVariable)resolved));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.NullabilityAnnotationInfo;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.codeInspection.dataFlow.interpreter.RunnerResult;
|
||||
import com.intellij.codeInspection.dataFlow.interpreter.StandardDataFlowInterpreter;
|
||||
@@ -37,6 +36,7 @@ import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.ControlFlowUtil;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.DeepestSuperMethodsSearch;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
@@ -231,7 +231,7 @@ public final class DfaPsiUtil {
|
||||
refExpr.resolve() instanceof PsiParameter parameter &&
|
||||
parameter.getParent() instanceof PsiForeachStatement targetLoop &&
|
||||
PsiTreeUtil.isAncestor(targetLoop, loop, true) &&
|
||||
!HighlightControlFlowUtil.isReassigned(parameter, new HashMap<>())) {
|
||||
!ControlFlowUtil.isReassigned(parameter, new HashMap<>())) {
|
||||
iteratedType = inferLoopParameterTypeWithNullability(targetLoop);
|
||||
}
|
||||
return JavaGenericsUtil.getCollectionItemType(iteratedType, iteratedValue.getResolveScope());
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.psiutils;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.ControlFlowUtil;
|
||||
import com.intellij.psi.controlFlow.LocalsOrMyInstanceFieldsControlFlowPolicy;
|
||||
@@ -69,8 +67,7 @@ public final class FinalUtils {
|
||||
PsiElement innerScope = ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, ref);
|
||||
if (innerScope != null && innerScope != ((PsiField)variable).getContainingClass()) return false;
|
||||
}
|
||||
HighlightInfo.Builder random =
|
||||
HighlightControlFlowUtil.checkFinalVariableMightAlreadyHaveBeenAssignedTo(variable, ref, finalVarProblems);
|
||||
return random == null;
|
||||
return ControlFlowUtil.findFinalVariableAlreadyInitializedProblem(variable, ref, finalVarProblems) ==
|
||||
ControlFlowUtil.DoubleInitializationProblem.NO_PROBLEM;
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -2,8 +2,6 @@
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.codeInsight.intention.FileModifier;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
@@ -321,9 +319,10 @@ public class VariableAccessFromInnerClassFix implements IntentionAction {
|
||||
expression, variable, uninitializedVarProblems, false)) {
|
||||
return false;
|
||||
}
|
||||
HighlightInfo.Builder highlightInfo =
|
||||
HighlightControlFlowUtil.checkFinalVariableMightAlreadyHaveBeenAssignedTo(variable, expression, finalVarProblems);
|
||||
if (highlightInfo != null) return false;
|
||||
if (ControlFlowUtil.findFinalVariableAlreadyInitializedProblem(variable, expression, finalVarProblems) !=
|
||||
ControlFlowUtil.DoubleInitializationProblem.NO_PROBLEM) {
|
||||
return false;
|
||||
}
|
||||
if (variable instanceof PsiParameter && PsiUtil.isAccessedForWriting(expression)) return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.intellij.codeInsight.AutoPopupController;
|
||||
import com.intellij.codeInsight.TailTypes;
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.daemon.impl.JavaColorProvider;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.BringVariableIntoScopeFix;
|
||||
import com.intellij.codeInsight.lookup.impl.JavaElementLookupRenderer;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.descriptors.PlainDescriptor;
|
||||
@@ -306,7 +305,7 @@ public class VariableLookupItem extends LookupItem<PsiVariable> implements Typed
|
||||
}
|
||||
|
||||
if (ControlFlowUtil.getScopeEnforcingEffectiveFinality(variable, place) != null &&
|
||||
!HighlightControlFlowUtil.isReassigned(variable, new HashMap<>())) {
|
||||
!ControlFlowUtil.isReassigned(variable, new HashMap<>())) {
|
||||
PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.usages.impl.rules;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.usages.PsiElementUsageTarget;
|
||||
import com.intellij.usages.UsageTarget;
|
||||
import com.siyeh.ig.psiutils.VariableAccessUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -78,7 +78,7 @@ public final class JavaUsageTypeProvider implements UsageTypeProviderEx {
|
||||
}
|
||||
|
||||
for (PsiParameter parameter : parameters) {
|
||||
if (HighlightControlFlowUtil.isAssigned(parameter)) return false;
|
||||
if (VariableAccessUtils.variableIsAssigned(parameter)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.ExpectedTypeInfo;
|
||||
import com.intellij.codeInsight.ExpectedTypesProvider;
|
||||
import com.intellij.codeInsight.completion.JavaCompletionUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.lang.LanguageRefactoringSupport;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -22,6 +21,7 @@ import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.*;
|
||||
import com.intellij.psi.controlFlow.ControlFlowUtil;
|
||||
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
|
||||
import com.intellij.psi.impl.source.codeStyle.javadoc.CommentFormatter;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
@@ -457,7 +457,7 @@ public final class CommonJavaRefactoringUtil {
|
||||
|
||||
public static boolean canBeDeclaredFinal(@NotNull PsiVariable variable) {
|
||||
LOG.assertTrue(variable instanceof PsiLocalVariable || variable instanceof PsiParameter);
|
||||
final boolean isReassigned = HighlightControlFlowUtil
|
||||
final boolean isReassigned = ControlFlowUtil
|
||||
.isReassigned(variable, new HashMap<>());
|
||||
return !isReassigned;
|
||||
}
|
||||
|
||||
@@ -435,6 +435,172 @@ public final class ControlFlowUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean variableDefinitelyNotAssignedIn(@NotNull PsiVariable variable, @NotNull PsiElement context) {
|
||||
ControlFlow flow = getControlFlow(context);
|
||||
return flow == null || isVariableDefinitelyNotAssigned(variable, flow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kind of double initialization problem
|
||||
*
|
||||
* @see #findFinalVariableAlreadyInitializedProblem(PsiVariable, PsiReferenceExpression, Map)
|
||||
*/
|
||||
public enum DoubleInitializationProblem {
|
||||
NO_PROBLEM,
|
||||
/**
|
||||
* Final variable is reassigned normally
|
||||
*/
|
||||
NORMAL,
|
||||
/**
|
||||
* Final variable is reassigned in loop
|
||||
*/
|
||||
IN_LOOP,
|
||||
/**
|
||||
* Double initialization of a final field due to chained constructor call
|
||||
*/
|
||||
IN_CONSTRUCTOR,
|
||||
/**
|
||||
* Double initialization of a final field in other initializer
|
||||
*/
|
||||
IN_INITIALIZER,
|
||||
/**
|
||||
* Double initialization of a final field in other field initializer
|
||||
*/
|
||||
IN_FIELD_INITIALIZER
|
||||
}
|
||||
|
||||
/**
|
||||
* @param variable final variable to check
|
||||
* @param expression variable reference (write location)
|
||||
* @param finalVarProblems a map to cache the results
|
||||
* @return DoubleInitializationProblem object that depicts the problem kind
|
||||
*/
|
||||
public static @NotNull DoubleInitializationProblem findFinalVariableAlreadyInitializedProblem(@NotNull PsiVariable variable,
|
||||
@NotNull PsiReferenceExpression expression,
|
||||
@NotNull Map<PsiElement, Collection<VariableInfo>> finalVarProblems) {
|
||||
if (!PsiUtil.isAccessedForWriting(expression)) return DoubleInitializationProblem.NO_PROBLEM;
|
||||
|
||||
PsiElement scope = variable instanceof PsiField
|
||||
? variable.getParent()
|
||||
: variable.getParent() == null ? null : variable.getParent().getParent();
|
||||
PsiElement codeBlock = PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope);
|
||||
if (codeBlock == null) return DoubleInitializationProblem.NO_PROBLEM;
|
||||
Collection<VariableInfo> codeBlockProblems = getFinalVariableProblemsInBlock(finalVarProblems, codeBlock);
|
||||
|
||||
VariableInfo variableInfo = ContainerUtil.find(codeBlockProblems, vi -> vi.expression == expression);
|
||||
if (variableInfo == null) {
|
||||
if (variable instanceof PsiField) {
|
||||
DoubleInitializationProblem problem = isFieldInitializedInAnotherMember((PsiField)variable, expression, codeBlock);
|
||||
if (problem != null) {
|
||||
return problem;
|
||||
}
|
||||
}
|
||||
return DoubleInitializationProblem.NO_PROBLEM;
|
||||
}
|
||||
return variableInfo instanceof InitializedInLoopProblemInfo ? DoubleInitializationProblem.IN_LOOP : DoubleInitializationProblem.NORMAL;
|
||||
}
|
||||
|
||||
private static DoubleInitializationProblem isFieldInitializedInAnotherMember(@NotNull PsiField field,
|
||||
@NotNull PsiReferenceExpression expression,
|
||||
@NotNull PsiElement codeBlock) {
|
||||
PsiClass aClass = field.getContainingClass();
|
||||
if (aClass == null) return null;
|
||||
boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC);
|
||||
PsiMember enclosingConstructorOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expression);
|
||||
|
||||
if (!isFieldStatic) {
|
||||
// constructor that delegates to another constructor cannot assign final fields
|
||||
if (enclosingConstructorOrInitializer instanceof PsiMethod) {
|
||||
PsiMethodCallExpression chainedCall = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(
|
||||
(PsiMethod)enclosingConstructorOrInitializer);
|
||||
if (JavaPsiConstructorUtil.isChainedConstructorCall(chainedCall)) {
|
||||
return DoubleInitializationProblem.IN_CONSTRUCTOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// field can get assigned in other field initializers or in class initializers
|
||||
List<PsiMember> members = new ArrayList<>(Arrays.asList(aClass.getFields()));
|
||||
if (enclosingConstructorOrInitializer != null
|
||||
&& aClass.getManager().areElementsEquivalent(enclosingConstructorOrInitializer.getContainingClass(), aClass)) {
|
||||
members.addAll(Arrays.asList(aClass.getInitializers()));
|
||||
members.sort(PsiUtil.BY_POSITION);
|
||||
}
|
||||
|
||||
for (PsiMember member : members) {
|
||||
if (member == field) continue;
|
||||
PsiElement context = member instanceof PsiField ? ((PsiField)member).getInitializer() : ((PsiClassInitializer)member).getBody();
|
||||
|
||||
if (context != null
|
||||
&& member.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic
|
||||
&& !variableDefinitelyNotAssignedIn(field, context)) {
|
||||
return context == codeBlock ? null :
|
||||
member instanceof PsiField ? DoubleInitializationProblem.IN_FIELD_INITIALIZER :
|
||||
DoubleInitializationProblem.IN_INITIALIZER;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @NotNull Collection<VariableInfo> getFinalVariableProblemsInBlock(
|
||||
@NotNull Map<PsiElement, Collection<VariableInfo>> finalVarProblems, @NotNull PsiElement codeBlock) {
|
||||
Collection<VariableInfo> codeBlockProblems =
|
||||
finalVarProblems.computeIfAbsent(codeBlock, cb -> {
|
||||
ControlFlow controlFlow = getControlFlow(codeBlock);
|
||||
return controlFlow == null ? Collections.emptyList() : addReassignedInLoopProblems(getInitializedTwice(controlFlow), controlFlow);
|
||||
});
|
||||
return codeBlockProblems;
|
||||
}
|
||||
|
||||
private static Collection<VariableInfo> addReassignedInLoopProblems(
|
||||
@NotNull Collection<VariableInfo> codeBlockProblems,
|
||||
@NotNull ControlFlow controlFlow) {
|
||||
List<Instruction> instructions = controlFlow.getInstructions();
|
||||
for (int index = 0; index < instructions.size(); index++) {
|
||||
Instruction instruction = instructions.get(index);
|
||||
if (instruction instanceof WriteVariableInstruction) {
|
||||
PsiVariable variable = ((WriteVariableInstruction)instruction).variable;
|
||||
if (variable instanceof PsiLocalVariable || variable instanceof PsiField) {
|
||||
PsiElement anchor = controlFlow.getElement(index);
|
||||
if (anchor instanceof PsiAssignmentExpression) {
|
||||
PsiExpression ref = PsiUtil.skipParenthesizedExprDown(((PsiAssignmentExpression)anchor).getLExpression());
|
||||
if (ref instanceof PsiReferenceExpression) {
|
||||
VariableInfo varInfo = new InitializedInLoopProblemInfo(variable, ref);
|
||||
if (!codeBlockProblems.contains(varInfo) && isInstructionReachable(controlFlow, index, index)) {
|
||||
if (!(codeBlockProblems instanceof HashSet)) {
|
||||
codeBlockProblems = new HashSet<>(codeBlockProblems);
|
||||
}
|
||||
codeBlockProblems.add(varInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return codeBlockProblems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param variable variable to check (local variable or parameter)
|
||||
* @param finalVarProblems cache map to reuse information
|
||||
* @return true if the variable is reassigned
|
||||
*/
|
||||
public static boolean isReassigned(@NotNull PsiVariable variable, @NotNull Map<PsiElement, Collection<VariableInfo>> finalVarProblems) {
|
||||
if (variable instanceof PsiLocalVariable) {
|
||||
PsiElement parent = variable.getParent();
|
||||
if (parent == null) return false;
|
||||
PsiElement declarationScope = parent.getParent();
|
||||
if (declarationScope == null) return false;
|
||||
Collection<VariableInfo> codeBlockProblems = getFinalVariableProblemsInBlock(finalVarProblems, declarationScope);
|
||||
return codeBlockProblems.contains(new VariableInfo(variable, null));
|
||||
}
|
||||
if (variable instanceof PsiParameter) {
|
||||
PsiParameter parameter = (PsiParameter)variable;
|
||||
return variableIsAssigned(parameter, parameter.getDeclarationScope());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class SSAInstructionState {
|
||||
private final int myWriteCount;
|
||||
private final int myInstructionIdx;
|
||||
@@ -2303,6 +2469,16 @@ public final class ControlFlowUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A kind of final variable problem returned from {@link #getFinalVariableProblemsInBlock(Map, PsiElement)}
|
||||
* which designates a final variable which is initialized in a loop.
|
||||
*/
|
||||
private static class InitializedInLoopProblemInfo extends VariableInfo {
|
||||
InitializedInLoopProblemInfo(@NotNull PsiVariable variable, @Nullable PsiElement expression) {
|
||||
super(variable, expression);
|
||||
}
|
||||
}
|
||||
|
||||
private static void merge(int offset, CopyOnWriteList source, CopyOnWriteList @NotNull [] target) {
|
||||
if (source != null) {
|
||||
CopyOnWriteList existing = target[offset];
|
||||
|
||||
+9
-9
@@ -5,7 +5,7 @@ class Foo {
|
||||
final int k;
|
||||
final int ff = 5;
|
||||
Foo(int i) {
|
||||
<error descr="Variable 'k' might already have been assigned to">k</error> =1;
|
||||
<error descr="Final field 'k' is already initialized in a class initializer">k</error> =1;
|
||||
}
|
||||
{
|
||||
k=0;
|
||||
@@ -21,7 +21,7 @@ class c2 {
|
||||
int i = k;
|
||||
}
|
||||
static {
|
||||
<error descr="Variable 'k' might already have been assigned to">k</error> =1;
|
||||
<error descr="Final field 'k' is already initialized in a class initializer">k</error> =1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class c3 {
|
||||
int i = k;
|
||||
}
|
||||
{
|
||||
<error descr="Variable 'k' might already have been assigned to">k</error> =1;
|
||||
<error descr="Final field 'k' is already initialized in a class initializer">k</error> =1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,11 +45,11 @@ class c4 {
|
||||
}
|
||||
c4(int i) {
|
||||
if (false)
|
||||
<error descr="Variable 'k' might already have been assigned to">k</error> =1;
|
||||
<error descr="Final field 'k' is already initialized in a class initializer">k</error> =1;
|
||||
}
|
||||
c4() {
|
||||
this(0);
|
||||
<error descr="Variable 'k' might already have been assigned to">k</error> =1;
|
||||
<error descr="Cannot assign final field 'k' after chained constructor call">k</error> =1;
|
||||
}
|
||||
}
|
||||
// redirected ctrs
|
||||
@@ -60,7 +60,7 @@ class c5 {
|
||||
}
|
||||
c5() {
|
||||
this(0);
|
||||
<error descr="Variable 'k' might already have been assigned to">k</error> =1;
|
||||
<error descr="Cannot assign final field 'k' after chained constructor call">k</error> =1;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,11 +68,11 @@ class c5 {
|
||||
}
|
||||
c5(int i, int j) {
|
||||
this('c');
|
||||
<error descr="Variable 'k' might already have been assigned to">k</error> = 5;
|
||||
<error descr="Cannot assign final field 'k' after chained constructor call">k</error> = 5;
|
||||
}
|
||||
c5(String s) {
|
||||
this(0,0);
|
||||
<error descr="Variable 'k' might already have been assigned to">k</error> =1;
|
||||
<error descr="Cannot assign final field 'k' after chained constructor call">k</error> =1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,6 @@ class c7 {
|
||||
}
|
||||
|
||||
{
|
||||
<error descr="Variable 'y' might already have been assigned to">y</error> = ""+i;
|
||||
<error descr="Final field 'y' is already initialized in a class initializer">y</error> = ""+i;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,6 +40,6 @@ class X {
|
||||
|
||||
X() {
|
||||
<error descr="Recursive constructor call">this()</error>;
|
||||
<error descr="Variable 'value' might already have been assigned to">value</error> = 1;
|
||||
<error descr="Cannot assign final field 'value' after chained constructor call">value</error> = 1;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -3,10 +3,10 @@ class A {
|
||||
c = "c";
|
||||
}
|
||||
static String b = a = "";
|
||||
static String d = <error descr="Variable 'c' might already have been assigned to">c</error> = "";
|
||||
static String d = <error descr="Final field 'c' is already initialized in a class initializer">c</error> = "";
|
||||
|
||||
static {
|
||||
<error descr="Variable 'a' might already have been assigned to">a</error> = "";
|
||||
<error descr="Final field 'a' is already initialized in another field initializer">a</error> = "";
|
||||
}
|
||||
static final String a;
|
||||
static final String c;
|
||||
@@ -16,10 +16,10 @@ class B {
|
||||
c = "c";
|
||||
}
|
||||
String b = a = "";
|
||||
String d = <error descr="Variable 'c' might already have been assigned to">c</error> = "";
|
||||
String d = <error descr="Final field 'c' is already initialized in a class initializer">c</error> = "";
|
||||
|
||||
{
|
||||
<error descr="Variable 'a' might already have been assigned to">a</error> = "";
|
||||
<error descr="Final field 'a' is already initialized in another field initializer">a</error> = "";
|
||||
}
|
||||
final String a;
|
||||
final String c;
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ record VarArgMismatch2(int[] x) {
|
||||
record Delegate(int x) {
|
||||
public Delegate(int x) {
|
||||
<error descr="Canonical constructor cannot delegate to another constructor">this()</error>;
|
||||
<error descr="Variable 'x' might already have been assigned to">this.x</error> = 0;
|
||||
<error descr="Cannot assign final field 'x' after chained constructor call">this.x</error> = 0;
|
||||
}
|
||||
|
||||
public <error descr="Non-canonical record constructor must delegate to another constructor">Delegate</error>() {
|
||||
@@ -67,7 +67,7 @@ record ImplicitCanonicalConstructor(String s) {
|
||||
record AssignmentInNonCanonical(int x, int y, long depth) {
|
||||
public AssignmentInNonCanonical(int x, int y) {
|
||||
this(x, y, 10);
|
||||
<error descr="Variable 'x' might already have been assigned to">this.x</error> = x;
|
||||
<error descr="Cannot assign final field 'x' after chained constructor call">this.x</error> = x;
|
||||
}
|
||||
|
||||
void method() {
|
||||
|
||||
Reference in New Issue
Block a user