From e77c362643e63dc18a9fb6bbeea06a82d4650d71 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 20 Jan 2025 17:15:37 +0100 Subject: [PATCH] [java-highlighting] unhandled exceptions, string templates -> ExpressionChecker Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: 0172c8851d083ddc6ee5dad2a74fd27e7374e9ff --- .../JavaCompilationErrorBundle.properties | 4 + .../highlighting/ExpressionChecker.java | 69 +++++++++++++++++ .../highlighting/JavaErrorVisitor.java | 40 +++++++--- .../errors/JavaErrorFormatUtil.java | 24 ++++++ .../errors/JavaErrorHighlightType.java | 7 +- .../highlighting/errors/JavaErrorKinds.java | 9 ++- .../impl/analysis/ErrorFixExtensionPoint.java | 17 ++-- .../impl/analysis/HighlightFixUtil.java | 22 ++---- .../impl/analysis/HighlightMethodUtil.java | 9 +-- .../daemon/impl/analysis/HighlightUtil.java | 77 +------------------ .../impl/analysis/HighlightVisitorImpl.java | 35 +-------- .../impl/analysis/JavaErrorFixProvider.java | 26 ++++--- .../StringTemplates.java | 2 +- .../StringTemplatesJava22.java | 2 +- 14 files changed, 181 insertions(+), 162 deletions(-) diff --git a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties index d70cd9882256..ce39be33cc70 100644 --- a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties +++ b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties @@ -229,6 +229,10 @@ pattern.type.pattern.expected=Type pattern expected expression.expected=Expression expected +string.template.void.not.allowed.in.embedded=Expression with the 'void' type is not allowed as a string template embedded expression +string.template.processor.missing=Processor missing from string template expression +string.template.raw.processor=Raw processor type is not allowed: {0} + modifier.not.allowed=Modifier ''{0}'' not allowed here modifier.not.allowed.local.class=Modifier ''{0}'' not allowed on local classes modifier.not.allowed.non.sealed=Modifier 'non-sealed' is not allowed on classes that do not have a sealed superclass 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 8ff479508397..aae663675452 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 @@ -1,16 +1,22 @@ // 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.codeInsight.ExceptionUtil; import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds; import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext; +import com.intellij.pom.java.JavaFeature; import com.intellij.psi.*; import com.intellij.psi.impl.IncompleteModelUtil; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.util.*; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.Map; + import static com.intellij.util.ObjectUtils.tryCast; final class ExpressionChecker { @@ -308,6 +314,8 @@ final class ExpressionChecker { PsiSubstitutor substitutor = resolveResult.getSubstitutor(); if (resolved instanceof PsiMethod psiMethod && resolveResult.isValidResult()) { + checkUnhandledExceptions(methodCall); + if (myVisitor.hasErrorResults()) return; } else { @@ -325,6 +333,67 @@ final class ExpressionChecker { } } + void checkTemplateExpression(@NotNull PsiTemplateExpression templateExpression) { + myVisitor.checkFeature(templateExpression, JavaFeature.STRING_TEMPLATES); + if (myVisitor.hasErrorResults()) return; + PsiExpression processor = templateExpression.getProcessor(); + if (processor == null) { + myVisitor.report(JavaErrorKinds.STRING_TEMPLATE_PROCESSOR_MISSING.create(templateExpression)); + return; + } + PsiType type = processor.getType(); + if (type == null) return; + + PsiElementFactory factory = JavaPsiFacade.getElementFactory(processor.getProject()); + 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)) { + return; + } + myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create(processor, new JavaIncompatibleTypeErrorContext(processorType, type))); + return; + } + + PsiClass processorClass = processorType.resolve(); + if (processorClass == null) return; + for (PsiClassType classType : PsiTypesUtil.getClassTypeComponents(type)) { + if (!TypeConversionUtil.isAssignable(processorType, classType)) continue; + PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics(); + PsiClass aClass = resolveResult.getElement(); + if (aClass == null) continue; + PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(processorClass, aClass, resolveResult.getSubstitutor()); + if (substitutor == null) continue; + Map substitutionMap = substitutor.getSubstitutionMap(); + if (substitutionMap.isEmpty() || substitutionMap.containsValue(null)) { + myVisitor.report(JavaErrorKinds.STRING_TEMPLATE_RAW_PROCESSOR.create(processor, type)); + return; + } + } + } + + private static boolean shouldHighlightUnhandledException(@NotNull PsiElement element) { + // JSP top-level errors are handled by UnhandledExceptionInJSP inspection + if (FileTypeUtils.isInServerPageFile(element)) { + PsiMethod targetMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class, true, PsiLambdaExpression.class); + if (targetMethod instanceof SyntheticElement) { + return false; + } + } + + return true; + } + + void checkUnhandledExceptions(@NotNull PsiElement element) { + List unhandled = ExceptionUtil.getOwnUnhandledExceptions(element); + if (unhandled.isEmpty()) return; + unhandled = ContainerUtil.filter(unhandled, type -> type.resolve() != null); + if (unhandled.isEmpty()) return; + + if (!shouldHighlightUnhandledException(element)) return; + + myVisitor.report(JavaErrorKinds.EXCEPTION_UNHANDLED.create(element, unhandled)); + } + boolean isDummyConstructorCall(@NotNull PsiMethodCallExpression methodCall, @NotNull PsiExpressionList list, @NotNull PsiReferenceExpression referenceToMethod) { 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 858f9c4ac08d..f17335276da3 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 @@ -171,6 +171,27 @@ final class JavaErrorVisitor extends JavaElementVisitor { public void visitEnumConstant(@NotNull PsiEnumConstant enumConstant) { super.visitEnumConstant(enumConstant); if (!hasErrorResults()) myClassChecker.checkEnumWithAbstractMethods(enumConstant); + if (!hasErrorResults()) myExpressionChecker.checkUnhandledExceptions(enumConstant); + } + + @Override + public void visitTemplateExpression(@NotNull PsiTemplateExpression expression) { + super.visitTemplateExpression(expression); + if (!hasErrorResults()) myExpressionChecker.checkTemplateExpression(expression); + if (!hasErrorResults()) myExpressionChecker.checkUnhandledExceptions(expression); + } + + @Override + public void visitTemplate(@NotNull PsiTemplate template) { + super.visitTemplate(template); + checkFeature(template, JavaFeature.STRING_TEMPLATES); + if (hasErrorResults()) return; + + for (PsiExpression embeddedExpression : template.getEmbeddedExpressions()) { + if (PsiTypes.voidType().equals(embeddedExpression.getType())) { + report(JavaErrorKinds.STRING_TEMPLATE_VOID_NOT_ALLOWED_IN_EMBEDDED.create(embeddedExpression)); + } + } } @Override @@ -186,6 +207,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (!hasErrorResults()) myClassChecker.checkAnonymousInheritProhibited(expression); if (!hasErrorResults()) myClassChecker.checkAnonymousSealedProhibited(expression); if (!hasErrorResults()) myExpressionChecker.checkQualifiedNew(expression, type, aClass); + if (!hasErrorResults()) myExpressionChecker.checkUnhandledExceptions(expression); } @Override @@ -239,15 +261,8 @@ final class JavaErrorVisitor extends JavaElementVisitor { PsiType functionalInterfaceType = expression.getFunctionalInterfaceType(); if (functionalInterfaceType != null && !PsiTypesUtil.allTypeParametersResolved(expression, functionalInterfaceType)) return; - JavaResolveResult result; - JavaResolveResult[] results; - try { - results = expression.multiResolve(true); - result = results.length == 1 ? results[0] : JavaResolveResult.EMPTY; - } - catch (IndexNotReadyException e) { - return; - } + JavaResolveResult[] results = expression.multiResolve(true); + JavaResolveResult result = results.length == 1 ? results[0] : JavaResolveResult.EMPTY; if (!hasErrorResults()) { boolean resolvedButNonApplicable = results.length == 1 && results[0] instanceof MethodCandidateInfo methodInfo && !methodInfo.isApplicable() && @@ -262,6 +277,13 @@ final class JavaErrorVisitor extends JavaElementVisitor { } } } + if (!hasErrorResults()) myExpressionChecker.checkUnhandledExceptions(expression); + } + + @Override + public void visitThrowStatement(@NotNull PsiThrowStatement statement) { + myExpressionChecker.checkUnhandledExceptions(statement); + if (!hasErrorResults()) visitStatement(statement); } @Override diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorFormatUtil.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorFormatUtil.java index 74e8423957a7..4c1ec6cbca07 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorFormatUtil.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorFormatUtil.java @@ -14,6 +14,7 @@ import com.intellij.psi.impl.source.tree.TreeUtil; import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiFormatUtilBase; import com.intellij.psi.util.PsiTypesUtil; +import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,6 +52,29 @@ final class JavaErrorFormatUtil { return PsiFormatUtil.formatVariable(field, PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME, PsiSubstitutor.EMPTY); } + static @Nullable TextRange getRange(@NotNull PsiElement element) { + if (element instanceof PsiMember member) { + return getMemberDeclarationTextRange(member); + } + if (element instanceof PsiNewExpression newExpression) { + PsiJavaCodeReferenceElement reference = newExpression.getClassReference(); + if (reference != null) { + return reference.getTextRangeInParent(); + } + } + if (element instanceof PsiMethodCallExpression callExpression) { + PsiElement nameElement = callExpression.getMethodExpression().getReferenceNameElement(); + if (nameElement != null) { + return nameElement.getTextRangeInParent(); + } + } + PsiElement nextSibling = element.getNextSibling(); + if (PsiUtil.isJavaToken(nextSibling, JavaTokenType.SEMICOLON)) { + return TextRange.create(0, element.getTextLength() + 1); + } + return null; + } + static @NotNull TextRange getMethodDeclarationTextRange(@NotNull PsiMethod method) { if (method instanceof SyntheticElement) return TextRange.EMPTY_RANGE; int start = stripAnnotationsFromModifierList(method.getModifierList()); diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorHighlightType.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorHighlightType.java index 878321610f90..bcbe1fafa8b4 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorHighlightType.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorHighlightType.java @@ -18,5 +18,10 @@ public enum JavaErrorHighlightType { /** * Error highlighting for unresolved/unknown reference */ - WRONG_REF + WRONG_REF, + + /** + * Error highlighting for unhandled exception + */ + UNHANDLED_EXCEPTION } 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 e239f5ae1b5c..ce1cda8ca82a 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 @@ -501,7 +501,7 @@ public final class JavaErrorKinds { public static final Parameterized> EXCEPTION_UNHANDLED = error(PsiElement.class, "exception.unhandled") - .withRange(psi -> psi instanceof PsiMember member ? getMemberDeclarationTextRange(member) : null) + .withRange(JavaErrorFormatUtil::getRange) .>parameterized() .withRawDescription((psi, unhandled) -> message("exception.unhandled", formatTypes(unhandled), unhandled.size())); public static final Parameterized EXCEPTION_MUST_BE_DISJOINT = @@ -635,6 +635,13 @@ public final class JavaErrorKinds { .withRawDescription((psi, cls) -> message("call.super.qualifier.not.inner.class", formatClass(cls))); public static final Simple CALL_EXPECTED = error("call.expected"); + public static final Simple STRING_TEMPLATE_VOID_NOT_ALLOWED_IN_EMBEDDED = + error("string.template.void.not.allowed.in.embedded"); + public static final Simple STRING_TEMPLATE_PROCESSOR_MISSING = + error("string.template.processor.missing"); + public static final Parameterized STRING_TEMPLATE_RAW_PROCESSOR = + parameterized(PsiExpression.class, PsiType.class, "string.template.raw.processor") + .withRawDescription((psi, type) -> message("string.template.raw.processor", type.getPresentableText())); private static @NotNull Simple error( @NotNull @PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String key) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ErrorFixExtensionPoint.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ErrorFixExtensionPoint.java index c978021ea3b5..9b0b86051a15 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ErrorFixExtensionPoint.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ErrorFixExtensionPoint.java @@ -4,7 +4,6 @@ package com.intellij.codeInsight.daemon.impl.analysis; import com.intellij.codeInsight.daemon.JavaErrorBundle; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.intention.CommonIntentionAction; -import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.diagnostic.PluginException; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.extensions.ExtensionPointName; @@ -21,6 +20,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Consumer; public final class ErrorFixExtensionPoint implements PluginAware { private static final ExtensionPointName ERROR_FIX_EXTENSION_POINT = @@ -69,13 +69,18 @@ public final class ErrorFixExtensionPoint implements PluginAware { return map; } + public static void registerFixes(@NotNull Consumer info, + @NotNull PsiElement context, + @NotNull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String code) { + List fixes = getCodeToFixMap().getOrDefault(code, Collections.emptyList()); + for (ErrorFixExtensionPoint fix : fixes) { + info.accept(fix.instantiate(context)); + } + } + public static void registerFixes(@NotNull HighlightInfo.Builder info, @NotNull PsiElement context, @NotNull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String code) { - List fixes = getCodeToFixMap().getOrDefault(code, Collections.emptyList()); - for (ErrorFixExtensionPoint fix : fixes) { - IntentionAction action = fix.instantiate(context).asIntention(); - info.registerFix(action, null, null, null, null); - } + registerFixes(HighlightUtil.asConsumer(info), context, code); } } 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 c4f7e44dadda..fb8d9b7a16fe 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 @@ -176,23 +176,13 @@ public final class HighlightFixUtil { } } - static void registerUnhandledExceptionFixes(@NotNull PsiElement element, @NotNull HighlightInfo.Builder info) { + static void registerUnhandledExceptionFixes(@NotNull PsiElement element, @NotNull Consumer info) { final QuickFixFactory quickFixFactory = QuickFixFactory.getInstance(); - - IntentionAction action4 = quickFixFactory.createAddExceptionFromFieldInitializerToConstructorThrowsFix(element); - info.registerFix(action4, null, null, null, null); - - IntentionAction action3 = quickFixFactory.createAddExceptionToCatchFix(); - info.registerFix(action3, null, null, null, null); - - IntentionAction action2 = quickFixFactory.createAddExceptionToExistingCatch(element); - info.registerFix(action2, null, null, null, null); - - IntentionAction action1 = quickFixFactory.createAddExceptionToThrowsFix(element); - info.registerFix(action1, null, null, null, null); - - IntentionAction action = quickFixFactory.createSurroundWithTryCatchFix(element); - info.registerFix(action, null, null, null, null); + info.accept(quickFixFactory.createAddExceptionFromFieldInitializerToConstructorThrowsFix(element)); + info.accept(quickFixFactory.createAddExceptionToCatchFix()); + info.accept(quickFixFactory.createAddExceptionToExistingCatch(element)); + info.accept(quickFixFactory.createAddExceptionToThrowsFix(element)); + info.accept(quickFixFactory.createSurroundWithTryCatchFix(element)); } static void registerStaticProblemQuickFixAction(@Nullable HighlightInfo.Builder info, @NotNull PsiElement refElement, @NotNull PsiJavaCodeReferenceElement place) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index 1c5fb49929bf..ce17f7b3a82e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -209,12 +209,11 @@ public final class HighlightMethodUtil { boolean isDummy = isDummyConstructorCall(methodCall, resolveHelper, list, referenceToMethod); if (isDummy) return; - HighlightInfo.Builder builder; + HighlightInfo.Builder builder = null; PsiSubstitutor substitutor = resolveResult.getSubstitutor(); if (resolved instanceof PsiMethod psiMethod && resolveResult.isValidResult()) { - builder = HighlightUtil.checkUnhandledExceptions(methodCall); - if (builder == null && psiMethod.hasModifierProperty(PsiModifier.STATIC)) { + if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) { PsiClass containingClass = psiMethod.getContainingClass(); if (containingClass != null && containingClass.isInterface()) { PsiElement element = ObjectUtils.notNull(referenceToMethod.getReferenceNameElement(), referenceToMethod); @@ -246,7 +245,6 @@ public final class HighlightMethodUtil { PsiMethod resolvedMethod = candidateInfo != null ? candidateInfo.getElement() : null; if (!resolveResult.isAccessible() || !resolveResult.isStaticsScopeCorrect()) { - builder = null; } else if (candidateInfo != null && !candidateInfo.isApplicable()) { if (candidateInfo.isTypeArgumentsApplicable()) { @@ -279,9 +277,6 @@ public final class HighlightMethodUtil { } } } - else { - builder = null; - } } if (builder == null) { builder = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(resolved, referenceToMethod, substitutor, javaSdkVersion); 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 2515deada270..0502bd541057 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 @@ -839,42 +839,6 @@ public final class HighlightUtil { return PsiFormatUtil.formatVariable(field, PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME, PsiSubstitutor.EMPTY); } - static HighlightInfo.Builder checkUnhandledExceptions(@NotNull PsiElement element) { - List unhandled = ExceptionUtil.getOwnUnhandledExceptions(element); - if (unhandled.isEmpty()) return null; - unhandled = ContainerUtil.filter(unhandled, type -> type.resolve() != null); - if (unhandled.isEmpty()) return null; - - HighlightInfoType highlightType = getUnhandledExceptionHighlightType(element); - if (highlightType == null) return null; - - TextRange textRange = computeRange(element); - String description = getUnhandledExceptionsDescriptor(unhandled); - HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(highlightType).range(textRange).descriptionAndTooltip(description); - HighlightFixUtil.registerUnhandledExceptionFixes(element, info); - ErrorFixExtensionPoint.registerFixes(info, element, "unhandled.exceptions"); - return info; - } - - private static TextRange computeRange(@NotNull PsiElement element) { - if (element instanceof PsiNewExpression newExpression) { - PsiJavaCodeReferenceElement reference = newExpression.getClassReference(); - if (reference != null) { - return reference.getTextRange(); - } - } - if (element instanceof PsiEnumConstant constant) { - return constant.getNameIdentifier().getTextRange(); - } - if (element instanceof PsiMethodCallExpression callExpression) { - PsiElement nameElement = callExpression.getMethodExpression().getReferenceNameElement(); - if (nameElement != null) { - return nameElement.getTextRange(); - } - } - return HighlightMethodUtil.getFixRange(element); - } - static HighlightInfo.Builder checkUnhandledCloserExceptions(@NotNull PsiResourceListElement resource) { List unhandled = ExceptionUtil.getUnhandledCloserExceptions(resource, null); if (unhandled.isEmpty()) return null; @@ -885,7 +849,7 @@ public final class HighlightUtil { String description = JavaErrorBundle.message("unhandled.close.exceptions", formatTypes(unhandled), unhandled.size(), JavaErrorBundle.message("auto.closeable.resource")); HighlightInfo.Builder highlight = HighlightInfo.newHighlightInfo(highlightType).range(resource).descriptionAndTooltip(description); - HighlightFixUtil.registerUnhandledExceptionFixes(resource, highlight); + HighlightFixUtil.registerUnhandledExceptionFixes(resource, asConsumer(highlight)); return highlight; } @@ -1438,45 +1402,6 @@ public final class HighlightUtil { return null; } - static HighlightInfo.Builder checkTemplateExpression(@NotNull PsiTemplateExpression templateExpression) { - HighlightInfo.Builder builder = checkFeature(templateExpression, JavaFeature.STRING_TEMPLATES, - PsiUtil.getLanguageLevel(templateExpression), templateExpression.getContainingFile()); - if (builder != null) return builder; - PsiExpression processor = templateExpression.getProcessor(); - if (processor == null) { - String message = JavaErrorBundle.message("processor.missing.from.string.template.expression"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(templateExpression).descriptionAndTooltip(message) - .registerFix(new MissingStrProcessorFix(templateExpression), null, null, null, null); - } - PsiType type = processor.getType(); - if (type == null) return null; - - PsiElementFactory factory = JavaPsiFacade.getElementFactory(processor.getProject()); - 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)) return null; - return createIncompatibleTypeHighlightInfo(processorType, type, processor.getTextRange(), 0); - } - - PsiClass processorClass = processorType.resolve(); - if (processorClass == null) return null; - for (PsiClassType classType : PsiTypesUtil.getClassTypeComponents(type)) { - if (!TypeConversionUtil.isAssignable(processorType, classType)) continue; - PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics(); - PsiClass aClass = resolveResult.getElement(); - if (aClass == null) continue; - PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(processorClass, aClass, resolveResult.getSubstitutor()); - if (substitutor == null) continue; - Map substitutionMap = substitutor.getSubstitutionMap(); - if (substitutionMap.isEmpty() || substitutionMap.containsValue(null)) { - String text = JavaErrorBundle.message("raw.processor.type.not.allowed", type.getPresentableText()); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(processor).descriptionAndTooltip(text); - } - } - - return null; - } - static HighlightInfo.Builder checkTryResourceIsAutoCloseable(@NotNull PsiResourceListElement resource) { PsiType type = resource.getType(); if (type == null) return null; 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 f3e705f1e573..2b68d606536b 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 @@ -229,6 +229,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh JavaErrorHighlightType javaHighlightType = error.highlightType(); HighlightInfoType type = switch (javaHighlightType) { case ERROR, FILE_LEVEL_ERROR -> HighlightInfoType.ERROR; + case UNHANDLED_EXCEPTION -> HighlightInfoType.UNHANDLED_EXCEPTION; case WRONG_REF -> HighlightInfoType.WRONG_REF; }; TextRange range = error.range(); @@ -526,7 +527,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh HighlightMethodUtil.checkConstructorCall(getProject(), type.resolveGenerics(), enumConstant, type, null, myJavaSdkVersion, enumConstant.getArgumentList(), myErrorSink); } - if (!hasErrorResults()) add(HighlightUtil.checkUnhandledExceptions(enumConstant)); } @Override @@ -773,28 +773,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } } - @Override - public void visitTemplateExpression(@NotNull PsiTemplateExpression expression) { - super.visitTemplateExpression(expression); - - add(HighlightUtil.checkTemplateExpression(expression)); - if (!hasErrorResults()) add(HighlightUtil.checkUnhandledExceptions(expression)); - } - - @Override - public void visitTemplate(@NotNull PsiTemplate template) { - super.visitTemplate(template); - add(checkFeature(template, JavaFeature.STRING_TEMPLATES)); - if (hasErrorResults()) return; - - for (PsiExpression embeddedExpression : template.getEmbeddedExpressions()) { - if (PsiTypes.voidType().equals(embeddedExpression.getType())) { - String message = JavaErrorBundle.message("expression.with.type.void.not.allowed.as.string.template.embedded.expression"); - add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(embeddedExpression).descriptionAndTooltip(message)); - } - } - } - @Override public void visitMethod(@NotNull PsiMethod method) { super.visitMethod(method); @@ -847,7 +825,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh @Override public void visitNewExpression(@NotNull PsiNewExpression expression) { PsiType type = expression.getType(); - add(HighlightUtil.checkUnhandledExceptions(expression)); if (!hasErrorResults()) add(GenericsHighlightUtil.checkTypeParameterInstantiation(expression)); if (!hasErrorResults()) add(GenericsHighlightUtil.checkGenericArrayCreation(expression, type)); try { @@ -1240,10 +1217,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh add(PsiMethodReferenceHighlightingUtil.checkRawConstructorReference(expression)); } - if (!hasErrorResults()) { - add(HighlightUtil.checkUnhandledExceptions(expression)); - } - if (!hasErrorResults()) { boolean resolvedButNonApplicable = results.length == 1 && results[0] instanceof MethodCandidateInfo methodInfo && !methodInfo.isApplicable() && @@ -1440,12 +1413,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } } - @Override - public void visitThrowStatement(@NotNull PsiThrowStatement statement) { - add(HighlightUtil.checkUnhandledExceptions(statement)); - if (!hasErrorResults()) visitStatement(statement); - } - @Override public void visitTryStatement(@NotNull PsiTryStatement statement) { super.visitTryStatement(statement); 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 ae7e32cda39e..ba63c5787cad 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 @@ -212,17 +212,22 @@ final class JavaErrorFixProvider { fix(CONSTRUCTOR_AMBIGUOUS_IMPLICIT_CALL, error -> myFactory.createAddDefaultConstructorFix( requireNonNull(error.context().psiClass().getSuperClass()))); fix(CONSTRUCTOR_NO_DEFAULT, error -> myFactory.createAddDefaultConstructorFix(error.context())); - fix(EXCEPTION_UNHANDLED, error -> { - PsiClass psiClass = error.psi() instanceof PsiClass cls ? cls : - error.psi() instanceof PsiMethod method ? method.getContainingClass() : - null; - return psiClass != null ? myFactory.createCreateConstructorMatchingSuperFix(psiClass) : null; - }); - fix(EXCEPTION_UNHANDLED, error -> { - if (error.psi() instanceof PsiMethod method) { - return myFactory.createAddExceptionToThrowsFix(method, error.context()); + multi(EXCEPTION_UNHANDLED, error -> { + List registrar = new ArrayList<>(); + PsiElement element = error.psi(); + HighlightFixUtil.registerUnhandledExceptionFixes(element, registrar::add); + if (element instanceof PsiMethod method) { + registrar.add(myFactory.createAddExceptionToThrowsFix(method, error.context())); + PsiClass aClass = method.getContainingClass(); + if (aClass != null) { + registrar.add(myFactory.createCreateConstructorMatchingSuperFix(aClass)); + } } - return null; + else if (element instanceof PsiClass cls) { + registrar.add(myFactory.createCreateConstructorMatchingSuperFix(cls)); + } + ErrorFixExtensionPoint.registerFixes(registrar::add, element, "unhandled.exceptions"); + return registrar; }); } @@ -274,6 +279,7 @@ final class JavaErrorFixProvider { return registrar; } }); + fix(STRING_TEMPLATE_PROCESSOR_MISSING, error -> new MissingStrProcessorFix(error.psi())); } private void createTypeFixes() { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java index 672219f80883..a1ad3b803e42 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java @@ -119,7 +119,7 @@ class X { } public static void voidExpression() { - String a = STR."\{voidExpression()}"; + String a = STR."\{voidExpression()}"; System.out.println(a); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplatesJava22.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplatesJava22.java index cf61d4c073a7..837bcbc12313 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplatesJava22.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplatesJava22.java @@ -119,7 +119,7 @@ class X { } public static void voidExpression() { - String a = STR."\{voidExpression()}"; + String a = STR."\{voidExpression()}"; System.out.println(a); }