From cc643a5ae432e96b18e2cd92199e55e0acd6d0d4 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 5 Feb 2025 13:50:26 +0100 Subject: [PATCH] [java-highlighting] More control-flow-related stuff migrated Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: 3c292fdf4869e6d13c16fabbf42e2055ea54f724 --- .../JavaCompilationErrorBundle.properties | 2 + .../highlighting/ControlFlowChecker.java | 23 ++ .../highlighting/JavaErrorVisitor.java | 4 +- .../highlighting/errors/JavaErrorKinds.java | 7 + .../analysis/HighlightControlFlowUtil.java | 372 +----------------- .../impl/analysis/HighlightVisitorImpl.java | 16 - .../impl/analysis/JavaErrorFixProvider.java | 26 ++ .../src/com/siyeh/ig/psiutils/FinalUtils.java | 4 +- .../classCanBeRecord/ConvertToRecordFix.java | 4 +- .../defUse/DefUseInspection.java | 5 +- .../quickfix/BringVariableIntoScopeFix.java | 4 +- ...reateConstructorParameterFromFieldFix.java | 4 +- .../quickfix/DefaultQuickFixProvider.java | 12 +- .../VariableAccessFromInnerClassFix.java | 2 +- ...rtCompactConstructorToCanonicalAction.java | 4 +- .../NotNullFieldNotInitializedInspection.java | 8 +- .../psi/controlFlow/ControlFlowUtil.java | 348 ++++++++++++++-- .../advHighlighting/AssignToFinal.java | 4 +- .../FieldDoubleInitialization.java | 2 +- .../advHighlighting/FinalFieldInit.java | 20 +- .../LocalVariableInitialization.java | 2 +- ...leUsedBeforeAssignmentWithParenthesis.java | 2 +- ...ghtNotHaveBeenInitializedInsideLambda.java | 4 +- .../advHighlighting8/LambdaExpressions.java | 4 +- .../lambda/highlighting/FinalInitializer.java | 2 +- .../EnhancedSwitchDefinitelyAssigned.java | 4 +- .../valueClass/ValueClass.java | 2 +- ...peUseWithTypeHighlightingUseGenerated.java | 2 +- ...seWithTypeHighlightingUseNotGenerated.java | 2 +- .../jetbrains/kotlin/nj2k/mutabilityUtils.kt | 5 +- ...uctorWithRequiredFieldsShouldBeForced.java | 6 +- 31 files changed, 455 insertions(+), 451 deletions(-) diff --git a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties index 963b613c4ff5..3f6ad8b02bc1 100644 --- a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties +++ b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties @@ -378,6 +378,8 @@ variable.must.be.final=Variable ''{0}'' is accessed from within inner class, nee variable.must.be.effectively.final=Variable ''{0}'' is accessed from within inner class, needs to be final or effectively final 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 +field.not.initialized=Field ''{0}'' might not have been initialized instanceof.type.parameter=Class or array expected instanceof.illegal.generic.type=Illegal generic type for instanceof diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ControlFlowChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ControlFlowChecker.java index 6570e618ded2..c601f3e9a728 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ControlFlowChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ControlFlowChecker.java @@ -11,13 +11,22 @@ import com.intellij.psi.impl.light.LightRecordCanonicalConstructor; import com.intellij.psi.util.JavaPsiRecordUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.BitUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + final class ControlFlowChecker { private final @NotNull JavaErrorVisitor myVisitor; + // map codeBlock->List of PsiReferenceExpression of uninitialized final variables + private final Map> myUninitializedVarProblems = new HashMap<>(); + // map codeBlock->List of PsiReferenceExpression of extra initialization of final variable + private final Map> myFinalVarProblems = new HashMap<>(); ControlFlowChecker(@NotNull JavaErrorVisitor visitor) { myVisitor = visitor; } @@ -123,6 +132,20 @@ final class ControlFlowChecker { } } + void checkFinalFieldInitialized(@NotNull PsiField field) { + if (!field.hasModifierProperty(PsiModifier.FINAL)) return; + if (ControlFlowUtil.isFieldInitializedAfterObjectConstruction(field)) return; + if (PsiUtilCore.hasErrorElementChild(field)) return; + myVisitor.report(JavaErrorKinds.FIELD_NOT_INITIALIZED.create(field)); + } + + void checkVariableInitializedBeforeUsage(@NotNull PsiReferenceExpression expression, @NotNull PsiVariable variable) { + if (ControlFlowUtil.isInitializedBeforeUsage(expression, variable, myUninitializedVarProblems, false)) { + return; + } + myVisitor.report(JavaErrorKinds.VARIABLE_NOT_INITIALIZED.create(expression, variable)); + } + /** * @return field that has initializer with this element as subexpression or null if not found */ diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java index f6b5a321ebfc..c95afda31bfa 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java @@ -506,6 +506,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { public void visitField(@NotNull PsiField field) { super.visitField(field); if (!hasErrorResults()) myClassChecker.checkIllegalInstanceMemberInRecord(field); + if (!hasErrorResults()) myControlFlowChecker.checkFinalFieldInitialized(field); } @Override @@ -701,10 +702,11 @@ final class JavaErrorVisitor extends JavaElementVisitor { PsiElement resolved = result.getElement(); PsiElement parent = expression.getParent(); PsiExpression qualifierExpression = expression.getQualifierExpression(); - if (resolved instanceof PsiVariable && resolved.getContainingFile() == expression.getContainingFile()) { + if (resolved instanceof PsiVariable variable && resolved.getContainingFile() == expression.getContainingFile()) { if (!hasErrorResults() && resolved instanceof PsiLocalVariable localVariable) { myExpressionChecker.checkVarTypeSelfReferencing(localVariable, expression); } + if (!hasErrorResults()) myControlFlowChecker.checkVariableInitializedBeforeUsage(expression, variable); } if (parent instanceof PsiMethodCallExpression methodCallExpression && methodCallExpression.getMethodExpression() == expression && diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java index b6e363f09902..07c8e3dbb2bc 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java @@ -1236,6 +1236,13 @@ public final class JavaErrorKinds { parameterized(PsiReturnStatement.class, PsiMethodCallExpression.class, "return.before.explicit.constructor.call") .withRawDescription((psi, call) -> message("return.before.explicit.constructor.call", call.getMethodExpression().getText() + "()")); + public static final Simple FIELD_NOT_INITIALIZED = + error(PsiField.class, "field.not.initialized") + .withRange(JavaErrorFormatUtil::getFieldDeclarationTextRange) + .withRawDescription(var -> message("field.not.initialized", var.getName())); + public static final Parameterized VARIABLE_NOT_INITIALIZED = + parameterized(PsiReferenceExpression.class, PsiVariable.class, "variable.not.initialized") + .withRawDescription((ref, var) -> message("variable.not.initialized", var.getName())); public static final Parameterized VARIABLE_MUST_BE_FINAL = parameterized(PsiJavaCodeReferenceElement.class, PsiVariable.class, "variable.must.be.final") .withRawDescription((ref, var) -> message("variable.must.be.final", var.getName())); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java index 59e4adefdafc..31137d8798c9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java @@ -4,23 +4,13 @@ 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.daemon.impl.quickfix.QuickFixAction; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; -import com.intellij.lang.jvm.JvmModifier; -import com.intellij.lang.jvm.actions.ChangeModifierRequest; -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.TextRange; import com.intellij.psi.*; -import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.controlFlow.*; -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.psi.util.PsiUtil; import com.intellij.util.JavaPsiConstructorUtil; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; @@ -28,7 +18,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.function.Predicate; public final class HighlightControlFlowUtil { @@ -46,106 +35,25 @@ public final class HighlightControlFlowUtil { return ControlFlowFactory.getControlFlowNoConstantEvaluate(body); } + /** + * @deprecated use {@link ControlFlowUtil#variableDefinitelyAssignedIn(PsiVariable, PsiElement)} + */ + @Deprecated + 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 isFieldInitializedAfterObjectConstruction(@NotNull PsiField field) { - if (field.hasInitializer()) return true; - boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC); - PsiClass aClass = field.getContainingClass(); - if (aClass != null) { - // field might be assigned in the other field initializers - if (isFieldInitializedInOtherFieldInitializer(aClass, field, isFieldStatic, Predicates.alwaysTrue())) return true; - } - PsiClassInitializer[] initializers; - if (aClass != null) { - initializers = aClass.getInitializers(); - } - else { - return false; - } - if (isFieldInitializedInClassInitializer(field, isFieldStatic, initializers)) return true; - if (isFieldStatic) { - return false; - } - else { - // instance field should be initialized at the end of each constructor - PsiMethod[] constructors = aClass.getConstructors(); - - if (constructors.length == 0) return false; - nextConstructor: - for (PsiMethod constructor : constructors) { - PsiCodeBlock ctrBody = constructor.getBody(); - if (ctrBody == null) return false; - for (PsiMethod redirectedConstructor : JavaPsiConstructorUtil.getChainedConstructors(constructor)) { - PsiCodeBlock body = redirectedConstructor.getBody(); - if (body != null && variableDefinitelyAssignedIn(field, body, true)) continue nextConstructor; - } - if (!ctrBody.isValid() || variableDefinitelyAssignedIn(field, ctrBody, true)) { - continue; - } - return false; - } - return true; - } - } - - private static boolean isFieldInitializedInClassInitializer(@NotNull PsiField field, - boolean isFieldStatic, - PsiClassInitializer @NotNull [] initializers) { - return ContainerUtil.find(initializers, initializer -> initializer.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic - && variableDefinitelyAssignedIn(field, initializer.getBody(), true)) != null; - } - - private static boolean isFieldInitializedInOtherFieldInitializer(@NotNull PsiClass aClass, - @NotNull PsiField field, - boolean fieldStatic, - @NotNull Predicate condition) { - for (PsiField psiField : aClass.getFields()) { - if (psiField != field - && psiField.hasModifierProperty(PsiModifier.STATIC) == fieldStatic - && variableDefinitelyAssignedIn(field, psiField, true) - && condition.test(psiField)) { - return true; - } - } - return false; - } - public static boolean isAssigned(@NotNull PsiParameter parameter) { ParamWriteProcessor processor = new ParamWriteProcessor(); ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), true).forEach(processor); return processor.isWriteRefFound(); } - /** - * @return field that has initializer with this element as subexpression or null if not found - */ - private 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; - } - - public static @NotNull TextRange getFixRange(@NotNull PsiElement element) { - PsiElement nextSibling = element.getNextSibling(); - TextRange range = element.getTextRange(); - if (PsiUtil.isJavaToken(nextSibling, JavaTokenType.SEMICOLON)) { - return range.grown(1); - } - return range; - } - private static class ParamWriteProcessor implements Processor { private volatile boolean myIsWriteRefFound; @Override @@ -163,25 +71,6 @@ public final class HighlightControlFlowUtil { } } - /** - * see JLS chapter 16 - * @return true if variable assigned (maybe more than once) - */ - public static boolean variableDefinitelyAssignedIn(@NotNull PsiVariable variable, @NotNull PsiElement context) { - return variableDefinitelyAssignedIn(variable, context, false); - } - - private static boolean variableDefinitelyAssignedIn(@NotNull PsiVariable variable, - @NotNull PsiElement context, - boolean resultOnIncompleteCode) { - try { - return ControlFlowUtil.isVariableDefinitelyAssigned(variable, getControlFlow(context)); - } - catch (AnalysisCanceledException e) { - return resultOnIncompleteCode; - } - } - private static boolean variableDefinitelyNotAssignedIn(@NotNull PsiVariable variable, @NotNull PsiElement context) { try { return ControlFlowUtil.isVariableDefinitelyNotAssigned(variable, getControlFlow(context)); @@ -191,244 +80,11 @@ public final class HighlightControlFlowUtil { } } - static HighlightInfo.Builder checkFinalFieldInitialized(@NotNull PsiField field) { - if (!field.hasModifierProperty(PsiModifier.FINAL)) return null; - if (isFieldInitializedAfterObjectConstruction(field)) return null; - if (PsiUtilCore.hasErrorElementChild(field)) return null; - String description = JavaErrorBundle.message("variable.not.initialized", field.getName()); - TextRange range = HighlightNamesUtil.getFieldDeclarationTextRange(field); - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(description); - IntentionAction action3 = getQuickFixFactory().createCreateConstructorParameterFromFieldFix(field); - builder.registerFix(action3, null, null, getFixRange(field), null); - IntentionAction action2 = getQuickFixFactory().createInitializeFinalFieldInConstructorFix(field); - builder.registerFix(action2, null, null, getFixRange(field), null); - IntentionAction action1 = getQuickFixFactory().createAddVariableInitializerFix(field); - builder.registerFix(action1, null, null, null, null); - PsiClass containingClass = field.getContainingClass(); - if (containingClass != null && !containingClass.isInterface()) { - IntentionAction action = getQuickFixFactory().createModifierListFix(field, PsiModifier.FINAL, false, false); - builder.registerFix(action, null, null, null, null); - } - return builder; - } - - - static HighlightInfo.Builder checkVariableInitializedBeforeUsage(@NotNull PsiReferenceExpression expression, - @NotNull PsiVariable variable, - @NotNull Map> uninitializedVarProblems) { - if (isInitializedBeforeUsage(expression, variable, uninitializedVarProblems, false)) return null; - return createNotInitializedError(expression, variable); - } - - public static boolean isInitializedBeforeUsage(@NotNull PsiReferenceExpression expression, - @NotNull PsiVariable variable, - @NotNull Map> uninitializedVarProblems, - boolean ignoreFinality) { - if (variable instanceof ImplicitVariable) return true; - if (!PsiUtil.isAccessedForReading(expression)) return true; - int startOffset = expression.getTextRange().getStartOffset(); - PsiElement topBlock = getTopBlock(expression, variable); - if (topBlock == null) return true; - if (!variable.hasInitializer()) { - if (variable instanceof PsiField field) { - // non-final field already initialized with default value - if (!ignoreFinality && !variable.hasModifierProperty(PsiModifier.FINAL)) return true; - // a final field may be initialized in ctor or class initializer only - // if we're inside non-ctr method, skip it - if (PsiUtil.findEnclosingConstructorOrInitializer(expression) == null - && findEnclosingFieldInitializer(expression) == null) { - return true; - } - PsiElement parent = topBlock.getParent(); - // access to final fields from inner classes always allowed - if (inInnerClass(expression, field.getContainingClass())) return true; - PsiCodeBlock block; - PsiClass aClass; - if (parent instanceof PsiMethod constructor) { - if (!constructor.getManager().areElementsEquivalent(constructor.getContainingClass(), field.getContainingClass())) return true; - // static variables already initialized in class initializers - if (variable.hasModifierProperty(PsiModifier.STATIC)) return true; - // as a last chance, field may be initialized in this() call - for (PsiMethod redirectedConstructor : JavaPsiConstructorUtil.getChainedConstructors(constructor)) { - // variable must be initialized before its usage - //??? - //if (startOffset < redirectedConstructor.getTextRange().getStartOffset()) continue; - if (JavaPsiRecordUtil.isCompactConstructor(redirectedConstructor)) return true; - PsiCodeBlock body = redirectedConstructor.getBody(); - if (body != null && variableDefinitelyAssignedIn(variable, body, true)) { - return true; - } - } - block = constructor.getBody(); - aClass = constructor.getContainingClass(); - } - else if (parent instanceof PsiClassInitializer classInitializer) { - if (!classInitializer.getManager().areElementsEquivalent(classInitializer.getContainingClass(), field.getContainingClass())) { - return true; - } - block = classInitializer.getBody(); - aClass = classInitializer.getContainingClass(); - - if (aClass == null || isFieldInitializedInOtherFieldInitializer(aClass, field, variable.hasModifierProperty(PsiModifier.STATIC), - f -> startOffset > f.getTextOffset())) { - return true; - } - } - else { - // field reference outside code block - // check variable initialized before its usage - aClass = field.getContainingClass(); - PsiField anotherField = PsiTreeUtil.getTopmostParentOfType(expression, PsiField.class); - if (aClass == null || - isFieldInitializedInOtherFieldInitializer(aClass, field, field.hasModifierProperty(PsiModifier.STATIC), - f -> f != anotherField && startOffset > f.getTextOffset())) { - return true; - } - if (anotherField != null - && !anotherField.hasModifierProperty(PsiModifier.STATIC) - && field.hasModifierProperty(PsiModifier.STATIC) - && isFieldInitializedInClassInitializer(field, true, aClass.getInitializers())) { - return true; - } - if (anotherField != null && anotherField.hasInitializer() && !PsiAugmentProvider.canTrustFieldInitializer(anotherField)) { - return true; - } - - int offset = startOffset; - if (anotherField != null && anotherField.getContainingClass() == aClass && !field.hasModifierProperty(PsiModifier.STATIC)) { - offset = 0; - } - block = null; - // initializers will be checked later - for (PsiMethod constructor : aClass.getConstructors()) { - // variable must be initialized before its usage - if (offset < constructor.getTextRange().getStartOffset()) continue; - PsiCodeBlock body = constructor.getBody(); - if (body != null && variableDefinitelyAssignedIn(variable, body)) { - return true; - } - // as a last chance, field may be initialized in this() call - for (PsiMethod redirectedConstructor : JavaPsiConstructorUtil.getChainedConstructors(constructor)) { - // variable must be initialized before its usage - if (offset < redirectedConstructor.getTextRange().getStartOffset()) continue; - PsiCodeBlock redirectedBody = redirectedConstructor.getBody(); - if (redirectedBody != null && variableDefinitelyAssignedIn(variable, redirectedBody)) { - return true; - } - } - } - } - - if (aClass != null) { - // field may be initialized in class initializer - for (PsiClassInitializer initializer : aClass.getInitializers()) { - PsiCodeBlock body = initializer.getBody(); - if (body == block) break; - // variable referenced in initializer must be initialized in initializer preceding assignment - // variable referenced in field initializer or in class initializer - boolean shouldCheckInitializerOrder = block == null || block.getParent() instanceof PsiClassInitializer; - if (shouldCheckInitializerOrder && startOffset < initializer.getTextRange().getStartOffset()) continue; - if (initializer.hasModifierProperty(PsiModifier.STATIC) == variable.hasModifierProperty(PsiModifier.STATIC)) { - if (variableDefinitelyAssignedIn(variable, body)) return true; - } - } - } - } - } - Collection codeBlockProblems = uninitializedVarProblems.get(topBlock); - if (codeBlockProblems == null) { - try { - ControlFlow controlFlow = getControlFlow(topBlock); - codeBlockProblems = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow); - } - catch (AnalysisCanceledException | IndexNotReadyException e) { - codeBlockProblems = Collections.emptyList(); - } - uninitializedVarProblems.put(topBlock, codeBlockProblems); - } - return !codeBlockProblems.contains(expression); - } - - private static @Nullable PsiElement getTopBlock(@NotNull PsiReferenceExpression expression, @NotNull PsiVariable variable) { - PsiElement topBlock; - if (variable.hasInitializer()) { - topBlock = PsiUtil.getVariableCodeBlock(variable, variable); - if (topBlock == null) return null; - } - else { - PsiElement scope = variable instanceof PsiField field - ? field.getContainingClass() - : variable.getParent() != null ? variable.getParent().getParent() : null; - while (scope instanceof PsiCodeBlock && scope.getParent() instanceof PsiSwitchBlock) { - scope = PsiTreeUtil.getParentOfType(scope, PsiCodeBlock.class); - } - - topBlock = FileTypeUtils.isInServerPageFile(scope) && scope instanceof PsiFile - ? scope - : PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope); - } - return topBlock; - } - - private static HighlightInfo.@NotNull Builder createNotInitializedError(@NotNull PsiReferenceExpression expression, - @NotNull PsiVariable variable) { - String name = expression.getElement().getText(); - String description = JavaErrorBundle.message("variable.not.initialized", name); - HighlightInfo.Builder builder = - HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description); - if (!(variable instanceof LightRecordField)) { - IntentionAction action1 = getQuickFixFactory().createAddVariableInitializerFix(variable); - builder.registerFix(action1, null, null, null, null); - } - if (variable instanceof PsiLocalVariable) { - PsiElement topBlock = getTopBlock(expression, variable); - if (topBlock != null) { - IntentionAction action = HighlightFixUtil.createInsertSwitchDefaultFix(variable, topBlock, expression); - if (action != null) { - builder.registerFix(action, null, null, null, null); - } - } - } - if (variable instanceof PsiField field) { - ChangeModifierRequest request = MemberRequestsKt.modifierRequest(JvmModifier.FINAL, false); - QuickFixAction.registerQuickFixActions(builder, null, JvmElementActionFactories.createModifierActions(field, request)); - } - return builder; - } - - private static boolean inInnerClass(@NotNull PsiElement psiElement, @Nullable PsiClass containingClass) { - for (PsiElement element = psiElement; element != null; element = element.getParent()) { - if (element instanceof PsiClass aClass) { - boolean innerClass = !psiElement.getManager().areElementsEquivalent(element, containingClass); - if (innerClass) { - if (element instanceof PsiAnonymousClass anonymous) { - if (PsiTreeUtil.isAncestor(anonymous.getArgumentList(), psiElement, false)) { - continue; - } - return !insideClassInitialization(containingClass, aClass); - } - PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(psiElement, PsiLambdaExpression.class); - return lambdaExpression == null || !insideClassInitialization(containingClass, aClass); - } - return false; - } - } - return false; - } - - private static boolean insideClassInitialization(@Nullable PsiClass containingClass, PsiClass aClass) { - PsiMember member = aClass; - while (member != null) { - if (member.getContainingClass() == containingClass) { - return member instanceof PsiField || - member instanceof PsiMethod method && method.isConstructor() || - member instanceof PsiClassInitializer; - } - member = PsiTreeUtil.getParentOfType(member, PsiMember.class, true); - } - return false; - } - + /** + * @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> finalVarProblems) { if (variable instanceof PsiLocalVariable) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index d771c36b38bf..959660163932 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -67,8 +67,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh private PreviewFeatureUtil.PreviewFeatureVisitor myPreviewFeatureVisitor; - // map codeBlock->List of PsiReferenceExpression of uninitialized final variables - private final Map> myUninitializedVarProblems = new HashMap<>(); // map codeBlock->List of PsiReferenceExpression of extra initialization of final variable private final Map> myFinalVarProblems = new HashMap<>(); @@ -159,7 +157,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } } finally { - myUninitializedVarProblems.clear(); myFinalVarProblems.clear(); myJavaModule = null; myFile = null; @@ -274,12 +271,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh if (!hasErrorResults()) GenericsHighlightUtil.checkTypeParameterOverrideEquivalentMethods(aClass, myLanguageLevel, myErrorSink, myOverrideEquivalentMethodsVisitedClasses, myOverrideEquivalentMethodsErrors); } - @Override - public void visitField(@NotNull PsiField field) { - super.visitField(field); - if (!hasErrorResults()) add(HighlightControlFlowUtil.checkFinalFieldInitialized(field)); - } - @Override public void visitImportStaticStatement(@NotNull PsiImportStaticStatement statement) { visitElement(statement); @@ -491,13 +482,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh add(HighlightControlFlowUtil.checkFinalVariableMightAlreadyHaveBeenAssignedTo(variable, expression, myFinalVarProblems)); } } - if (!hasErrorResults()) { - try { - add(HighlightControlFlowUtil.checkVariableInitializedBeforeUsage(expression, variable, myUninitializedVarProblems)); - } - catch (IndexNotReadyException ignored) { - } - } } if (!hasErrorResults()) add(HighlightUtil.checkClassReferenceAfterQualifier(expression, resolved)); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java index cdf105e3a878..540cd94451d4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java @@ -28,6 +28,7 @@ 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.LightRecordField; import com.intellij.psi.impl.light.LightRecordMethod; import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; import com.intellij.psi.infos.MethodCandidateInfo; @@ -387,6 +388,31 @@ 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())); + fixes(FIELD_NOT_INITIALIZED, (error, sink) -> { + PsiField field = error.psi(); + sink.accept(myFactory.createCreateConstructorParameterFromFieldFix(field)); + sink.accept(myFactory.createInitializeFinalFieldInConstructorFix(field)); + sink.accept(myFactory.createAddVariableInitializerFix(field)); + PsiClass containingClass = field.getContainingClass(); + if (containingClass != null && !containingClass.isInterface()) { + sink.accept(removeModifierFix(field, PsiModifier.FINAL)); + } + }); + fixes(VARIABLE_NOT_INITIALIZED, (error, sink) -> { + PsiVariable variable = error.context(); + if (!(variable instanceof LightRecordField)) { + sink.accept(myFactory.createAddVariableInitializerFix(variable)); + } + if (variable instanceof PsiLocalVariable) { + PsiElement topBlock = PsiUtil.getVariableCodeBlock(variable, null); + if (topBlock != null) { + sink.accept(HighlightFixUtil.createInsertSwitchDefaultFix(variable, topBlock, error.psi())); + } + } + if (variable instanceof PsiField field) { + sink.accept(removeModifierFix(field, PsiModifier.FINAL)); + } + }); } private void createExpressionFixes() { diff --git a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/FinalUtils.java b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/FinalUtils.java index 601829f036de..2d34d626206d 100644 --- a/java/java-analysis-impl/src/com/siyeh/ig/psiutils/FinalUtils.java +++ b/java/java-analysis-impl/src/com/siyeh/ig/psiutils/FinalUtils.java @@ -23,7 +23,7 @@ public final class FinalUtils { // parameters have an implicit initializer return !VariableAccessUtils.variableIsAssigned(variable); } - if (variable instanceof PsiField && !HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction((PsiField)variable)) { + if (variable instanceof PsiField && !ControlFlowUtil.isFieldInitializedAfterObjectConstruction((PsiField)variable)) { return false; } return checkIfElementViolatesFinality(variable); @@ -57,7 +57,7 @@ public final class FinalUtils { Map> finalVarProblems) { if (!(e instanceof PsiReferenceExpression ref)) return true; if (!ref.isReferenceTo(variable)) return true; - if (!HighlightControlFlowUtil.isInitializedBeforeUsage( + if (!ControlFlowUtil.isInitializedBeforeUsage( ref, variable, uninitializedVarProblems, true)) { return false; } diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/classCanBeRecord/ConvertToRecordFix.java b/java/java-impl-inspections/src/com/intellij/codeInspection/classCanBeRecord/ConvertToRecordFix.java index 6ed1b1fe9135..9cca9ba56e52 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/classCanBeRecord/ConvertToRecordFix.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/classCanBeRecord/ConvertToRecordFix.java @@ -4,7 +4,6 @@ package com.intellij.codeInspection.classCanBeRecord; import com.intellij.codeInsight.AnnotationTargetUtil; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.ExceptionUtil; -import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.util.IntentionFamilyName; @@ -12,6 +11,7 @@ import com.intellij.java.JavaBundle; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.PsiAnnotation.TargetType; +import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.util.JavaPsiRecordUtil; import com.intellij.psi.util.PropertyUtil; @@ -343,7 +343,7 @@ public class ConvertToRecordFix extends InspectionGadgetsFix { myCanonical = false; return; } - if (!HighlightControlFlowUtil.variableDefinitelyAssignedIn(instanceField, ctorBody)) { + if (!ControlFlowUtil.variableDefinitelyAssignedIn(instanceField, ctorBody)) { myCanonical = false; return; } diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/defUse/DefUseInspection.java b/java/java-impl-inspections/src/com/intellij/codeInspection/defUse/DefUseInspection.java index 2a603d195b45..e0ba09625ada 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/defUse/DefUseInspection.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/defUse/DefUseInspection.java @@ -2,7 +2,6 @@ package com.intellij.codeInspection.defUse; import com.intellij.codeInsight.ExpressionUtil; -import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.codeInspection.*; import com.intellij.codeInspection.dataFlow.java.ControlFlowAnalyzer; import com.intellij.codeInspection.dataFlow.java.anchor.JavaExpressionAnchor; @@ -179,7 +178,7 @@ public final class DefUseInspection extends AbstractBaseJavaLocalInspectionTool if (classInitializer.hasModifierProperty(PsiModifier.STATIC) == isStatic) { final List assignments = collectAssignments(field, classInitializer); if (!assignments.isEmpty()) { - boolean isDefinitely = HighlightControlFlowUtil.variableDefinitelyAssignedIn(field, classInitializer.getBody()); + boolean isDefinitely = ControlFlowUtil.variableDefinitelyAssignedIn(field, classInitializer.getBody()); if (isDefinitely) { try { ControlFlow flow = ControlFlowFactory.getControlFlowNoConstantEvaluate(classInitializer.getBody()); @@ -229,7 +228,7 @@ public final class DefUseInspection extends AbstractBaseJavaLocalInspectionTool for (PsiMethod constructor : constructors) { if (!JavaPsiConstructorUtil.getChainedConstructors(constructor).isEmpty()) continue; final PsiCodeBlock body = constructor.getBody(); - if (body == null || !HighlightControlFlowUtil.variableDefinitelyAssignedIn(field, body)) { + if (body == null || !ControlFlowUtil.variableDefinitelyAssignedIn(field, body)) { return false; } try { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/BringVariableIntoScopeFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/BringVariableIntoScopeFix.java index bd096677eb7d..34265140cdca 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/BringVariableIntoScopeFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/BringVariableIntoScopeFix.java @@ -2,7 +2,6 @@ package com.intellij.codeInsight.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.codeInsight.intention.PriorityAction; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; import com.intellij.codeInspection.util.IntentionName; @@ -14,6 +13,7 @@ import com.intellij.openapi.diagnostic.Logger; 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.*; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; @@ -170,7 +170,7 @@ public final class BringVariableIntoScopeFix implements ModCommandAction { outOfScopeVariable.delete(); } - if (!HighlightControlFlowUtil.isInitializedBeforeUsage(reference, addedVar, new HashMap<>(), false)) { + if (!ControlFlowUtil.isInitializedBeforeUsage(reference, addedVar, new HashMap<>(), false)) { initialize(addedVar); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java index 32eb2e9beb8b..73e4274fd855 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java @@ -5,7 +5,6 @@ import com.intellij.application.options.CodeStyle; import com.intellij.codeInsight.AnnotationTargetUtil; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.codeInsight.generation.PsiFieldMember; import com.intellij.codeInsight.generation.PsiMethodMember; import com.intellij.codeInsight.intention.impl.AssignFieldFromParameterAction; @@ -16,6 +15,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.codeStyle.*; +import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.util.CommonJavaRefactoringUtil; import com.intellij.util.JavaPsiConstructorUtil; import com.intellij.util.containers.ContainerUtil; @@ -103,7 +103,7 @@ public class CreateConstructorParameterFromFieldFix extends PsiBasedModCommandAc if (field == startField || (!field.hasModifierProperty(PsiModifier.STATIC) && field.hasModifierProperty(PsiModifier.FINAL) && - !HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction(field) && + !ControlFlowUtil.isFieldInitializedAfterObjectConstruction(field) && (constructors.isEmpty() || ContainerUtil.exists(constructors, ctr -> !isFieldAssignedInConstructor(field, ctr))))) { fields.add(field); } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DefaultQuickFixProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DefaultQuickFixProvider.java index ab65e1dc5f64..99fc206e6cb3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DefaultQuickFixProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DefaultQuickFixProvider.java @@ -2,7 +2,6 @@ package com.intellij.codeInsight.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; -import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper; import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider; @@ -51,7 +50,7 @@ public class DefaultQuickFixProvider extends UnresolvedReferenceQuickFixProvider MoveClassToModuleFix.registerFixes(registrar, ref); if (ref instanceof PsiReferenceExpression refExpr) { - TextRange fixRange = HighlightControlFlowUtil.getFixRange(ref); + TextRange fixRange = getFixRange(ref); registrar.register(fixRange, new RenameWrongRefFix(refExpr), null); PsiExpression qualifier = refExpr.getQualifierExpression(); if (qualifier != null) { @@ -186,4 +185,13 @@ public class DefaultQuickFixProvider extends UnresolvedReferenceQuickFixProvider public @NotNull Class getReferenceClass() { return PsiJavaCodeReferenceElement.class; } + + private static @NotNull TextRange getFixRange(@NotNull PsiElement element) { + PsiElement nextSibling = element.getNextSibling(); + TextRange range = element.getTextRange(); + if (PsiUtil.isJavaToken(nextSibling, JavaTokenType.SEMICOLON)) { + return range.grown(1); + } + return range; + } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java index 51b50fbcbc04..77b388c2bbb5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java @@ -317,7 +317,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { Map> finalVarProblems = new HashMap<>(); for (PsiReferenceExpression expression : references) { if (ControlFlowUtil.isVariableAssignedInLoop(expression, variable)) return false; - if (!HighlightControlFlowUtil.isInitializedBeforeUsage( + if (!ControlFlowUtil.isInitializedBeforeUsage( expression, variable, uninitializedVarProblems, false)) { return false; } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompactConstructorToCanonicalAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompactConstructorToCanonicalAction.java index d4567a821900..b8406c20a728 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompactConstructorToCanonicalAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompactConstructorToCanonicalAction.java @@ -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.codeInsight.intention.impl; -import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.codeInsight.generation.RecordConstructorMember; import com.intellij.java.JavaBundle; import com.intellij.modcommand.ActionContext; @@ -9,6 +8,7 @@ import com.intellij.modcommand.ModPsiUpdater; import com.intellij.modcommand.Presentation; import com.intellij.modcommand.PsiUpdateModCommandAction; import com.intellij.psi.*; +import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.psi.util.JavaPsiRecordUtil; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.Nls; @@ -60,7 +60,7 @@ public final class ConvertCompactConstructorToCanonicalAction extends PsiUpdateM PsiElementFactory factory = JavaPsiFacade.getElementFactory(compactConstructor.getProject()); for (PsiRecordComponent component : recordClass.getRecordComponents()) { PsiField field = JavaPsiRecordUtil.getFieldForComponent(component); - if (field != null && !HighlightControlFlowUtil.variableDefinitelyAssignedIn(field, body)) { + if (field != null && !ControlFlowUtil.variableDefinitelyAssignedIn(field, body)) { body.add(factory.createStatementFromText("this." + field.getName() + "=" + field.getName() + ";", body)); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/nullable/NotNullFieldNotInitializedInspection.java b/java/java-impl/src/com/intellij/codeInspection/nullable/NotNullFieldNotInitializedInspection.java index fd8f1ad03384..470f6160e94a 100644 --- a/java/java-impl/src/com/intellij/codeInspection/nullable/NotNullFieldNotInitializedInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/nullable/NotNullFieldNotInitializedInspection.java @@ -6,7 +6,6 @@ import com.intellij.codeInsight.NullabilityAnnotationInfo; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInsight.TestFrameworks; import com.intellij.codeInsight.daemon.impl.UnusedSymbolUtil; -import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; import com.intellij.codeInsight.daemon.impl.quickfix.AddVariableInitializerFix; import com.intellij.codeInsight.daemon.impl.quickfix.InitializeFinalFieldInConstructorFix; import com.intellij.codeInsight.intention.QuickFixFactory; @@ -19,6 +18,7 @@ import com.intellij.codeInspection.util.InspectionMessage; import com.intellij.java.JavaBundle; import com.intellij.openapi.util.text.HtmlChunk; import com.intellij.psi.*; +import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.util.JavaPsiConstructorUtil; import com.intellij.util.containers.ContainerUtil; import org.intellij.lang.annotations.Language; @@ -56,7 +56,7 @@ public class NotNullFieldNotInitializedInspection extends AbstractBaseJavaLocalI NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(field); if (info == null || info.getNullability() != Nullability.NOT_NULL) return; - if (HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction(field) || + if (ControlFlowUtil.isFieldInitializedAfterObjectConstruction(field) || isWrittenIndirectly(field)) { return; } @@ -124,7 +124,7 @@ public class NotNullFieldNotInitializedInspection extends AbstractBaseJavaLocalI if (target != null && !target.hasModifierProperty(PsiModifier.STATIC) && target.getContainingClass() == constructor.getContainingClass() && !target.isConstructor()) { PsiCodeBlock targetBody = target.getBody(); - if (targetBody != null && HighlightControlFlowUtil.variableDefinitelyAssignedIn(field, targetBody)) { + if (targetBody != null && ControlFlowUtil.variableDefinitelyAssignedIn(field, targetBody)) { return true; } } @@ -145,7 +145,7 @@ public class NotNullFieldNotInitializedInspection extends AbstractBaseJavaLocalI PsiMethod method = TestFrameworks.getInstance().findSetUpMethod(field.getContainingClass()); if (method != null) { PsiCodeBlock body = method.getBody(); - if (body != null && HighlightControlFlowUtil.variableDefinitelyAssignedIn(field, body)) { + if (body != null && ControlFlowUtil.variableDefinitelyAssignedIn(field, body)) { return true; } } diff --git a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowUtil.java b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowUtil.java index e84a3dc84bb1..83d86b370f7a 100644 --- a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowUtil.java @@ -4,9 +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.project.IndexNotReadyException; +import com.intellij.openapi.util.Predicates; import com.intellij.openapi.util.Ref; import com.intellij.psi.*; +import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.source.DummyHolder; +import com.intellij.psi.util.FileTypeUtils; +import com.intellij.psi.util.JavaPsiRecordUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.*; @@ -17,6 +22,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.IntFunction; +import java.util.function.Predicate; public final class ControlFlowUtil { private static final Logger LOG = Logger.getInstance(ControlFlowUtil.class); @@ -31,20 +37,7 @@ public final class ControlFlowUtil { * 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()}; - } + PsiElement[] scope = getVariableScope(variable); if (scope.length < 1 || scope[0] == null || scope[0].getContainingFile() != context.getContainingFile()) return null; PsiElement parent = context.getParent(); PsiElement prevParent = context; @@ -68,6 +61,24 @@ public final class ControlFlowUtil { return null; } + private static PsiElement @NotNull [] getVariableScope(@NotNull PsiVariable variable) { + 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()}; + } + return scope; + } + /** * @param variable variable to check * @param scope variable scope @@ -90,16 +101,9 @@ public final class ControlFlowUtil { } 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; - } + if (codeBlock == null) return true; + ControlFlow controlFlow = getControlFlow(codeBlock); + if (controlFlow == null) return true; Collection initializedTwice = getInitializedTwice(controlFlow); effectivelyFinal = !initializedTwice.contains(new VariableInfo(variable, null)); @@ -131,12 +135,306 @@ public final class ControlFlowUtil { return effectivelyFinal; } + private static @Nullable ControlFlow getControlFlow(PsiElement codeBlock) { + try { + LocalsOrMyInstanceFieldsControlFlowPolicy policy = LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(); + return ControlFlowFactory.getControlFlow( + codeBlock, policy, ControlFlowOptions.create(true, true, true)); + } + catch (AnalysisCanceledException e) { + return null; + } + } + private static boolean variableIsAssigned(@NotNull PsiVariable variable, @NotNull PsiElement scope) { return !PsiTreeUtil.processElements(scope, PsiReferenceExpression.class, e -> { return !(PsiUtil.isAccessedForWriting(e) && e.isReferenceTo(variable)); }); } + /** + * @param field field to check + * @return true if the field is initialized (in class initializer, own initializer, another field initializer, or constructor) + */ + public static boolean isFieldInitializedAfterObjectConstruction(@NotNull PsiField field) { + if (field.hasInitializer()) return true; + boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC); + PsiClass aClass = field.getContainingClass(); + if (aClass == null) return false; + // field might be assigned in the other field initializers + if (isFieldInitializedInOtherFieldInitializer(aClass, field, Predicates.alwaysTrue())) return true; + if (isFieldInitializedInClassInitializer(field)) return true; + if (isFieldStatic) return false; + // instance field should be initialized at the end of each constructor + PsiMethod[] constructors = aClass.getConstructors(); + + if (constructors.length == 0) return false; + nextConstructor: + for (PsiMethod constructor : constructors) { + PsiCodeBlock ctrBody = constructor.getBody(); + if (ctrBody == null) return false; + for (PsiMethod redirectedConstructor : JavaPsiConstructorUtil.getChainedConstructors(constructor)) { + PsiCodeBlock body = redirectedConstructor.getBody(); + if (body != null && variableDefinitelyAssignedIn(field, body, true)) continue nextConstructor; + } + if (!ctrBody.isValid() || variableDefinitelyAssignedIn(field, ctrBody, true)) { + continue; + } + return false; + } + return true; + } + + /** + * @param field field to check + * @return true if the field is initialized in its class initializers + */ + private static boolean isFieldInitializedInClassInitializer(@NotNull PsiField field) { + PsiClass aClass = field.getContainingClass(); + if (aClass == null) return false; + PsiClassInitializer[] initializers = aClass.getInitializers(); + boolean isFieldStatic = field.hasModifierProperty(PsiModifier.STATIC); + return ContainerUtil.find(initializers, initializer -> initializer.hasModifierProperty(PsiModifier.STATIC) == isFieldStatic + && variableDefinitelyAssignedIn(field, initializer.getBody(), true)) != null; + } + + private static boolean isFieldInitializedInOtherFieldInitializer(@NotNull PsiClass aClass, + @NotNull PsiField field, + @NotNull Predicate condition) { + boolean fieldStatic = field.hasModifierProperty(PsiModifier.STATIC); + for (PsiField psiField : aClass.getFields()) { + if (psiField != field + && psiField.hasModifierProperty(PsiModifier.STATIC) == fieldStatic + && variableDefinitelyAssignedIn(field, psiField, true) + && condition.test(psiField)) { + return true; + } + } + return false; + } + + /** + * @return field that has initializer with this element as subexpression or null if not found + */ + private static PsiField findEnclosingFieldInitializer(@NotNull PsiElement entry) { + PsiElement element = entry; + while (element != null) { + PsiElement parent = element.getParent(); + if (parent instanceof PsiField) { + PsiField field = (PsiField)parent; + if (element == field.getInitializer()) return field; + if (field instanceof PsiEnumConstant && element == ((PsiEnumConstant)field).getArgumentList()) return field; + } + if (element instanceof PsiClass || element instanceof PsiMethod) return null; + element = parent; + } + return null; + } + + /** + * see JLS chapter 16 + * @param variable variable to check + * @param scope variable scope (code block, field initializer, etc.) + * @return true if variable assigned (maybe more than once) + */ + public static boolean variableDefinitelyAssignedIn(@NotNull PsiVariable variable, @NotNull PsiElement scope) { + return variableDefinitelyAssignedIn(variable, scope, false); + } + + private static boolean variableDefinitelyAssignedIn(@NotNull PsiVariable variable, + @NotNull PsiElement scope, + boolean resultOnIncompleteCode) { + ControlFlow flow = getControlFlow(scope); + return flow == null ? resultOnIncompleteCode : isVariableDefinitelyAssigned(variable, flow); + } + + /** + * @param expression variable reference (usage) + * @param variable variable + * @param uninitializedVarProblems map to cache results from the same code block + * @param treatNonFinalFieldsAsNonInitialized if true, the non-final field will not be considered as initialized with the default value + * @return true if the variable is initialized before usage + */ + public static boolean isInitializedBeforeUsage(@NotNull PsiReferenceExpression expression, + @NotNull PsiVariable variable, + @NotNull Map> uninitializedVarProblems, + boolean treatNonFinalFieldsAsNonInitialized) { + if (variable instanceof ImplicitVariable) return true; + if (!PsiUtil.isAccessedForReading(expression)) return true; + int startOffset = expression.getTextRange().getStartOffset(); + PsiElement topBlock = getTopBlock(expression, variable); + if (topBlock == null) return true; + if (!variable.hasInitializer()) { + if (variable instanceof PsiField) { + PsiField field = (PsiField)variable; + // non-final field already initialized with default value + if (!treatNonFinalFieldsAsNonInitialized && !variable.hasModifierProperty(PsiModifier.FINAL)) return true; + // a final field may be initialized in ctor or class initializer only + // if we're inside a non-constructor method, skip it + if (PsiUtil.findEnclosingConstructorOrInitializer(expression) == null + && findEnclosingFieldInitializer(expression) == null) { + return true; + } + PsiElement parent = topBlock.getParent(); + // access to final fields from inner classes always allowed + if (inInnerClass(expression, field.getContainingClass())) return true; + PsiCodeBlock block; + PsiClass aClass; + if (parent instanceof PsiMethod) { + PsiMethod constructor = (PsiMethod)parent; + if (!constructor.getManager().areElementsEquivalent(constructor.getContainingClass(), field.getContainingClass())) return true; + // static variables already initialized in class initializers + if (variable.hasModifierProperty(PsiModifier.STATIC)) return true; + // as a last chance, the field may be initialized in this() call + for (PsiMethod redirectedConstructor : JavaPsiConstructorUtil.getChainedConstructors(constructor)) { + // variable must be initialized before its usage + //??? + //if (startOffset < redirectedConstructor.getTextRange().getStartOffset()) continue; + if (JavaPsiRecordUtil.isCompactConstructor(redirectedConstructor)) return true; + PsiCodeBlock body = redirectedConstructor.getBody(); + if (body != null && variableDefinitelyAssignedIn(variable, body, true)) { + return true; + } + } + block = constructor.getBody(); + aClass = constructor.getContainingClass(); + } + else if (parent instanceof PsiClassInitializer) { + PsiClassInitializer classInitializer = (PsiClassInitializer)parent; + if (!classInitializer.getManager().areElementsEquivalent(classInitializer.getContainingClass(), field.getContainingClass())) { + return true; + } + block = classInitializer.getBody(); + aClass = classInitializer.getContainingClass(); + + if (aClass == null || isFieldInitializedInOtherFieldInitializer(aClass, field, f -> startOffset > f.getTextOffset())) { + return true; + } + } + else { + // field reference outside code block + // check variable initialized before its usage + aClass = field.getContainingClass(); + PsiField anotherField = PsiTreeUtil.getTopmostParentOfType(expression, PsiField.class); + if (aClass == null || + isFieldInitializedInOtherFieldInitializer(aClass, field, f -> f != anotherField && startOffset > f.getTextOffset())) { + return true; + } + if (anotherField != null + && !anotherField.hasModifierProperty(PsiModifier.STATIC) + && field.hasModifierProperty(PsiModifier.STATIC) + && isFieldInitializedInClassInitializer(field)) { + return true; + } + if (anotherField != null && anotherField.hasInitializer() && !PsiAugmentProvider.canTrustFieldInitializer(anotherField)) { + return true; + } + + int offset = startOffset; + if (anotherField != null && anotherField.getContainingClass() == aClass && !field.hasModifierProperty(PsiModifier.STATIC)) { + offset = 0; + } + block = null; + // initializers will be checked later + for (PsiMethod constructor : aClass.getConstructors()) { + // the variable must be initialized before its usage + if (offset < constructor.getTextRange().getStartOffset()) continue; + PsiCodeBlock body = constructor.getBody(); + if (body != null && variableDefinitelyAssignedIn(variable, body)) { + return true; + } + // as a last chance, the field may be initialized in this() call + for (PsiMethod redirectedConstructor : JavaPsiConstructorUtil.getChainedConstructors(constructor)) { + // the variable must be initialized before its usage + if (offset < redirectedConstructor.getTextRange().getStartOffset()) continue; + PsiCodeBlock redirectedBody = redirectedConstructor.getBody(); + if (redirectedBody != null && variableDefinitelyAssignedIn(variable, redirectedBody)) { + return true; + } + } + } + } + + if (aClass != null) { + // field may be initialized in class initializer + for (PsiClassInitializer initializer : aClass.getInitializers()) { + PsiCodeBlock body = initializer.getBody(); + if (body == block) break; + // variable referenced in initializer must be initialized in initializer preceding assignment + // variable referenced in field initializer or in class initializer + boolean shouldCheckInitializerOrder = block == null || block.getParent() instanceof PsiClassInitializer; + if (shouldCheckInitializerOrder && startOffset < initializer.getTextRange().getStartOffset()) continue; + if (initializer.hasModifierProperty(PsiModifier.STATIC) == variable.hasModifierProperty(PsiModifier.STATIC)) { + if (variableDefinitelyAssignedIn(variable, body)) return true; + } + } + } + } + } + Collection codeBlockProblems = uninitializedVarProblems.get(topBlock); + if (codeBlockProblems == null) { + try { + ControlFlow controlFlow = getControlFlow(topBlock); + codeBlockProblems = controlFlow == null ? Collections.emptyList() : getReadBeforeWriteLocals(controlFlow); + } + catch (IndexNotReadyException e) { + codeBlockProblems = Collections.emptyList(); + } + uninitializedVarProblems.put(topBlock, codeBlockProblems); + } + return !codeBlockProblems.contains(expression); + } + + private static @Nullable PsiElement getTopBlock(@NotNull PsiReferenceExpression expression, @NotNull PsiVariable variable) { + if (variable.hasInitializer()) { + return PsiUtil.getVariableCodeBlock(variable, null); + } + PsiElement scope = variable instanceof PsiField + ? ((PsiField)variable).getContainingClass() + : variable.getParent() != null ? variable.getParent().getParent() : null; + while (scope instanceof PsiCodeBlock && scope.getParent() instanceof PsiSwitchBlock) { + scope = PsiTreeUtil.getParentOfType(scope, PsiCodeBlock.class); + } + + return FileTypeUtils.isInServerPageFile(scope) && scope instanceof PsiFile + ? scope + : PsiUtil.getTopLevelEnclosingCodeBlock(expression, scope); + } + + private static boolean inInnerClass(@NotNull PsiElement psiElement, @Nullable PsiClass containingClass) { + for (PsiElement element = psiElement; element != null; element = element.getParent()) { + if (element instanceof PsiClass) { + PsiClass aClass = (PsiClass)element; + boolean innerClass = !psiElement.getManager().areElementsEquivalent(element, containingClass); + if (innerClass) { + if (element instanceof PsiAnonymousClass) { + if (PsiTreeUtil.isAncestor(((PsiAnonymousClass)element).getArgumentList(), psiElement, false)) { + continue; + } + return !insideClassInitialization(containingClass, aClass); + } + PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(psiElement, PsiLambdaExpression.class); + return lambdaExpression == null || !insideClassInitialization(containingClass, aClass); + } + return false; + } + } + return false; + } + + private static boolean insideClassInitialization(@Nullable PsiClass containingClass, PsiClass aClass) { + PsiMember member = aClass; + while (member != null) { + if (member.getContainingClass() == containingClass) { + return member instanceof PsiField || + member instanceof PsiMethod && ((PsiMethod)member).isConstructor() || + member instanceof PsiClassInitializer; + } + member = PsiTreeUtil.getParentOfType(member, PsiMember.class, true); + } + return false; + } + private static class SSAInstructionState { private final int myWriteCount; private final int myInstructionIdx; @@ -387,7 +685,7 @@ public final class ControlFlowUtil { } /** - * If the variable occurs only once in the element and it's read access return that occurrence + * If the variable occurs only once in the element, and it's read access return that occurrence */ public static PsiReferenceExpression findSingleReadOccurrence(@NotNull ControlFlow flow, @NotNull PsiElement element, diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/AssignToFinal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/AssignToFinal.java index d8d754234254..bafb320ab464 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/AssignToFinal.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/AssignToFinal.java @@ -223,7 +223,7 @@ class AssignmentInUnreachablePolyadic { if (true && (b = false)) ; } - private final boolean bb; + private final boolean bb; { if (false && (bb = false) && (bb = true)) ; } @@ -255,7 +255,7 @@ class QualifiedThis { class ParenthesizedThis { final int x; - final int y = (this).x + 1; + final int y = (this).x + 1; ParenthesizedThis() { (this).x = 5; // javac disallows this -- probably a bug in javac diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FieldDoubleInitialization.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FieldDoubleInitialization.java index e340fa84fdec..2fecc6319565 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FieldDoubleInitialization.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FieldDoubleInitialization.java @@ -54,7 +54,7 @@ class c4 { } // redirected ctrs class c5 { - final int k; + final int k; c5(int i) { k =1; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FinalFieldInit.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FinalFieldInit.java index a4f70378fe2f..ba51b93cc568 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FinalFieldInit.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FinalFieldInit.java @@ -5,15 +5,15 @@ class a { /** * javadoc should not be highlighted */ - final int javaDoced; + final int javaDoced; - static final int sfi1; - static final int sfi2; - final int fi1; - final int fi2; + static final int sfi1; + static final int sfi2; + final int fi1; + final int fi2; class inner { - final int fii; + final int fii; } final int fi3; final int fi4; @@ -140,7 +140,7 @@ class c2 { } class UninitializedFinal2 { - private final String s; + private final String s; UninitializedFinal2(){ try { @@ -151,7 +151,7 @@ class UninitializedFinal2 { } class UninitedFinalFied { - private final String string; + private final String string; public UninitedFinalFied() throws IOException { init(); @@ -277,7 +277,7 @@ class InitializedInClassInitializerUsedInTheFollowingFieldInitializer { } class AssignInAssert { - private final boolean b; + private final boolean b; AssignInAssert() { assert b = true; @@ -301,7 +301,7 @@ class DefiniteAssignmentInFinally { } class StaticInitializerUsedInAnotherInstanceField { private final int myEnumerationCacheConstant = ENUMERATION_CACHE_SIZE; - private static final int ourEnumerationCacheConstant = ENUMERATION_CACHE_SIZE; + private static final int ourEnumerationCacheConstant = ENUMERATION_CACHE_SIZE; private static final int ENUMERATION_CACHE_SIZE; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/LocalVariableInitialization.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/LocalVariableInitialization.java index 8947d019a025..f80db76e29d8 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/LocalVariableInitialization.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/LocalVariableInitialization.java @@ -122,7 +122,7 @@ public class a { public String getTreeCellRendererComponent(String value) { if (value instanceof String) { - int i = myLabel.length(); + int i = myLabel.length(); } return null; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/VariableUsedBeforeAssignmentWithParenthesis.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/VariableUsedBeforeAssignmentWithParenthesis.java index 46157f4d39a8..5ba1cf8361fb 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/VariableUsedBeforeAssignmentWithParenthesis.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting6/VariableUsedBeforeAssignmentWithParenthesis.java @@ -5,7 +5,7 @@ class Unassigned { public int b; Unassigned(int value) { - b = (this).a; + b = (this).a; a = value; } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting8/FinalVariableMightNotHaveBeenInitializedInsideLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting8/FinalVariableMightNotHaveBeenInitializedInsideLambda.java index 0b1a7693376f..c5842477aab0 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting8/FinalVariableMightNotHaveBeenInitializedInsideLambda.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting8/FinalVariableMightNotHaveBeenInitializedInsideLambda.java @@ -92,11 +92,11 @@ class TestAnonymousWithRefToTheTopLevelUninitializedField { class TestThisQualified { final String s; - final Runnable r = () -> System.out.println(this.s.length()); + final Runnable r = () -> System.out.println(this.s.length()); final Runnable r2 = () -> System.out.println(this.r2); final Runnable r3; { - r3 = () -> System.out.println(this.r3); + r3 = () -> System.out.println(this.r3); } public TestThisQualified() { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting8/LambdaExpressions.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting8/LambdaExpressions.java index 4b6ad938406f..7bc8ce622509 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting8/LambdaExpressions.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting8/LambdaExpressions.java @@ -11,9 +11,9 @@ class Test { private final IntFunction multiply = i -> i * Test.this.idx; - private final int idx3 = this.idx + 1; + private final int idx3 = this.idx + 1; - private final IntFunction multiply2 = i -> i * this.idx; + private final IntFunction multiply2 = i -> i * this.idx; public Test(int idx) { this.idx = idx; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/FinalInitializer.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/FinalInitializer.java index 4a276cea328e..b0ff4b7dbc76 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/FinalInitializer.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/FinalInitializer.java @@ -1,6 +1,6 @@ class MyTest { final Runnable lambdaRunnable = () -> { - System.out.println(o); + System.out.println(o); }; final Object o; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/EnhancedSwitchDefinitelyAssigned.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/EnhancedSwitchDefinitelyAssigned.java index 01e3e3a6eb77..528802bf472f 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/EnhancedSwitchDefinitelyAssigned.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/switchExpressions/EnhancedSwitchDefinitelyAssigned.java @@ -69,7 +69,7 @@ class C { static class FinalFieldAssignedInSomeBranches { - final int n; + final int n; { switch ((int)Math.random()) { case 1 -> n = 1; @@ -79,7 +79,7 @@ class C { } static class FinalFieldAssignedInSomeBranchesNoDefault { - final int n; + final int n; { switch ((int)Math.random()) { case 1 -> n = 1; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/valueClass/ValueClass.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/valueClass/ValueClass.java index 27a300896d12..e8d824810fec 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/valueClass/ValueClass.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/valueClass/ValueClass.java @@ -1,6 +1,6 @@ value class One { - private int value; + private int value; synchronized void x() {} } diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/style/missorted_modifiers/TypeUseWithTypeHighlightingUseGenerated.java b/java/java-tests/testData/ig/com/siyeh/igtest/style/missorted_modifiers/TypeUseWithTypeHighlightingUseGenerated.java index 130802dcd805..83ac0f966e1c 100644 --- a/java/java-tests/testData/ig/com/siyeh/igtest/style/missorted_modifiers/TypeUseWithTypeHighlightingUseGenerated.java +++ b/java/java-tests/testData/ig/com/siyeh/igtest/style/missorted_modifiers/TypeUseWithTypeHighlightingUseGenerated.java @@ -8,7 +8,7 @@ class TypeUseWithTypeHighlighting { @ReadOnly private String fieldOneLine; - private @ReadOnly final String field1; + private @ReadOnly final String field1; private final @ReadOnly String field2 = "2"; } @Target({ElementType.TYPE_USE, ElementType.FIELD}) diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/style/missorted_modifiers/TypeUseWithTypeHighlightingUseNotGenerated.java b/java/java-tests/testData/ig/com/siyeh/igtest/style/missorted_modifiers/TypeUseWithTypeHighlightingUseNotGenerated.java index 1801c855ed19..2615fe77c298 100644 --- a/java/java-tests/testData/ig/com/siyeh/igtest/style/missorted_modifiers/TypeUseWithTypeHighlightingUseNotGenerated.java +++ b/java/java-tests/testData/ig/com/siyeh/igtest/style/missorted_modifiers/TypeUseWithTypeHighlightingUseNotGenerated.java @@ -8,7 +8,7 @@ class TypeUseWithTypeHighlighting { @ReadOnly private String fieldOneLine; - private @ReadOnly final String field1; + private @ReadOnly final String field1; private final @ReadOnly String field2 = "2"; } @Target({ElementType.TYPE_USE, ElementType.FIELD}) diff --git a/plugins/kotlin/j2k/shared/src/org/jetbrains/kotlin/nj2k/mutabilityUtils.kt b/plugins/kotlin/j2k/shared/src/org/jetbrains/kotlin/nj2k/mutabilityUtils.kt index a8a610eb2cb1..d6100b497ff0 100644 --- a/plugins/kotlin/j2k/shared/src/org/jetbrains/kotlin/nj2k/mutabilityUtils.kt +++ b/plugins/kotlin/j2k/shared/src/org/jetbrains/kotlin/nj2k/mutabilityUtils.kt @@ -2,7 +2,6 @@ package org.jetbrains.kotlin.nj2k -import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil import com.intellij.codeInspection.localCanBeFinal.LocalCanBeFinal import com.intellij.psi.* import com.intellij.psi.controlFlow.ControlFlowUtil @@ -186,7 +185,7 @@ private fun fieldConstructionImpliesMutable(field: PsiField): Boolean { val usefulRedirectedConstructors: MutableList = ArrayList() for (redirectedConstructor in redirectedConstructors) { val body = redirectedConstructor.getBody() - if (body != null && (HighlightControlFlowUtil.variableDefinitelyAssignedIn(field, body) || isValidThisMethodInConstructor( + if (body != null && (ControlFlowUtil.variableDefinitelyAssignedIn(field, body) || isValidThisMethodInConstructor( redirectedConstructor )) ) { @@ -194,7 +193,7 @@ private fun fieldConstructionImpliesMutable(field: PsiField): Boolean { } } if (usefulRedirectedConstructors.isNotEmpty() && usefulRedirectedConstructors.size != redirectedConstructors.size) return true - if (ctrBody.isValid() && (HighlightControlFlowUtil.variableDefinitelyAssignedIn(field, ctrBody) || isValidThisMethodInConstructor( + if (ctrBody.isValid() && (ControlFlowUtil.variableDefinitelyAssignedIn(field, ctrBody) || isValidThisMethodInConstructor( constructor )) ) { diff --git a/plugins/lombok/testData/inspection/diverse/NoArgsConstructorWithRequiredFieldsShouldBeForced.java b/plugins/lombok/testData/inspection/diverse/NoArgsConstructorWithRequiredFieldsShouldBeForced.java index c2cef48cb49e..8946776eb443 100644 --- a/plugins/lombok/testData/inspection/diverse/NoArgsConstructorWithRequiredFieldsShouldBeForced.java +++ b/plugins/lombok/testData/inspection/diverse/NoArgsConstructorWithRequiredFieldsShouldBeForced.java @@ -1,6 +1,6 @@ @lombok.NoArgsConstructor public class NoArgsConstructorWithRequiredFieldsShouldBeForced { - private final String test; - private final String test2; - private final int test3; + private final String test; + private final String test2; + private final int test3; } \ No newline at end of file