From a157ee801ed8fca3e925ab30dd8825b2f920f136 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 28 Jan 2025 10:35:55 +0100 Subject: [PATCH] [java-highlighting] type-related errors, lvti-related errors, TypeChecker introduced Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: 126dfbfe5a1046072d520e9a8f57261ff7add384 --- .../JavaCompilationErrorBundle.properties | 15 ++ .../highlighting/ExpressionChecker.java | 52 +----- .../highlighting/GenericsChecker.java | 36 ++++ .../highlighting/JavaErrorVisitor.java | 24 ++- .../highlighting/StatementChecker.java | 50 +++-- .../codeserver/highlighting/TypeChecker.java | 176 ++++++++++++++++++ .../errors/JavaCompilationError.java | 21 +++ .../highlighting/errors/JavaErrorKinds.java | 26 +++ .../impl/analysis/GenericsHighlightUtil.java | 73 -------- .../impl/analysis/HighlightFixUtil.java | 5 +- .../daemon/impl/analysis/HighlightUtil.java | 106 ----------- .../impl/analysis/HighlightVisitorImpl.java | 32 +--- .../impl/analysis/JavaErrorFixProvider.java | 15 ++ .../messages/JavaErrorBundle.properties | 3 - .../genericsHighlighting/IDEA57668.java | 6 +- .../ReferenceTypeParams.java | 8 +- .../genericsHighlighting8/IDEA57668.java | 6 +- .../ReferenceTypeParams.java | 8 +- 18 files changed, 371 insertions(+), 291 deletions(-) create mode 100644 java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/TypeChecker.java diff --git a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties index e6f935c45cb8..053972a22bdc 100644 --- a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties +++ b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties @@ -186,6 +186,18 @@ type.incompatible.reason.inference=
reason: {0} type.void.not.allowed='void' type is not allowed here type.void.illegal=Illegal type: 'void' type.inaccessible=''{0}'' is inaccessible here +type.unknown.class=Unknown class: ''{0}'' +type.argument.primitive=Type argument cannot be of a primitive type +type.wildcard.cannot.be.instantiated=Wildcard type ''{0}'' cannot be instantiated directly +type.wildcard.not.expected=No wildcard expected +type.wildcard.may.be.used.only.as.reference.parameters=Wildcards may be used only as reference parameters + +lvti.no.initializer=Cannot infer type: 'var' on variable without initializer +lvti.lambda=Cannot infer type: lambda expression requires an explicit target type +lvti.method.reference=Cannot infer type: method reference requires an explicit target type +lvti.array='var' is not allowed as an element type of an array +lvti.null=Cannot infer type: variable initializer is 'null' +lvti.void=Cannot infer type: variable initializer is 'void' label.without.statement=Label without statement label.duplicate=Label ''{0}'' already in use @@ -197,6 +209,8 @@ 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 +foreach.not.applicable=Foreach not applicable to type ''{0}'' + new.expression.qualified.malformed=Invalid qualified new new.expression.qualified.static.class=Qualified new of static class new.expression.qualified.anonymous.implements.interface=Anonymous class implements interface; cannot have qualifier for new @@ -282,6 +296,7 @@ array.type.expected=Array type expected; found: ''{0}'' array.generic=Generic array creation not allowed array.empty.diamond=Array creation with '<>' not allowed array.type.arguments=Array creation with type arguments not allowed +array.too.many.dimensions=Too many array dimensions pattern.type.pattern.expected=Type pattern 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 231aaa4e3280..f73cea6e3b80 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 @@ -155,31 +155,6 @@ final class ExpressionChecker { } } - void checkIllegalVoidType(@NotNull PsiKeyword type) { - if (!PsiKeyword.VOID.equals(type.getText())) return; - - PsiElement parent = type.getParent(); - if (parent instanceof PsiErrorElement) return; - if (parent instanceof PsiTypeElement) { - PsiElement typeOwner = parent.getParent(); - if (typeOwner != null) { - // do not highlight incomplete declarations - if (PsiUtilCore.hasErrorElementChild(typeOwner)) return; - } - - if (typeOwner instanceof PsiMethod method) { - if (method.getReturnTypeElement() == parent && PsiTypes.voidType().equals(method.getReturnType())) return; - } - else if (typeOwner instanceof PsiClassObjectAccessExpression classAccess) { - if (TypeConversionUtil.isVoidType(classAccess.getOperand().getType())) return; - } - else if (typeOwner instanceof JavaCodeFragment) { - if (typeOwner.getUserData(PsiUtil.VALID_VOID_TYPE_IN_CODE_FRAGMENT) != null) return; - } - } - myVisitor.report(JavaErrorKinds.TYPE_VOID_ILLEGAL.create(type)); - } - void checkMustBeBoolean(@NotNull PsiExpression expr) { PsiElement parent = expr.getParent(); if (parent instanceof PsiIfStatement || @@ -199,31 +174,6 @@ final class ExpressionChecker { } } - void checkAssertOperatorTypes(@NotNull PsiExpression expression) { - if (!(expression.getParent() instanceof PsiAssertStatement assertStatement)) return; - PsiType type = expression.getType(); - if (type == null) return; - if (expression == assertStatement.getAssertCondition() && !TypeConversionUtil.isBooleanType(type)) { - myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create( - expression, new JavaIncompatibleTypeErrorContext(PsiTypes.booleanType(), type))); - } - else if (expression == assertStatement.getAssertDescription() && TypeConversionUtil.isVoidType(type)) { - myVisitor.report(JavaErrorKinds.TYPE_VOID_NOT_ALLOWED.create(expression)); - } - } - - void checkSynchronizedExpressionType(@NotNull PsiExpression expression) { - if (expression.getParent() instanceof PsiSynchronizedStatement synchronizedStatement && - expression == synchronizedStatement.getLockExpression()) { - PsiType type = expression.getType(); - if (type == null) return; - if (type instanceof PsiPrimitiveType || TypeConversionUtil.isNullType(type)) { - PsiClassType objectType = PsiType.getJavaLangObject(myVisitor.file().getManager(), expression.getResolveScope()); - myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create(expression, new JavaIncompatibleTypeErrorContext(objectType, type))); - } - } - } - void checkArrayInitializer(@NotNull PsiExpression initializer, @NotNull PsiArrayInitializerExpression initializerList) { PsiType arrayType = initializerList.getType(); if (!(arrayType instanceof PsiArrayType theArrayType)) return; @@ -754,7 +704,7 @@ final class ExpressionChecker { myVisitor.report(JavaErrorKinds.EXCEPTION_UNHANDLED_CLOSE.create(resource, unhandled)); } - private static boolean isArrayDeclaration(@NotNull PsiVariable variable) { + 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)); } diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/GenericsChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/GenericsChecker.java index 8588516413b1..2307efd04606 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/GenericsChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/GenericsChecker.java @@ -384,6 +384,42 @@ final class GenericsChecker { } } + void checkReferenceTypeUsedAsTypeArgument(@NotNull PsiTypeElement typeElement) { + PsiType type = typeElement.getType(); + PsiType wildCardBind = type instanceof PsiWildcardType wildcardType ? wildcardType.getBound() : null; + if (type != PsiTypes.nullType() && type instanceof PsiPrimitiveType || wildCardBind instanceof PsiPrimitiveType) { + if (!(typeElement.getParent() instanceof PsiReferenceParameterList list)) return; + PsiElement parent = list.getParent(); + if (!(parent instanceof PsiJavaCodeReferenceElement) && !(parent instanceof PsiNewExpression)) return; + myVisitor.report(JavaErrorKinds.TYPE_ARGUMENT_PRIMITIVE.create(typeElement)); + } + } + + void checkWildcardUsage(@NotNull PsiTypeElement typeElement) { + PsiType type = typeElement.getType(); + if (type instanceof PsiWildcardType) { + if (typeElement.getParent() instanceof PsiReferenceParameterList) { + PsiElement parent = typeElement.getParent().getParent(); + PsiElement refParent = parent.getParent(); + if (refParent instanceof PsiAnonymousClass) refParent = refParent.getParent(); + if (refParent instanceof PsiNewExpression newExpression) { + if (!(newExpression.getType() instanceof PsiArrayType)) { + myVisitor.report(JavaErrorKinds.TYPE_WILDCARD_CANNOT_BE_INSTANTIATED.create(typeElement)); + } + } + else if (refParent instanceof PsiReferenceList) { + PsiElement refPParent = refParent.getParent(); + if (!(refPParent instanceof PsiTypeParameter typeParameter) || refParent != typeParameter.getExtendsList()) { + myVisitor.report(JavaErrorKinds.TYPE_WILDCARD_NOT_EXPECTED.create(typeElement)); + } + } + } + else if (!typeElement.isInferredType()){ + myVisitor.report(JavaErrorKinds.TYPE_WILDCARD_MAY_BE_USED_ONLY_AS_REFERENCE_PARAMETERS.create(typeElement)); + } + } + } + private static PsiType detectExpectedType(@NotNull PsiReferenceParameterList referenceParameterList) { PsiNewExpression newExpression = requireNonNull(PsiTreeUtil.getParentOfType(referenceParameterList, PsiNewExpression.class)); PsiElement parent = newExpression.getParent(); 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 1988abaae382..7055cd7016cd 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 @@ -47,6 +47,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { private final @NotNull RecordChecker myRecordChecker = new RecordChecker(this); private final @NotNull ImportChecker myImportChecker = new ImportChecker(this); final @NotNull GenericsChecker myGenericsChecker = new GenericsChecker(this); + final @NotNull TypeChecker myTypeChecker = new TypeChecker(this); final @NotNull MethodChecker myMethodChecker = new MethodChecker(this); private final @NotNull ReceiverChecker myReceiverChecker = new ReceiverChecker(this); final @NotNull ModifierChecker myModifierChecker = new ModifierChecker(this); @@ -147,6 +148,16 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (!hasErrorResults()) myStatementChecker.checkContinueTarget(statement); } + @Override + public void visitTypeElement(@NotNull PsiTypeElement type) { + super.visitTypeElement(type); + if (!hasErrorResults()) myTypeChecker.checkIllegalType(type); + if (!hasErrorResults()) myTypeChecker.checkVarTypeApplicability(type); + if (!hasErrorResults()) myTypeChecker.checkArrayType(type); + if (!hasErrorResults()) myGenericsChecker.checkReferenceTypeUsedAsTypeArgument(type); + if (!hasErrorResults()) myGenericsChecker.checkWildcardUsage(type); + } + @Override public void visitParameter(@NotNull PsiParameter parameter) { super.visitParameter(parameter); @@ -160,7 +171,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (!hasErrorResults() && parameter.getType() instanceof PsiDisjunctionType) { checkFeature(parameter, JavaFeature.MULTI_CATCH); } - if (!hasErrorResults()) myStatementChecker.checkMustBeThrowable(parameter, parameter.getType()); + if (!hasErrorResults()) myTypeChecker.checkMustBeThrowable(parameter, parameter.getType()); if (!hasErrorResults()) myStatementChecker.checkCatchTypeIsDisjoint(parameter); if (!hasErrorResults()) myGenericsChecker.checkCatchParameterIsClass(parameter); } @@ -461,7 +472,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { public void visitKeyword(@NotNull PsiKeyword keyword) { super.visitKeyword(keyword); if (!hasErrorResults()) myClassChecker.checkStaticDeclarationInInnerClass(keyword); - if (!hasErrorResults()) myExpressionChecker.checkIllegalVoidType(keyword); + if (!hasErrorResults()) myTypeChecker.checkIllegalVoidType(keyword); PsiElement parent = keyword.getParent(); if (parent instanceof PsiModifierList psiModifierList) { if (!hasErrorResults()) myModifierChecker.checkNotAllowedModifier(keyword, psiModifierList); @@ -750,15 +761,18 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (parent instanceof PsiMethodCallExpression) return; if (!hasErrorResults()) myAnnotationChecker.checkConstantExpression(expression); if (!hasErrorResults()) myExpressionChecker.checkMustBeBoolean(expression); - if (!hasErrorResults()) myExpressionChecker.checkAssertOperatorTypes(expression); - if (!hasErrorResults()) myExpressionChecker.checkSynchronizedExpressionType(expression); + if (!hasErrorResults()) myStatementChecker.checkAssertStatementTypes(expression); + if (!hasErrorResults()) myStatementChecker.checkSynchronizedStatementType(expression); if (expression.getParent() instanceof PsiArrayInitializerExpression arrayInitializer) { if (!hasErrorResults()) myExpressionChecker.checkArrayInitializer(expression, arrayInitializer); } if (!hasErrorResults() && expression instanceof PsiArrayAccessExpression accessExpression) { myExpressionChecker.checkValidArrayAccessExpression(accessExpression); } - if (!hasErrorResults()) myStatementChecker.checkThrowExceptionType(expression); + if (!hasErrorResults() && expression.getParent() instanceof PsiThrowStatement statement && statement.getException() == expression) { + myTypeChecker.checkMustBeThrowable(expression, expression.getType()); + } + if (!hasErrorResults()) myStatementChecker.checkForeachExpressionTypeIsIterable(expression); } @Override diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/StatementChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/StatementChecker.java index 06b11aad7872..04cc2d69d110 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/StatementChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/StatementChecker.java @@ -2,6 +2,7 @@ package com.intellij.java.codeserver.highlighting; import com.intellij.codeInsight.ExceptionUtil; +import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil; import com.intellij.core.JavaPsiBundle; import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds; import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext; @@ -81,6 +82,26 @@ final class StatementChecker { } } + void checkForeachExpressionTypeIsIterable(@NotNull PsiExpression expression) { + if (!shouldReportForeachNotApplicable(expression)) return; + if (expression.getType() == null) return; + PsiType itemType = JavaGenericsUtil.getCollectionItemType(expression); + if (itemType == null) { + myVisitor.report(JavaErrorKinds.FOREACH_NOT_APPLICABLE.create(expression)); + } + } + + private static boolean shouldReportForeachNotApplicable(@NotNull PsiExpression expression) { + if (!(expression.getParent() instanceof PsiForeachStatementBase parentForEach)) return false; + + PsiExpression iteratedValue = parentForEach.getIteratedValue(); + if (iteratedValue != expression) return false; + + // Ignore if the type of the value which is being iterated over is not resolved yet + PsiType iteratedValueType = iteratedValue.getType(); + return iteratedValueType == null || !PsiTypesUtil.hasUnresolvedComponents(iteratedValueType); + } + void checkCatchTypeIsDisjoint(@NotNull PsiParameter parameter) { if (!(parameter.getType() instanceof PsiDisjunctionType)) return; @@ -339,20 +360,27 @@ final class StatementChecker { myVisitor.report(kind.create(statement)); } - void checkThrowExceptionType(@NotNull PsiExpression expression) { - if (expression.getParent() instanceof PsiThrowStatement statement && statement.getException() == expression) { - PsiType type = expression.getType(); - checkMustBeThrowable(expression, type); + void checkAssertStatementTypes(@NotNull PsiExpression expression) { + if (!(expression.getParent() instanceof PsiAssertStatement assertStatement)) return; + PsiType type = expression.getType(); + if (type == null) return; + if (expression == assertStatement.getAssertCondition() && !TypeConversionUtil.isBooleanType(type)) { + myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create( + expression, new JavaIncompatibleTypeErrorContext(PsiTypes.booleanType(), type))); + } + else if (expression == assertStatement.getAssertDescription() && TypeConversionUtil.isVoidType(type)) { + myVisitor.report(JavaErrorKinds.TYPE_VOID_NOT_ALLOWED.create(expression)); } } - void checkMustBeThrowable(@NotNull PsiElement context, PsiType type) { - if (type != null && !InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_THROWABLE)) { - PsiElementFactory factory = myVisitor.factory(); - PsiClassType throwable = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_THROWABLE, context.getResolveScope()); - if (!(IncompleteModelUtil.isIncompleteModel(context) && - IncompleteModelUtil.isPotentiallyConvertible(throwable, type, context))) { - myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create(context, new JavaIncompatibleTypeErrorContext(throwable, type))); + void checkSynchronizedStatementType(@NotNull PsiExpression expression) { + if (expression.getParent() instanceof PsiSynchronizedStatement synchronizedStatement && + expression == synchronizedStatement.getLockExpression()) { + PsiType type = expression.getType(); + if (type == null) return; + if (type instanceof PsiPrimitiveType || TypeConversionUtil.isNullType(type)) { + PsiClassType objectType = PsiType.getJavaLangObject(myVisitor.file().getManager(), expression.getResolveScope()); + myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create(expression, new JavaIncompatibleTypeErrorContext(objectType, type))); } } } diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/TypeChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/TypeChecker.java new file mode 100644 index 000000000000..42311e8c4507 --- /dev/null +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/TypeChecker.java @@ -0,0 +1,176 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.java.codeserver.highlighting; + +import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds; +import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext; +import com.intellij.psi.*; +import com.intellij.psi.impl.IncompleteModelUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; +import com.intellij.psi.util.TypeConversionUtil; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.util.ObjectUtils.tryCast; + +final class TypeChecker { + private final @NotNull JavaErrorVisitor myVisitor; + + TypeChecker(@NotNull JavaErrorVisitor visitor) { myVisitor = visitor; } + + void checkIllegalType(@NotNull PsiTypeElement typeElement) { + PsiElement parent = typeElement.getParent(); + if (parent instanceof PsiTypeElement) return; + + if (PsiUtil.isInsideJavadocComment(typeElement)) return; + + PsiType type = typeElement.getType(); + PsiType componentType = type.getDeepComponentType(); + if (componentType instanceof PsiClassType) { + PsiClass aClass = PsiUtil.resolveClassInType(componentType); + if (aClass == null) { + if (typeElement.isInferredType() && parent instanceof PsiLocalVariable localVariable) { + PsiExpression initializer = PsiUtil.skipParenthesizedExprDown(localVariable.getInitializer()); + if (initializer instanceof PsiNewExpression) { + // The problem is already reported on the initializer + return; + } + } + if (IncompleteModelUtil.isIncompleteModel(myVisitor.file())) return; + myVisitor.report(JavaErrorKinds.TYPE_UNKNOWN_CLASS.create(typeElement)); + } + } + } + + void checkVarTypeApplicability(@NotNull PsiTypeElement typeElement) { + if (!typeElement.isInferredType()) return; + PsiElement parent = typeElement.getParent(); + PsiVariable variable = tryCast(parent, PsiVariable.class); + if (variable instanceof PsiLocalVariable localVariable) { + PsiExpression initializer = variable.getInitializer(); + if (initializer == null) { + if (PsiUtilCore.hasErrorElementChild(variable)) return; + myVisitor.report(JavaErrorKinds.LVTI_NO_INITIALIZER.create(localVariable)); + return; + } + PsiExpression deparen = PsiUtil.skipParenthesizedExprDown(initializer); + if (deparen instanceof PsiFunctionalExpression) { + var kind = deparen instanceof PsiLambdaExpression ? JavaErrorKinds.LVTI_LAMBDA : JavaErrorKinds.LVTI_METHOD_REFERENCE; + myVisitor.report(kind.create(localVariable)); + return; + } + + if (ExpressionChecker.isArrayDeclaration(variable)) { + myVisitor.report(JavaErrorKinds.LVTI_ARRAY.create(localVariable)); + return; + } + + PsiType lType = variable.getType(); + if (PsiTypes.nullType().equals(lType) && allChildrenAreNullLiterals(initializer)) { + myVisitor.report(JavaErrorKinds.LVTI_NULL.create(localVariable)); + return; + } + if (PsiTypes.voidType().equals(lType)) { + myVisitor.report(JavaErrorKinds.LVTI_VOID.create(localVariable)); + } + } + else if (variable instanceof PsiParameter && variable.getParent() instanceof PsiParameterList && + ExpressionChecker.isArrayDeclaration(variable)) { + myVisitor.report(JavaErrorKinds.LVTI_ARRAY.create(variable)); + } + } + + private static boolean allChildrenAreNullLiterals(PsiExpression expression) { + expression = PsiUtil.skipParenthesizedExprDown(expression); + if (expression == null) return false; + if (expression instanceof PsiLiteralExpression literal && PsiTypes.nullType().equals(literal.getType())) return true; + if (expression instanceof PsiTypeCastExpression cast) { + return allChildrenAreNullLiterals(cast.getOperand()); + } + if (expression instanceof PsiConditionalExpression conditional) { + return allChildrenAreNullLiterals(conditional.getThenExpression()) && + allChildrenAreNullLiterals(conditional.getElseExpression()); + } + if (expression instanceof PsiSwitchExpression switchExpression) { + PsiCodeBlock switchBody = switchExpression.getBody(); + if (switchBody == null) return false; + PsiStatement[] statements = switchBody.getStatements(); + for (PsiStatement statement : statements) { + if (statement instanceof PsiSwitchLabeledRuleStatement rule) { + PsiStatement ruleBody = rule.getBody(); + if (ruleBody instanceof PsiBlockStatement blockStatement) { + for (PsiYieldStatement yield : PsiTreeUtil.findChildrenOfType(blockStatement, PsiYieldStatement.class)) { + if (yield.findEnclosingExpression() == switchExpression && !allChildrenAreNullLiterals(yield.getExpression())) { + return false; + } + } + } + else if (ruleBody instanceof PsiExpressionStatement expr) { + if (!allChildrenAreNullLiterals(expr.getExpression())) return false; + } + } + else if (statement instanceof PsiYieldStatement yield) { + if (!allChildrenAreNullLiterals(yield.getExpression())) return false; + } + else { + for (PsiYieldStatement yield : PsiTreeUtil.findChildrenOfType(statement, PsiYieldStatement.class)) { + if (yield.findEnclosingExpression() == switchExpression && !allChildrenAreNullLiterals(yield.getExpression())) { + return false; + } + } + } + } + return true; + } + return false; + } + + void checkArrayType(@NotNull PsiTypeElement type) { + int dimensions = 0; + for (PsiElement child = type.getFirstChild(); child != null; child = child.getNextSibling()) { + if (PsiUtil.isJavaToken(child, JavaTokenType.LBRACKET)) { + dimensions++; + } + } + if (dimensions > 255) { + // JVM Specification, 4.3.2: no more than 255 dimensions allowed + myVisitor.report(JavaErrorKinds.ARRAY_TOO_MANY_DIMENSIONS.create(type)); + } + } + + void checkIllegalVoidType(@NotNull PsiKeyword type) { + if (!PsiKeyword.VOID.equals(type.getText())) return; + + PsiElement parent = type.getParent(); + if (parent instanceof PsiErrorElement) return; + if (parent instanceof PsiTypeElement) { + PsiElement typeOwner = parent.getParent(); + if (typeOwner != null) { + // do not highlight incomplete declarations + if (PsiUtilCore.hasErrorElementChild(typeOwner)) return; + } + + if (typeOwner instanceof PsiMethod method) { + if (method.getReturnTypeElement() == parent && PsiTypes.voidType().equals(method.getReturnType())) return; + } + else if (typeOwner instanceof PsiClassObjectAccessExpression classAccess) { + if (TypeConversionUtil.isVoidType(classAccess.getOperand().getType())) return; + } + else if (typeOwner instanceof JavaCodeFragment) { + if (typeOwner.getUserData(PsiUtil.VALID_VOID_TYPE_IN_CODE_FRAGMENT) != null) return; + } + } + myVisitor.report(JavaErrorKinds.TYPE_VOID_ILLEGAL.create(type)); + } + + void checkMustBeThrowable(@NotNull PsiElement context, PsiType type) { + PsiElementFactory factory = myVisitor.factory(); + PsiClassType throwable = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_THROWABLE, context.getResolveScope()); + if (type != null && !TypeConversionUtil.isAssignable(throwable, type)) { + if (!(IncompleteModelUtil.isIncompleteModel(context) && + IncompleteModelUtil.isPotentiallyConvertible(throwable, type, context))) { + myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create(context, new JavaIncompatibleTypeErrorContext(throwable, type))); + } + } + } +} diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaCompilationError.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaCompilationError.java index d8bd9fc8edc8..39c70e681412 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaCompilationError.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaCompilationError.java @@ -8,6 +8,8 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Optional; + /** * A concrete instance of a Java compilation error. Note that the instance is bound to a PSI element, so it should not * outlive the read-action. Otherwise it may become invalid. @@ -75,4 +77,23 @@ public record JavaCompilationError(@NotNull Jav public @NotNull HtmlChunk tooltip() { return kind.tooltip(psi, context); } + + /** + * A helper method to match wanted error message and extract PSI without doing unchecked casts in client code. + * + * @param kinds wanted error kinds + * @return an optional containing typed {@link #psi()} if the current error is one of supplied kinds; + * empty optional otherwise + * @param the common PSI type of wanted kinds + */ + @SafeVarargs + public final @NotNull Optional psiForKind(JavaErrorKind... kinds) { + for (JavaErrorKind errorKind : kinds) { + if (errorKind.equals(kind)) { + //noinspection unchecked + return Optional.of((WantedPsi)psi); + } + } + return Optional.empty(); + } } 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 bc355bff16d4..077de0c4a197 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 @@ -613,6 +613,31 @@ public final class JavaErrorKinds { public static final Parameterized TYPE_INACCESSIBLE = parameterized(PsiElement.class, PsiClass.class, "type.inaccessible") .withRawDescription((psi, cls) -> message("type.inaccessible", formatClass(cls))); + public static final Simple TYPE_UNKNOWN_CLASS = error(PsiTypeElement.class, "type.unknown.class") + .withRawDescription(type -> message("type.unknown.class", type.getType().getDeepComponentType().getCanonicalText())); + public static final Simple TYPE_ARGUMENT_PRIMITIVE = error(PsiTypeElement.class, "type.argument.primitive"); + public static final Simple TYPE_WILDCARD_NOT_EXPECTED = error(PsiTypeElement.class, "type.wildcard.not.expected"); + public static final Simple TYPE_WILDCARD_MAY_BE_USED_ONLY_AS_REFERENCE_PARAMETERS = + error(PsiTypeElement.class, "type.wildcard.may.be.used.only.as.reference.parameters"); + public static final Simple TYPE_WILDCARD_CANNOT_BE_INSTANTIATED = + error(PsiTypeElement.class, "type.wildcard.cannot.be.instantiated") + .withRawDescription(type -> message("type.wildcard.cannot.be.instantiated", formatType(type.getType()))); + + public static final Simple FOREACH_NOT_APPLICABLE = error(PsiExpression.class, "foreach.not.applicable") + .withRawDescription(expression -> message("foreach.not.applicable", formatType(expression.getType()))); + + public static final Simple LVTI_NO_INITIALIZER = error(PsiLocalVariable.class, "lvti.no.initializer") + .withAnchor(var -> var.getTypeElement()); + public static final Simple LVTI_VOID = error(PsiLocalVariable.class, "lvti.void") + .withAnchor(var -> var.getTypeElement()); + public static final Simple LVTI_NULL = error(PsiLocalVariable.class, "lvti.null") + .withAnchor(var -> var.getTypeElement()); + public static final Simple LVTI_LAMBDA = error(PsiLocalVariable.class, "lvti.lambda") + .withAnchor(var -> var.getTypeElement()); + public static final Simple LVTI_METHOD_REFERENCE = error(PsiLocalVariable.class, "lvti.method.reference") + .withAnchor(var -> var.getTypeElement()); + public static final Simple LVTI_ARRAY = error(PsiVariable.class, "lvti.array") + .withAnchor(var -> var.getTypeElement()); public static final Simple LABEL_WITHOUT_STATEMENT = error(PsiLabeledStatement.class, "label.without.statement") .withAnchor(label -> label.getLabelIdentifier()); @@ -642,6 +667,7 @@ public final class JavaErrorKinds { public static final Simple ARRAY_GENERIC = error("array.generic"); public static final Simple ARRAY_EMPTY_DIAMOND = error("array.empty.diamond"); public static final Simple ARRAY_TYPE_ARGUMENTS = error("array.type.arguments"); + public static final Simple ARRAY_TOO_MANY_DIMENSIONS = error("array.too.many.dimensions"); public static final Parameterized PATTERN_TYPE_PATTERN_EXPECTED = parameterized("pattern.type.pattern.expected"); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index f1f61de8467d..ec07f657101a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -644,79 +644,6 @@ public final class GenericsHighlightUtil { return null; } - static HighlightInfo.Builder checkWildcardUsage(@NotNull PsiTypeElement typeElement) { - PsiType type = typeElement.getType(); - if (type instanceof PsiWildcardType) { - if (typeElement.getParent() instanceof PsiReferenceParameterList) { - PsiElement parent = typeElement.getParent().getParent(); - LOG.assertTrue(parent instanceof PsiJavaCodeReferenceElement, parent); - PsiElement refParent = parent.getParent(); - if (refParent instanceof PsiAnonymousClass) refParent = refParent.getParent(); - if (refParent instanceof PsiNewExpression newExpression) { - if (!(newExpression.getType() instanceof PsiArrayType)) { - String description = JavaErrorBundle.message("wildcard.type.cannot.be.instantiated", JavaHighlightUtil.formatType(type)); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description); - } - } - else if (refParent instanceof PsiReferenceList) { - PsiElement refPParent = refParent.getParent(); - if (!(refPParent instanceof PsiTypeParameter typeParameter) || refParent != typeParameter.getExtendsList()) { - String description = JavaErrorBundle.message("generics.wildcard.not.expected"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description); - } - } - } - else if (!typeElement.isInferredType()){ - String description = JavaErrorBundle.message("generics.wildcards.may.be.used.only.as.reference.parameters"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description); - } - } - - return null; - } - - static HighlightInfo.Builder checkReferenceTypeUsedAsTypeArgument(@NotNull PsiTypeElement typeElement) { - PsiType type = typeElement.getType(); - PsiType wildCardBind = type instanceof PsiWildcardType wildcardType ? wildcardType.getBound() : null; - if (type != PsiTypes.nullType() && type instanceof PsiPrimitiveType || wildCardBind instanceof PsiPrimitiveType) { - PsiElement element = new PsiMatcherImpl(typeElement) - .parent(PsiMatchers.hasClass(PsiReferenceParameterList.class)) - .parent(PsiMatchers.hasClass(PsiJavaCodeReferenceElement.class, PsiNewExpression.class)) - .getElement(); - if (element == null) return null; - - String text = JavaErrorBundle.message("generics.type.argument.cannot.be.of.primitive.type"); - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(text); - - PsiPrimitiveType toConvert = (PsiPrimitiveType)(type instanceof PsiWildcardType ? wildCardBind : type); - PsiClassType boxedType = toConvert.getBoxedType(typeElement); - if (boxedType != null) { - IntentionAction action = QuickFixFactory.getInstance().createReplacePrimitiveWithBoxedTypeAction( - typeElement, toConvert.getPresentableText(), toConvert.getBoxedTypeName()); - builder.registerFix(action, null, null, null, null); - } - return builder; - } - - return null; - } - - static HighlightInfo.Builder checkForeachExpressionTypeIsIterable(@NotNull PsiExpression expression) { - if (expression.getType() == null) return null; - PsiType itemType = JavaGenericsUtil.getCollectionItemType(expression); - if (itemType == null) { - String description = JavaErrorBundle.message("foreach.not.applicable", - JavaHighlightUtil.formatType(expression.getType())); - HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description); - IntentionAction action = QuickFixFactory.getInstance().createNotIterableForEachLoopFix(expression); - if (action != null) { - builder.registerFix(action, null, null, null, null); - } - return builder; - } - return null; - } - //http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.9.2 static HighlightInfo.Builder checkAccessStaticFieldFromEnumConstructor(@NotNull PsiReferenceExpression expr, @NotNull JavaResolveResult result) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightFixUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightFixUtil.java index 016d6a33f7a7..b551ca215b04 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightFixUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightFixUtil.java @@ -417,7 +417,7 @@ public final class HighlightFixUtil { return copyType != null && returnType.isAssignableFrom(copyType); } - static void registerSpecifyVarTypeFix(@NotNull PsiLocalVariable variable, @NotNull HighlightInfo.Builder info) { + static void registerSpecifyVarTypeFix(@NotNull PsiLocalVariable variable, @NotNull Consumer info) { PsiElement block = PsiUtil.getVariableCodeBlock(variable, null); if (block == null) return; PsiTreeUtil.processElements(block, PsiReferenceExpression.class, ref -> { @@ -435,8 +435,7 @@ public final class HighlightFixUtil { if (type != null) { type = GenericsUtil.getVariableTypeByExpressionType(type); if (PsiTypesUtil.isDenotableType(type, variable) && !PsiTypes.voidType().equals(type)) { - IntentionAction fix = QuickFixFactory.getInstance().createSetVariableTypeFix(variable, type); - info.registerFix(fix, null, null, null, null); + info.accept(QuickFixFactory.getInstance().createSetVariableTypeFix(variable, type)); } return false; } 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 55bb6187cdea..b19ff579b12c 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 @@ -51,7 +51,6 @@ import com.intellij.util.ui.JBUI; import com.intellij.util.ui.NamedColorUtil; import com.intellij.util.ui.UIUtil; import com.siyeh.ig.psiutils.ControlFlowUtils; -import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.InstanceOfUtils; import com.siyeh.ig.psiutils.VariableAccessUtils; import org.jetbrains.annotations.Contract; @@ -297,61 +296,6 @@ public final class HighlightUtil { return null; } - static HighlightInfo.Builder checkVarTypeApplicability(@NotNull PsiTypeElement typeElement) { - if (!typeElement.isInferredType()) { - return null; - } - PsiElement parent = typeElement.getParent(); - PsiVariable variable = tryCast(parent, PsiVariable.class); - if (variable instanceof PsiLocalVariable localVariable) { - PsiExpression initializer = variable.getInitializer(); - if (initializer == null) { - if (PsiUtilCore.hasErrorElementChild(variable)) return null; - String message = JavaErrorBundle.message("lvti.no.initializer"); - HighlightInfo.Builder info = - HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(message).range(typeElement); - HighlightFixUtil.registerSpecifyVarTypeFix(localVariable, info); - return info; - } - PsiExpression deparen = PsiUtil.skipParenthesizedExprDown(initializer); - if (deparen instanceof PsiFunctionalExpression) { - boolean lambda = deparen instanceof PsiLambdaExpression; - String message = JavaErrorBundle.message(lambda ? "lvti.lambda" : "lvti.method.ref"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(message).range(typeElement); - } - - if (isArrayDeclaration(variable)) { - String message = JavaErrorBundle.message("lvti.array"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(message).range(typeElement); - } - - PsiType lType = variable.getType(); - if (PsiTypes.nullType().equals(lType) && - ExpressionUtils.nonStructuralChildren(initializer).allMatch(ExpressionUtils::isNullLiteral)) { - HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) - .descriptionAndTooltip(JavaErrorBundle.message("lvti.null")) - .range(typeElement); - HighlightFixUtil.registerSpecifyVarTypeFix(localVariable, info); - return info; - } - if (PsiTypes.voidType().equals(lType)) { - String message = JavaErrorBundle.message("lvti.void"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(message).range(typeElement); - } - } - else if (variable instanceof PsiParameter && variable.getParent() instanceof PsiParameterList && isArrayDeclaration(variable)) { - String message = JavaErrorBundle.message("lvti.array"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(message).range(typeElement); - } - - return null; - } - - private 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)); - } - static HighlightInfo.Builder checkAssignability(@Nullable PsiType lType, @Nullable PsiType rType, @Nullable PsiExpression expression, @@ -870,41 +814,6 @@ public final class HighlightUtil { } - static HighlightInfo.Builder checkIllegalType(@NotNull PsiTypeElement typeElement, @NotNull PsiFile containingFile) { - PsiElement parent = typeElement.getParent(); - if (parent instanceof PsiTypeElement) return null; - - if (PsiUtil.isInsideJavadocComment(typeElement)) return null; - - PsiType type = typeElement.getType(); - PsiType componentType = type.getDeepComponentType(); - if (componentType instanceof PsiClassType) { - PsiClass aClass = PsiUtil.resolveClassInType(componentType); - if (aClass == null) { - if (typeElement.isInferredType() && parent instanceof PsiLocalVariable localVariable) { - PsiExpression initializer = PsiUtil.skipParenthesizedExprDown(localVariable.getInitializer()); - if (initializer instanceof PsiNewExpression) { - // The problem is already reported on the initializer - return null; - } - } - if (IncompleteModelUtil.isIncompleteModel(containingFile)) { - return null; - } - String canonicalText = componentType.getCanonicalText(); - String description = JavaErrorBundle.message("unknown.class", canonicalText); - HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description); - PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement(); - if (referenceElement != null) { - UnresolvedReferenceQuickFixUpdater.getInstance(containingFile.getProject()).registerQuickFixesLater(referenceElement, info); - } - return info; - } - } - - return null; - } - static HighlightInfo.Builder checkMemberReferencedBeforeConstructorCalled(@NotNull PsiElement expression, @Nullable PsiElement resolved, @NotNull Function surroundingConstructor) { @@ -1191,21 +1100,6 @@ public final class HighlightUtil { .navigationShift(navigationShift); } - public static HighlightInfo.Builder checkArrayType(PsiTypeElement type) { - int dimensions = 0; - for (PsiElement child = type.getFirstChild(); child != null; child = child.getNextSibling()) { - if (PsiUtil.isJavaToken(child, JavaTokenType.LBRACKET)) { - dimensions++; - } - } - if (dimensions > 255) { - // JVM Specification, 4.3.2: no more than 255 dimensions allowed - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(type.getTextRange()) - .description(JavaErrorBundle.message("too.many.array.dimensions")); - } - return null; - } - static HighlightInfo.Builder checkExtraSemicolonBetweenImportStatements(@NotNull PsiJavaToken token, IElementType type, @NotNull LanguageLevel level) { 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 eca1d91d72cd..ffce1d98e853 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 @@ -64,6 +64,7 @@ import java.util.function.Function; import static com.intellij.psi.PsiModifier.SEALED; import static com.intellij.util.ObjectUtils.tryCast; +import static java.util.Objects.*; // java highlighting: problems in java code like unresolved/incompatible symbols/methods etc. public class HighlightVisitorImpl extends JavaElementVisitor implements HighlightVisitor { @@ -250,10 +251,9 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh info.range(anchor); } errorFixProvider.processFixes(error, fix -> info.registerFix(fix.asIntention(), null, null, null, null)); - if (error.kind() == JavaErrorKinds.EXPRESSION_EXPECTED || error.kind() == JavaErrorKinds.REFERENCE_UNRESOLVED || - error.kind() == JavaErrorKinds.REFERENCE_AMBIGUOUS) { - UnresolvedReferenceQuickFixUpdater.getInstance(getProject()).registerQuickFixesLater((PsiReference)error.psi(), info); - } + error.psiForKind(JavaErrorKinds.EXPRESSION_EXPECTED, JavaErrorKinds.REFERENCE_UNRESOLVED, JavaErrorKinds.REFERENCE_AMBIGUOUS) + .or(() -> error.psiForKind(JavaErrorKinds.TYPE_UNKNOWN_CLASS).map(PsiTypeElement::getInnermostComponentReferenceElement)) + .ifPresent(ref -> UnresolvedReferenceQuickFixUpdater.getInstance(getProject()).registerQuickFixesLater(ref, info)); add(info); } @@ -478,20 +478,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh if (!hasErrorResults()) add(HighlightControlFlowUtil.checkCannotWriteToFinal(expression, myFile)); if (!hasErrorResults()) add(HighlightUtil.checkVariableExpected(expression)); if (!hasErrorResults()) add(HighlightUtil.checkConditionalExpressionBranchTypesMatch(expression, type)); - if (!hasErrorResults() && shouldReportForeachNotApplicable(expression)) { - add(GenericsHighlightUtil.checkForeachExpressionTypeIsIterable(expression)); - } - } - - private static boolean shouldReportForeachNotApplicable(@NotNull PsiExpression expression) { - if (!(expression.getParent() instanceof PsiForeachStatementBase parentForEach)) return false; - - PsiExpression iteratedValue = parentForEach.getIteratedValue(); - if (iteratedValue != expression) return false; - - // Ignore if the type of the value which is being iterated over is not resolved yet - PsiType iteratedValueType = iteratedValue.getType(); - return iteratedValueType == null || !PsiTypesUtil.hasUnresolvedComponents(iteratedValueType); } @Override @@ -1024,8 +1010,8 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh IncompleteModelUtil.isUnresolvedClassType(functionalInterfaceType)) { return; } - String t1 = HighlightUtil.format(Objects.requireNonNull(results[0].getElement())); - String t2 = HighlightUtil.format(Objects.requireNonNull(results[1].getElement())); + String t1 = HighlightUtil.format(requireNonNull(results[0].getElement())); + String t2 = HighlightUtil.format(requireNonNull(results[1].getElement())); description = JavaErrorBundle.message("ambiguous.reference", expression.getReferenceName(), t1, t2); } else { @@ -1148,11 +1134,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh @Override public void visitTypeElement(@NotNull PsiTypeElement type) { - if (!hasErrorResults()) add(HighlightUtil.checkIllegalType(type, myFile)); - if (!hasErrorResults()) add(HighlightUtil.checkVarTypeApplicability(type)); - if (!hasErrorResults()) add(GenericsHighlightUtil.checkReferenceTypeUsedAsTypeArgument(type)); - if (!hasErrorResults()) add(GenericsHighlightUtil.checkWildcardUsage(type)); - if (!hasErrorResults()) add(HighlightUtil.checkArrayType(type)); + super.visitTypeElement(type); if (!hasErrorResults()) { PreviewFeatureUtil.checkPreviewFeature(type, myPreviewFeatureVisitor); } 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 9c748b062993..49b4922d89fd 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 @@ -158,6 +158,7 @@ final class JavaErrorFixProvider { } } }); + fix(FOREACH_NOT_APPLICABLE, error -> myFactory.createNotIterableForEachLoopFix(error.psi())); } private void createMethodFixes() { @@ -302,6 +303,8 @@ final class JavaErrorFixProvider { private void createVariableFixes() { fix(UNNAMED_VARIABLE_BRACKETS, error -> new NormalizeBracketsFix(error.psi())); fix(UNNAMED_VARIABLE_WITHOUT_INITIALIZER, error -> myFactory.createAddVariableInitializerFix(error.psi())); + fixes(LVTI_NO_INITIALIZER, (error, sink) -> HighlightFixUtil.registerSpecifyVarTypeFix(error.psi(), sink)); + fixes(LVTI_NULL, (error, sink) -> HighlightFixUtil.registerSpecifyVarTypeFix(error.psi(), sink)); } private void createExpressionFixes() { @@ -545,6 +548,18 @@ final class JavaErrorFixProvider { HighlightFixUtil.registerMethodReturnFixAction(sink, candidate, constructorCall); } }); + fix(TYPE_ARGUMENT_PRIMITIVE, error -> { + PsiTypeElement typeElement = error.psi(); + PsiType type = typeElement.getType(); + PsiPrimitiveType toConvert = + (PsiPrimitiveType)(type instanceof PsiWildcardType wildcardType ? requireNonNull(wildcardType.getBound()) : type); + PsiClassType boxedType = toConvert.getBoxedType(typeElement); + if (boxedType != null) { + return QuickFixFactory.getInstance().createReplacePrimitiveWithBoxedTypeAction( + typeElement, toConvert.getPresentableText(), boxedType.getCanonicalText()); + } + return null; + }); } private void createClassFixes() { diff --git a/java/java-psi-impl/resources/messages/JavaErrorBundle.properties b/java/java-psi-impl/resources/messages/JavaErrorBundle.properties index 8e233f50cf66..2e5e0a3013d9 100644 --- a/java/java-psi-impl/resources/messages/JavaErrorBundle.properties +++ b/java/java-psi-impl/resources/messages/JavaErrorBundle.properties @@ -24,7 +24,6 @@ generics.methods.have.same.erasure.hide={0}; both methods have same erasure, yet generics.type.parameter.cannot.be.instantiated=Type parameter ''{0}'' cannot be instantiated directly wildcard.type.cannot.be.instantiated=Wildcard type ''{0}'' cannot be instantiated directly generics.wildcard.not.expected=No wildcard expected -generics.wildcards.may.be.used.only.as.reference.parameters=Wildcards may be used only as reference parameters generics.type.argument.cannot.be.of.primitive.type=Type argument cannot be of primitive type generics.unchecked.assignment=Unchecked assignment: ''{0}'' to ''{1}'' generics.unchecked.cast=Unchecked cast: ''{0}'' to ''{1}'' @@ -371,11 +370,9 @@ module.bad.name=Module ''{0}'' has an invalid name restricted.identifier=''{0}'' is a restricted identifier and cannot be used for type declarations restricted.identifier.reference=Illegal reference to restricted type ''{0}'' -lvti.no.initializer=Cannot infer type: 'var' on variable without initializer lvti.lambda=Cannot infer type: lambda expression requires an explicit target type lvti.method.ref=Cannot infer type: method reference requires an explicit target type lvti.compound='var' is not allowed in a compound declaration -lvti.array='var' is not allowed as an element type of an array lvti.null=Cannot infer type: variable initializer is 'null' lvti.void=Cannot infer type: variable initializer is 'void' lvti.selfReferenced=Cannot infer type for ''{0}'', it is used in its own variable initializer diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57668.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57668.java index cc49162b0b8d..654fef3a7964 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57668.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57668.java @@ -2,14 +2,14 @@ class A { A() {} { - new <int>A(); + new <int>A(); } } class B { { - new B<int>(); - B.<int>m(); + new B<int>(); + B.<int>m(); } void m(){} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/ReferenceTypeParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/ReferenceTypeParams.java index 3a72c73bdc53..4f075b8c48d0 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/ReferenceTypeParams.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/ReferenceTypeParams.java @@ -27,10 +27,10 @@ class D { } class Primitives { - Object a = new Primitives<? extends int>(); - Object o = new Primitives<int>(); - void f(Primitives<boolean> param) { - if (this instanceof Primitives<double>) { + Object a = new Primitives<? extends int>(); + Object o = new Primitives<int>(); + void f(Primitives<boolean> param) { + if (this instanceof Primitives<double>) { return; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA57668.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA57668.java index 65efaee0edda..912f4f66d28c 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA57668.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA57668.java @@ -2,14 +2,14 @@ class A { A() {} { - new <int>A(); + new <int>A(); } } class B { { - new B<int>(); - B.<int>m(); + new B<int>(); + B.<int>m(); } void m(){} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/ReferenceTypeParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/ReferenceTypeParams.java index 92d8df33263d..c67cfa4b6afa 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/ReferenceTypeParams.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/ReferenceTypeParams.java @@ -27,10 +27,10 @@ class D { } class Primitives { - Object a = new Primitives<? extends int>(); - Object o = new Primitives<int>(); - void f(Primitives<boolean> param) { - if (this instanceof Primitives<double>) { + Object a = new Primitives<? extends int>(); + Object o = new Primitives<int>(); + void f(Primitives<boolean> param) { + if (this instanceof Primitives<double>) { return; } }