[java-highlighting] checkVariableExpected; yield checks migrated

Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: e4261df8c429a46d6aa238b7dcb4a97fdc45f9eb
This commit is contained in:
Tagir Valeev
2025-01-30 09:37:04 +00:00
committed by intellij-monorepo-bot
parent d50a093e84
commit 2b4707f2ef
9 changed files with 74 additions and 72 deletions
@@ -212,6 +212,9 @@ break.out.of.switch.expression=Break out of switch expression is not allowed
continue.outside.loop='continue' statement outside of loop
continue.out.of.switch.expression=Continue out of switch expression is not allowed
yield.unexpected='yield' outside of a switch expression
yield.void=Expression type should not be 'void'
foreach.not.applicable=Foreach not applicable to type ''{0}''
new.expression.qualified.malformed=Invalid qualified new
@@ -322,6 +325,7 @@ expression.super.unqualified.default.method=Unqualified super reference is not a
expression.qualified.class.expected=Class name expected here
assignment.declared.outside.guard=Cannot assign a value to variable ''{0}'', because it is declared outside the guard
lvalue.variable.expected=Variable expected
binary.operator.not.applicable=Operator ''{0}'' cannot be applied to ''{1}'', ''{2}''
unary.operator.not.applicable=Operator ''{0}'' cannot be applied to ''{1}''
@@ -707,6 +707,25 @@ final class ExpressionChecker {
}
}
void checkVariableExpected(@NotNull PsiExpression expression) {
PsiExpression lValue;
if (expression instanceof PsiAssignmentExpression assignment) {
lValue = assignment.getLExpression();
}
else if (PsiUtil.isIncrementDecrementOperation(expression)) {
lValue = ((PsiUnaryExpression)expression).getOperand();
}
else {
lValue = null;
}
if (lValue != null && !TypeConversionUtil.isLValue(lValue) && !PsiTreeUtil.hasErrorElements(expression) &&
!(IncompleteModelUtil.isIncompleteModel(expression) &&
PsiUtil.skipParenthesizedExprDown(lValue) instanceof PsiReferenceExpression ref &&
IncompleteModelUtil.canBePendingReference(ref))) {
myVisitor.report(JavaErrorKinds.LVALUE_VARIABLE_EXPECTED.create(lValue));
}
}
static boolean isArrayDeclaration(@NotNull PsiVariable variable) {
// Java-style 'var' arrays are prohibited by the parser; for C-style ones, looking for a bracket is enough
return ContainerUtil.or(variable.getChildren(), e -> PsiUtil.isJavaToken(e, JavaTokenType.LBRACKET));
@@ -4,6 +4,7 @@ package com.intellij.java.codeserver.highlighting;
import com.intellij.codeInsight.UnhandledExceptions;
import com.intellij.java.codeserver.highlighting.errors.JavaCompilationError;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
@@ -14,6 +15,7 @@ import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.javadoc.PsiDocComment;
@@ -139,6 +141,30 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) myStatementChecker.checkBreakTarget(statement);
}
@Override
public void visitYieldStatement(@NotNull PsiYieldStatement statement) {
super.visitYieldStatement(statement);
if (!hasErrorResults()) myStatementChecker.checkYieldOutsideSwitchExpression(statement);
if (!hasErrorResults()) {
PsiExpression expression = statement.getExpression();
if (expression != null) {
myStatementChecker.checkYieldExpressionType(expression);
}
}
}
@Override
public void visitExpressionStatement(@NotNull PsiExpressionStatement statement) {
super.visitExpressionStatement(statement);
PsiElement parent = statement.getParent();
if (parent instanceof PsiSwitchLabeledRuleStatement ruleStatement) {
PsiSwitchBlock switchBlock = ruleStatement.getEnclosingSwitchBlock();
if (switchBlock instanceof PsiSwitchExpression expr && !PsiPolyExpressionUtil.isPolyExpression(expr)) {
myStatementChecker.checkYieldExpressionType(statement.getExpression());
}
}
}
@Override
public void visitContinueStatement(@NotNull PsiContinueStatement statement) {
super.visitContinueStatement(statement);
@@ -788,6 +814,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
@Override
public void visitExpression(@NotNull PsiExpression expression) {
ProgressManager.checkCanceled(); // visitLiteralExpression is invoked very often in array initializers
super.visitExpression(expression);
PsiElement parent = expression.getParent();
// Method expression of the call should not be especially processed
@@ -805,7 +832,12 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults() && expression.getParent() instanceof PsiThrowStatement statement && statement.getException() == expression) {
myTypeChecker.checkMustBeThrowable(expression, expression.getType());
}
if (!hasErrorResults() && parent instanceof PsiNewExpression newExpression &&
newExpression.getQualifier() != expression && newExpression.getArrayInitializer() != expression) {
myExpressionChecker.checkAssignability(PsiTypes.intType(), expression.getType(), expression, expression); // like in 'new String["s"]'
}
if (!hasErrorResults()) myStatementChecker.checkForeachExpressionTypeIsIterable(expression);
if (!hasErrorResults()) myExpressionChecker.checkVariableExpected(expression);
}
@Override
@@ -384,4 +384,16 @@ final class StatementChecker {
}
}
}
void checkYieldOutsideSwitchExpression(@NotNull PsiYieldStatement statement) {
if (statement.findEnclosingExpression() == null) {
myVisitor.report(JavaErrorKinds.YIELD_UNEXPECTED.create(statement));
}
}
void checkYieldExpressionType(@NotNull PsiExpression expression) {
if (PsiTypes.voidType().equals(expression.getType())) {
myVisitor.report(JavaErrorKinds.YIELD_VOID.create(expression));
}
}
}
@@ -662,6 +662,8 @@ public final class JavaErrorKinds {
public static final Simple<PsiBreakStatement> BREAK_OUT_OF_SWITCH_EXPRESSION = error("break.out.of.switch.expression");
public static final Simple<PsiContinueStatement> CONTINUE_OUTSIDE_LOOP = error("continue.outside.loop");
public static final Simple<PsiContinueStatement> CONTINUE_OUT_OF_SWITCH_EXPRESSION = error("continue.out.of.switch.expression");
public static final Simple<PsiYieldStatement> YIELD_UNEXPECTED = error("yield.unexpected");
public static final Simple<PsiExpression> YIELD_VOID = error("yield.void");
public static final Simple<PsiTypeElement> CATCH_TYPE_PARAMETER = error("catch.type.parameter");
@@ -708,6 +710,7 @@ public final class JavaErrorKinds {
public static final Parameterized<PsiExpression, PsiVariable> ASSIGNMENT_DECLARED_OUTSIDE_GUARD =
parameterized(PsiExpression.class, PsiVariable.class, "assignment.declared.outside.guard")
.withRawDescription((expr, variable) -> message("assignment.declared.outside.guard", variable.getName()));
public static final Simple<PsiExpression> LVALUE_VARIABLE_EXPECTED = error("lvalue.variable.expected");
public static final Parameterized<PsiJavaToken, JavaIncompatibleTypeErrorContext> BINARY_OPERATOR_NOT_APPLICABLE =
parameterized(PsiJavaToken.class, JavaIncompatibleTypeErrorContext.class, "binary.operator.not.applicable")
@@ -226,27 +226,6 @@ public final class HighlightUtil {
return null;
}
static HighlightInfo.Builder checkVariableExpected(@NotNull PsiExpression expression) {
PsiExpression lValue;
if (expression instanceof PsiAssignmentExpression assignment) {
lValue = assignment.getLExpression();
}
else if (PsiUtil.isIncrementDecrementOperation(expression)) {
lValue = ((PsiUnaryExpression)expression).getOperand();
}
else {
lValue = null;
}
if (lValue != null && !TypeConversionUtil.isLValue(lValue) && !PsiTreeUtil.hasErrorElements(expression) &&
!(IncompleteModelUtil.isIncompleteModel(expression) &&
PsiUtil.skipParenthesizedExprDown(lValue) instanceof PsiReferenceExpression ref &&
IncompleteModelUtil.canBePendingReference(ref))) {
String description = JavaErrorBundle.message("variable.expected");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(lValue).descriptionAndTooltip(description);
}
return null;
}
static HighlightInfo.Builder checkAssignability(@Nullable PsiType lType,
@Nullable PsiType rType,
@Nullable PsiExpression expression,
@@ -469,23 +448,6 @@ public final class HighlightUtil {
return PsiFormatUtil.formatVariable(field, PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME, PsiSubstitutor.EMPTY);
}
static HighlightInfo.Builder checkYieldOutsideSwitchExpression(@NotNull PsiYieldStatement statement) {
if (statement.findEnclosingExpression() == null) {
String message = JavaErrorBundle.message("yield.unexpected");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message);
}
return null;
}
static HighlightInfo.Builder checkYieldExpressionType(@NotNull PsiExpression expression) {
if (PsiTypes.voidType().equals(expression.getType())) {
String message = JavaErrorBundle.message("yield.void");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message);
}
return null;
}
static void checkSwitchExpressionReturnTypeCompatible(@NotNull PsiSwitchExpression switchExpression,
@NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
if (!PsiPolyExpressionUtil.isPolyExpression(switchExpression)) {
@@ -383,30 +383,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
@Override
public void visitYieldStatement(@NotNull PsiYieldStatement statement) {
super.visitYieldStatement(statement);
if (!hasErrorResults()) add(HighlightUtil.checkYieldOutsideSwitchExpression(statement));
if (!hasErrorResults()) {
PsiExpression expression = statement.getExpression();
if (expression != null) {
add(HighlightUtil.checkYieldExpressionType(expression));
}
}
}
@Override
public void visitExpressionStatement(@NotNull PsiExpressionStatement statement) {
super.visitExpressionStatement(statement);
PsiElement parent = statement.getParent();
if (parent instanceof PsiSwitchLabeledRuleStatement ruleStatement) {
PsiSwitchBlock switchBlock = ruleStatement.getEnclosingSwitchBlock();
if (switchBlock instanceof PsiSwitchExpression expr && !PsiPolyExpressionUtil.isPolyExpression(expr)) {
add(HighlightUtil.checkYieldExpressionType(statement.getExpression()));
}
}
}
@Override
public void visitClass(@NotNull PsiClass aClass) {
super.visitClass(aClass);
@@ -463,7 +439,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitExpression(@NotNull PsiExpression expression) {
ProgressManager.checkCanceled(); // visitLiteralExpression is invoked very often in array initializers
super.visitExpression(expression);
PsiElement parent = expression.getParent();
@@ -471,12 +446,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (parent instanceof PsiMethodCallExpression) return;
PsiType type = expression.getType();
if (!hasErrorResults() && parent instanceof PsiNewExpression newExpression &&
newExpression.getQualifier() != expression && newExpression.getArrayInitializer() != expression) {
add(HighlightUtil.checkAssignability(PsiTypes.intType(), expression.getType(), expression, expression)); // like in 'new String["s"]'
}
if (!hasErrorResults()) add(HighlightControlFlowUtil.checkCannotWriteToFinal(expression, myFile));
if (!hasErrorResults()) add(HighlightUtil.checkVariableExpected(expression));
if (!hasErrorResults()) add(HighlightUtil.checkConditionalExpressionBranchTypesMatch(expression, type));
}
@@ -2,12 +2,12 @@ class YieldStatements {
static final int ref = -1;
void m(int i) {
<error descr="Case statement outside switch">default:</error> <error descr="Yield outside of switch expression">yield 0;</error>
<error descr="Case statement outside switch">default:</error> <error descr="'yield' outside of a switch expression">yield 0;</error>
l1: yield <error descr="Cannot resolve symbol 'l1'">l1</error>;
switch (i) {
default: <error descr="Yield outside of switch expression">yield 0;</error>
default: <error descr="'yield' outside of a switch expression">yield 0;</error>
}
out: System.out.println(switch (i) {
@@ -21,7 +21,7 @@ class YieldStatements {
a: switch (0) { default: yield a; }
}
case 7 -> {
Runnable r = () -> { <error descr="Yield outside of switch expression">yield 0;</error> };
Runnable r = () -> { <error descr="'yield' outside of a switch expression">yield 0;</error> };
r.run();
yield 0;
}
@@ -147,7 +147,7 @@ public class UnnecessaryParenthesesInspection
}
public java.util.function.IntFunction context() {
return <error descr="Incompatible types. Found: 'int', required: '<lambda expression>'">(a -> a)=1</error>;
return <error descr="Variable expected">(a -> a)</error>=1;
}
void conditionals(boolean prevConnect, boolean nextConnect) {