diff --git a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties index 40d5b71706db..bd1521eff485 100644 --- a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties +++ b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties @@ -377,6 +377,7 @@ pattern.instanceof.supertype=Pattern type ''{0}'' is a supertype of expression t pattern.instanceof.equals=Pattern type ''{0}'' is the same as expression type variable.must.be.final=Variable ''{0}'' is accessed from within inner class, needs to be declared final +variable.must.be.final.resource=Variable used as a try-with-resources resource should be final or effectively final 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 @@ -411,6 +412,8 @@ expression.qualified.class.expected=Class name expected here expression.class.type.parameter=Cannot access class object of a type parameter expression.class.parameterized.type=Cannot access class object of parameterized type +resource.declaration.or.variable.expected=Declaration, final or effectively final variable expected + assignment.declared.outside.guard=Cannot assign a value to variable ''{0}'', because it is declared outside the guard assignment.to.final.variable=Cannot assign a value to final variable ''{0}'' lvalue.variable.expected=Variable expected diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java index 570018aadf43..2263931faa6f 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java @@ -11,6 +11,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.pom.java.JavaFeature; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.psi.impl.IncompleteModelUtil; import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; @@ -788,6 +789,29 @@ final class ExpressionChecker { } } + void checkResourceVariableIsFinal(@NotNull PsiResourceExpression resource) { + PsiExpression expression = resource.getExpression(); + + if (expression instanceof PsiThisExpression) return; + + if (expression instanceof PsiReferenceExpression ref) { + PsiElement target = ref.resolve(); + if (target == null) return; + + if (target instanceof PsiVariable variable) { + PsiModifierList modifierList = variable.getModifierList(); + if (modifierList != null && modifierList.hasModifierProperty(PsiModifier.FINAL)) return; + + if (!(variable instanceof PsiField) && ControlFlowUtil.isEffectivelyFinal(variable, resource)) return; + } + + myVisitor.report(JavaErrorKinds.VARIABLE_MUST_BE_FINAL_RESOURCE.create(ref)); + return; + } + + myVisitor.report(JavaErrorKinds.RESOURCE_DECLARATION_OR_VARIABLE_EXPECTED.create(expression)); + } + private static boolean isIntersection(@NotNull PsiTypeElement castTypeElement, @NotNull PsiType castType) { if (castType instanceof PsiIntersectionType) return true; return castType instanceof PsiClassType && PsiTreeUtil.getChildrenOfType(castTypeElement, PsiTypeElement.class) != null; 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 6e5303cb8af8..e5862f9f7eaa 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 @@ -654,6 +654,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (!hasErrorResults()) checkFeature(resource, JavaFeature.REFS_AS_RESOURCE); if (!hasErrorResults()) myStatementChecker.checkTryResourceIsAutoCloseable(resource); if (!hasErrorResults()) myExpressionChecker.checkUnhandledCloserExceptions(resource); + if (!hasErrorResults()) myExpressionChecker.checkResourceVariableIsFinal(resource); } 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 b25db2094739..45d3587a0828 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 @@ -853,6 +853,9 @@ public final class JavaErrorKinds { requireNonNull(ctx.type1()).getPresentableText(), requireNonNull(ctx.type2()).getPresentableText())); + public static final Simple RESOURCE_DECLARATION_OR_VARIABLE_EXPECTED = + error("resource.declaration.or.variable.expected"); + public static final Simple EXPRESSION_EXPECTED = error("expression.expected"); public static final Parameterized EXPRESSION_SUPER_UNQUALIFIED_DEFAULT_METHOD = parameterized("expression.super.unqualified.default.method"); @@ -1288,6 +1291,8 @@ public final class JavaErrorKinds { 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())); + public static final Simple VARIABLE_MUST_BE_FINAL_RESOURCE = + error(PsiReferenceExpression.class, "variable.must.be.final.resource"); public static final Parameterized VARIABLE_MUST_BE_EFFECTIVELY_FINAL = parameterized(PsiJavaCodeReferenceElement.class, PsiVariable.class, "variable.must.be.effectively.final") .withRawDescription((ref, var) -> message("variable.must.be.effectively.final", var.getName())); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 2a10560a6bda..c0f03bb1752b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -28,7 +28,6 @@ import com.intellij.openapi.util.text.HtmlChunk; import com.intellij.pom.java.JavaFeature; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; -import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.psi.impl.IncompleteModelUtil; import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; import com.intellij.psi.search.GlobalSearchScope; @@ -257,33 +256,6 @@ public final class HighlightUtil { return container == null ? "?" : HighlightMessageUtil.getSymbolName(container, substitutor); } - static HighlightInfo.Builder checkResourceVariableIsFinal(@NotNull PsiResourceExpression resource) { - PsiExpression expression = resource.getExpression(); - - if (expression instanceof PsiThisExpression) return null; - - if (expression instanceof PsiReferenceExpression ref) { - PsiElement target = ref.resolve(); - if (target == null) return null; - - if (target instanceof PsiVariable variable) { - PsiModifierList modifierList = variable.getModifierList(); - if (modifierList != null && modifierList.hasModifierProperty(PsiModifier.FINAL)) return null; - - if (!(variable instanceof PsiField) && ControlFlowUtil.isEffectivelyFinal(variable, resource)) { - return null; - } - } - - String text = JavaErrorBundle.message("resource.variable.must.be.final"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(text); - } - - String text = JavaErrorBundle.message("declaration.or.variable.expected"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(text); - } - - static void checkSwitchExpressionHasResult(@NotNull PsiSwitchExpression switchExpression, @NotNull Consumer errorSink) { PsiCodeBlock switchBody = switchExpression.getBody(); 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 72bb198b61db..c6ee7f354921 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 @@ -489,12 +489,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh if (!hasErrorResults()) model.checkSwitchLabelValues(myErrorSink); } - @Override - public void visitResourceExpression(@NotNull PsiResourceExpression resource) { - super.visitResourceExpression(resource); - if (!hasErrorResults()) add(HighlightUtil.checkResourceVariableIsFinal(resource)); - } - @Override public void visitTypeElement(@NotNull PsiTypeElement type) { super.visitTypeElement(type);