diff --git a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties index d920efb40173..e6f935c45cb8 100644 --- a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties +++ b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties @@ -340,3 +340,5 @@ return.before.explicit.constructor.call='return' not allowed before ''{0}'' call return.value.missing=Missing return value return.from.void.method=Cannot return a value from a method with void result type return.from.constructor=Cannot return a value from a constructor + +catch.type.parameter=Cannot catch type parameters diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ClassChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ClassChecker.java index 0d8fc212703a..77614e27239e 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ClassChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ClassChecker.java @@ -740,7 +740,7 @@ final class ClassChecker { if (isRealInnerClass(targetClass)) { PsiClass outerClass = targetClass.getContainingClass(); if (outerClass != null) { - PsiClassType outerType = JavaPsiFacade.getElementFactory(myVisitor.project()).createType(outerClass); + PsiClassType outerType = myVisitor.factory().createType(outerClass); myVisitor.myExpressionChecker.checkAssignability(outerType, null, qualifier, qualifier); } } else { 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 313e8d41d4ad..231aaa4e3280 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 @@ -433,7 +433,7 @@ final class ExpressionChecker { PsiType type = processor.getType(); if (type == null) return; - PsiElementFactory factory = JavaPsiFacade.getElementFactory(processor.getProject()); + PsiElementFactory factory = myVisitor.factory(); PsiClassType processorType = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_STRING_TEMPLATE_PROCESSOR, processor.getResolveScope()); if (!TypeConversionUtil.isAssignable(processorType, type)) { if (IncompleteModelUtil.isIncompleteModel(templateExpression) && IncompleteModelUtil.isPotentiallyConvertible(processorType, processor)) { 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 f61ca3dab0f5..8588516413b1 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 @@ -20,6 +20,8 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import static java.util.Objects.*; + final class GenericsChecker { private final @NotNull JavaErrorVisitor myVisitor; @@ -146,11 +148,10 @@ final class GenericsChecker { PsiClass superClass = result.getElement(); if (superClass == null || visited.contains(superClass)) continue; PsiSubstitutor superTypeSubstitutor = result.getSubstitutor(); - PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(aClass.getProject()); //JLS 4.8 The superclasses (respectively, superinterfaces) of a raw type are the erasures // of the superclasses (superinterfaces) of any of the parameterizations of the generic type. superTypeSubstitutor = PsiUtil.isRawSubstitutor(aClass, derivedSubstitutor) - ? elementFactory.createRawSubstitutor(superClass) + ? myVisitor.factory().createRawSubstitutor(superClass) : MethodSignatureUtil.combineSubstitutors(superTypeSubstitutor, derivedSubstitutor); PsiSubstitutor inheritedSubstitutor = inheritedClasses.get(superClass); @@ -372,8 +373,19 @@ final class GenericsChecker { } } + void checkCatchParameterIsClass(@NotNull PsiParameter parameter) { + if (!(parameter.getDeclarationScope() instanceof PsiCatchSection)) return; + + List typeElements = PsiUtil.getParameterTypeElements(parameter); + for (PsiTypeElement typeElement : typeElements) { + if (PsiUtil.resolveClassInClassTypeOnly(typeElement.getType()) instanceof PsiTypeParameter) { + myVisitor.report(JavaErrorKinds.CATCH_TYPE_PARAMETER.create(typeElement)); + } + } + } + private static PsiType detectExpectedType(@NotNull PsiReferenceParameterList referenceParameterList) { - PsiNewExpression newExpression = Objects.requireNonNull(PsiTreeUtil.getParentOfType(referenceParameterList, PsiNewExpression.class)); + PsiNewExpression newExpression = requireNonNull(PsiTreeUtil.getParentOfType(referenceParameterList, PsiNewExpression.class)); PsiElement parent = newExpression.getParent(); PsiType expectedType = null; if (parent instanceof PsiVariable psiVariable && newExpression.equals(psiVariable.getInitializer())) { 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 c5f4b63ca0e8..1988abaae382 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 @@ -40,6 +40,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { private final @NotNull Consumer> myErrorConsumer; private final @NotNull Project myProject; private final @NotNull PsiFile myFile; + private final @NotNull PsiElementFactory myFactory; private final @NotNull LanguageLevel myLanguageLevel; private final @NotNull AnnotationChecker myAnnotationChecker = new AnnotationChecker(this); final @NotNull ClassChecker myClassChecker = new ClassChecker(this); @@ -64,6 +65,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { myJavaModule = module; myJavaSdkVersion = ObjectUtils .notNull(JavaVersionService.getInstance().getJavaSdkVersion(file), JavaSdkVersion.fromLanguageLevel(myLanguageLevel)); + myFactory = JavaPsiFacade.getElementFactory(myProject); } void report(@NotNull JavaCompilationError error) { @@ -83,6 +85,10 @@ final class JavaErrorVisitor extends JavaElementVisitor { @NotNull Project project() { return myProject; } + + @NotNull PsiElementFactory factory() { + return myFactory; + } @NotNull LanguageLevel languageLevel() { return myLanguageLevel; @@ -154,7 +160,9 @@ 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()) myStatementChecker.checkCatchTypeIsDisjoint(parameter); + if (!hasErrorResults()) myGenericsChecker.checkCatchParameterIsClass(parameter); } else if (parent instanceof PsiForeachStatement forEach) { checkFeature(forEach, JavaFeature.FOR_EACH); @@ -194,8 +202,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (!hasErrorResults()) myClassChecker.checkEnumWithAbstractMethods(enumConstant); if (!hasErrorResults()) myExpressionChecker.checkUnhandledExceptions(enumConstant); if (!hasErrorResults()) { - PsiClass containingClass = requireNonNull(enumConstant.getContainingClass()); - PsiClassType type = JavaPsiFacade.getElementFactory(myProject).createType(containingClass); + PsiClassType type = factory().createType(requireNonNull(enumConstant.getContainingClass())); myExpressionChecker.checkConstructorCall(type.resolveGenerics(), enumConstant, type, null); } } @@ -738,6 +745,9 @@ final class JavaErrorVisitor extends JavaElementVisitor { @Override public void visitExpression(@NotNull PsiExpression expression) { super.visitExpression(expression); + PsiElement parent = expression.getParent(); + // Method expression of the call should not be especially processed + if (parent instanceof PsiMethodCallExpression) return; if (!hasErrorResults()) myAnnotationChecker.checkConstantExpression(expression); if (!hasErrorResults()) myExpressionChecker.checkMustBeBoolean(expression); if (!hasErrorResults()) myExpressionChecker.checkAssertOperatorTypes(expression); @@ -748,6 +758,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (!hasErrorResults() && expression instanceof PsiArrayAccessExpression accessExpression) { myExpressionChecker.checkValidArrayAccessExpression(accessExpression); } + if (!hasErrorResults()) myStatementChecker.checkThrowExceptionType(expression); } @Override diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/MethodChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/MethodChecker.java index 0a1cbf13a593..17a66501ad84 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/MethodChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/MethodChecker.java @@ -49,11 +49,11 @@ final class MethodChecker { return signatures; } - void checkMustBeThrowable(@NotNull PsiClass aClass, @NotNull PsiElement context) { - PsiClassType type = JavaPsiFacade.getElementFactory(aClass.getProject()).createType(aClass); - PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject()); - PsiClassType throwable = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_THROWABLE, context.getResolveScope()); - if (!TypeConversionUtil.isAssignable(throwable, type)) { + void checkMustBeThrowable(@NotNull PsiClass aClass, @NotNull PsiJavaCodeReferenceElement context) { + if (!InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_LANG_THROWABLE)) { + PsiElementFactory factory = myVisitor.factory(); + PsiClassType type = factory.createType(aClass); + PsiClassType throwable = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_THROWABLE, context.getResolveScope()); if (IncompleteModelUtil.isIncompleteModel(context) && IncompleteModelUtil.isPotentiallyConvertible(throwable, type, context)) return; myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create(context, new JavaIncompatibleTypeErrorContext(throwable, type))); } 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 2b6f9bc0b251..06b11aad7872 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 @@ -195,7 +195,7 @@ final class StatementChecker { PsiType type = resource.getType(); if (type == null) return; - PsiElementFactory factory = JavaPsiFacade.getElementFactory(resource.getProject()); + PsiElementFactory factory = myVisitor.factory(); PsiClassType autoCloseable = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, resource.getResolveScope()); if (TypeConversionUtil.isAssignable(autoCloseable, type)) return; if (IncompleteModelUtil.isIncompleteModel(resource) && IncompleteModelUtil.isPotentiallyConvertible(autoCloseable, type, resource)) return; @@ -338,4 +338,22 @@ final class StatementChecker { var kind = isDeclarationNotAllowed ? JavaErrorKinds.STATEMENT_DECLARATION_NOT_ALLOWED : JavaErrorKinds.STATEMENT_BAD_EXPRESSION; 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 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))); + } + } + } } 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 e978238437c3..bc355bff16d4 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 @@ -629,6 +629,8 @@ public final class JavaErrorKinds { public static final Simple BREAK_OUT_OF_SWITCH_EXPRESSION = error("break.out.of.switch.expression"); public static final Simple CONTINUE_OUTSIDE_LOOP = error("continue.outside.loop"); public static final Simple CONTINUE_OUT_OF_SWITCH_EXPRESSION = error("continue.out.of.switch.expression"); + + public static final Simple CATCH_TYPE_PARAMETER = error("catch.type.parameter"); public static final Parameterized ARRAY_ILLEGAL_INITIALIZER = parameterized(PsiExpression.class, PsiType.class, "array.illegal.initializer") 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 ddc76bd87f58..f1f61de8467d 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 @@ -778,19 +778,6 @@ public final class GenericsHighlightUtil { return enumClass != null && enumClass.isEnum() ? enumClass : null; } - static void checkCatchParameterIsClass(@NotNull PsiParameter parameter, @NotNull Consumer errorSink) { - if (!(parameter.getDeclarationScope() instanceof PsiCatchSection)) return; - - List typeElements = PsiUtil.getParameterTypeElements(parameter); - for (PsiTypeElement typeElement : typeElements) { - PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(typeElement.getType()); - if (aClass instanceof PsiTypeParameter) { - String message = JavaErrorBundle.message("generics.cannot.catch.type.parameters"); - errorSink.accept(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(message)); - } - } - } - static HighlightInfo.Builder checkInstanceOfGenericType(@NotNull LanguageLevel languageLevel, @NotNull PsiInstanceOfExpression expression) { PsiTypeElement checkTypeElement = InstanceOfUtils.findCheckTypeElement(expression); if (checkTypeElement == null) return null; 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 3cda68039724..55bb6187cdea 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 @@ -761,14 +761,6 @@ public final class HighlightUtil { return container == null ? "?" : HighlightMessageUtil.getSymbolName(container, substitutor); } - static HighlightInfo.Builder checkCatchParameterIsThrowable(@NotNull PsiParameter parameter) { - if (parameter.getDeclarationScope() instanceof PsiCatchSection) { - PsiType type = parameter.getType(); - return checkMustBeThrowable(type, parameter, true); - } - return null; - } - static HighlightInfo.Builder checkResourceVariableIsFinal(@NotNull PsiResourceExpression resource) { PsiExpression expression = resource.getExpression(); @@ -1357,30 +1349,6 @@ public final class HighlightUtil { } - static HighlightInfo.Builder checkMustBeThrowable(@NotNull PsiType type, @NotNull PsiElement context, boolean addCastIntention) { - PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject()); - PsiClassType throwable = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_THROWABLE, context.getResolveScope()); - if (!TypeConversionUtil.isAssignable(throwable, type)) { - if (IncompleteModelUtil.isIncompleteModel(context) && IncompleteModelUtil.isPotentiallyConvertible(throwable, type, context)) return null; - HighlightInfo.Builder highlightInfo = createIncompatibleTypeHighlightInfo(throwable, type, context.getTextRange(), 0); - if (addCastIntention && TypeConversionUtil.areTypesConvertible(type, throwable)) { - if (context instanceof PsiExpression) { - IntentionAction action = getFixFactory().createAddTypeCastFix(throwable, (PsiExpression)context); - highlightInfo.registerFix(action, null, null, null, null); - } - } - - PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(type); - if (aClass != null) { - IntentionAction action = getFixFactory().createExtendsListFix(aClass, throwable, true); - highlightInfo.registerFix(action, null, null, null, null); - } - return highlightInfo; - } - return null; - } - - static HighlightInfo.Builder checkReference(@NotNull PsiJavaCodeReferenceElement ref, @NotNull JavaResolveResult result, @NotNull PsiFile containingFile, 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 fc2ad6d87684..eca1d91d72cd 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 @@ -478,12 +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() && - parent instanceof PsiThrowStatement statement && - statement.getException() == expression && - type != null) { - add(HighlightUtil.checkMustBeThrowable(type, expression, true)); - } if (!hasErrorResults() && shouldReportForeachNotApplicable(expression)) { add(GenericsHighlightUtil.checkForeachExpressionTypeIsIterable(expression)); } @@ -680,17 +674,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh if (!hasErrorResults()) add(HighlightControlFlowUtil.checkRecordComponentInitialized(recordComponent)); } - @Override - public void visitParameter(@NotNull PsiParameter parameter) { - super.visitParameter(parameter); - - PsiElement parent = parameter.getParent(); - if (parent instanceof PsiCatchSection) { - if (!hasErrorResults()) add(HighlightUtil.checkCatchParameterIsThrowable(parameter)); - if (!hasErrorResults()) GenericsHighlightUtil.checkCatchParameterIsClass(parameter, myErrorSink); - } - } - @Override public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) { super.visitReferenceElement(ref); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/MustBeThrowable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/MustBeThrowable.java index bd4e59b434db..e1ac4d790803 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/MustBeThrowable.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/MustBeThrowable.java @@ -23,16 +23,16 @@ class a60 { public void test() throws MyException { - throw new MyException(); + throw new MyException(); } public void test(int i) { switch (i) { - case 1: throw false; - case 2: throw 1; - case 3: throw 1.0; - case 4: throw 'a'; - case 5: throw 1L; - case 6: throw 1.0f; + case 1: throw false; + case 2: throw 1; + case 3: throw 1.0; + case 4: throw 'a'; + case 5: throw 1L; + case 6: throw 1.0f; } } }