diff --git a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties index 321ae8ecc2a9..9e855001ddcc 100644 --- a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties +++ b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties @@ -166,3 +166,6 @@ literal.text.block.no.new.line=Illegal text block start: missing new line after exception.unhandled=Unhandled {1, choice, 0#exception|2#exceptions}: {0} #{0} - exceptions list (comma separated), {1} - exceptions count in the list, {2} - exception source exception.unhandled.close=Unhandled {1, choice, 0#exception|2#exceptions} from {2}: {0} + +call.super.enum.constructor=Call to super is not allowed in enum constructor +call.super.qualifier.not.inner.class=Qualifier is not allowed because superclass ''{0}'' is not a non-static inner class 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 d4e0ee677b00..fca550757b7d 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 @@ -727,6 +727,51 @@ final class ClassChecker { checkBaseClassDefaultConstructorProblem(aClass, constructor, handledExceptions); } + void checkEnumSuperConstructorCall(@NotNull PsiMethodCallExpression expr) { + PsiReferenceExpression methodExpression = expr.getMethodExpression(); + PsiElement refNameElement = methodExpression.getReferenceNameElement(); + if (refNameElement != null && PsiKeyword.SUPER.equals(refNameElement.getText())) { + PsiMember constructor = PsiUtil.findEnclosingConstructorOrInitializer(expr); + if (constructor instanceof PsiMethod) { + PsiClass aClass = constructor.getContainingClass(); + if (aClass != null && aClass.isEnum()) { + myVisitor.report(JavaErrorKinds.CALL_SUPER_ENUM_CONSTRUCTOR.create(expr)); + } + } + } + } + + void checkSuperQualifierType(@NotNull PsiMethodCallExpression superCall) { + if (!JavaPsiConstructorUtil.isSuperConstructorCall(superCall)) return; + PsiMethod ctr = PsiTreeUtil.getParentOfType(superCall, PsiMethod.class, true, PsiMember.class); + if (ctr == null) return; + PsiClass aClass = ctr.getContainingClass(); + if (aClass == null) return; + PsiClass targetClass = aClass.getSuperClass(); + if (targetClass == null) return; + PsiExpression qualifier = superCall.getMethodExpression().getQualifierExpression(); + if (qualifier != null) { + if (isRealInnerClass(targetClass)) { + PsiClass outerClass = targetClass.getContainingClass(); + if (outerClass != null) { + PsiClassType outerType = JavaPsiFacade.getElementFactory(myVisitor.project()).createType(outerClass); + myVisitor.myExpressionChecker.checkAssignability(outerType, null, qualifier, qualifier); + } + } else { + myVisitor.report(JavaErrorKinds.CALL_SUPER_QUALIFIER_NOT_INNER_CLASS.create(qualifier, targetClass)); + } + } + } + + /** JLS 8.1.3. Inner Classes and Enclosing Instances */ + private static boolean isRealInnerClass(PsiClass aClass) { + if (PsiUtil.isInnerClass(aClass)) return true; + if (!PsiUtil.isLocalOrAnonymousClass(aClass)) return false; + if (aClass.hasModifierProperty(PsiModifier.STATIC)) return false; // check for implicit staticness + PsiMember member = PsiTreeUtil.getParentOfType(aClass, PsiMember.class, true); + return member != null && !member.hasModifierProperty(PsiModifier.STATIC); + } + private static @Unmodifiable @NotNull Map getPermittedClassesRefs(@NotNull PsiClass psiClass) { PsiReferenceList permitsList = psiClass.getPermitsList(); if (permitsList == null) return Collections.emptyMap(); 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 45dae992d0e4..f43d7d3b6d85 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 @@ -2,10 +2,13 @@ package com.intellij.java.codeserver.highlighting; import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds; +import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeError; import com.intellij.psi.*; +import com.intellij.psi.impl.IncompleteModelUtil; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -88,4 +91,28 @@ final class ExpressionChecker { myVisitor.report(JavaErrorKinds.CLASS_CANNOT_BE_REFERENCED_FROM_STATIC_CONTEXT.create(elementToHighlight, context)); } } + + void checkAssignability(@Nullable PsiType lType, + @Nullable PsiType rType, + @Nullable PsiExpression expression, + @NotNull PsiElement elementToHighlight) { + if (lType == rType) return; + if (expression == null) { + if (rType == null || lType == null || TypeConversionUtil.isAssignable(lType, rType)) return; + } + else if (TypeConversionUtil.areTypesAssignmentCompatible(lType, expression) || PsiTreeUtil.hasErrorElements(expression)) { + return; + } + if (rType == null) { + rType = expression.getType(); + } + if (lType == null || lType == PsiTypes.nullType()) { + return; + } + if (expression != null && IncompleteModelUtil.isIncompleteModel(expression) && + IncompleteModelUtil.isPotentiallyConvertible(lType, expression)) { + return; + } + myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create(elementToHighlight, new JavaIncompatibleTypeError(lType, rType))); + } } 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 36be0eff2bd7..88f2ceb39d65 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 @@ -37,7 +37,7 @@ final class JavaErrorVisitor extends JavaElementVisitor { private final @NotNull GenericsChecker myGenericsChecker = new GenericsChecker(this); private final @NotNull MethodChecker myMethodChecker = new MethodChecker(this); private final @NotNull ReceiverChecker myReceiverChecker = new ReceiverChecker(this); - private final @NotNull ExpressionChecker myExpressionChecker = new ExpressionChecker(this); + final @NotNull ExpressionChecker myExpressionChecker = new ExpressionChecker(this); private final @NotNull LiteralChecker myLiteralChecker = new LiteralChecker(this); private boolean myHasError; // true if myHolder.add() was called with HighlightInfo of >=ERROR severity. On each .visit(PsiElement) call this flag is reset. Useful to determine whether the error was already reported while visiting this PsiElement. @@ -62,6 +62,10 @@ final class JavaErrorVisitor extends JavaElementVisitor { return myFile; } + @NotNull Project project() { + return myProject; + } + @NotNull LanguageLevel languageLevel() { return myLanguageLevel; } @@ -249,6 +253,13 @@ final class JavaErrorVisitor extends JavaElementVisitor { if (!hasErrorResults()) myClassChecker.checkIllegalInstanceMemberInRecord(field); } + @Override + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { + if (!hasErrorResults()) myClassChecker.checkEnumSuperConstructorCall(expression); + if (!hasErrorResults()) myClassChecker.checkSuperQualifierType(expression); + + } + @Override public void visitIdentifier(@NotNull PsiIdentifier identifier) { PsiElement parent = identifier.getParent(); diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKind.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKind.java index 71563513945b..1cd6fc3bbc33 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKind.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKind.java @@ -5,10 +5,7 @@ import com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.HtmlChunk; import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.PropertyKey; +import org.jetbrains.annotations.*; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -229,6 +226,7 @@ public sealed interface JavaErrorKind { * @param psi psi element to bind an error instance to * @return an instance of this error */ + @Contract(pure = true) public @NotNull JavaCompilationError create(@NotNull Psi psi) { return new JavaCompilationError<>(this, psi, null); } @@ -319,6 +317,7 @@ public sealed interface JavaErrorKind { * @param context context to bind an error instance to * @return an instance of this error */ + @Contract(pure = true) public @NotNull JavaCompilationError create(@NotNull Psi psi, Context context) { return new JavaCompilationError<>(this, psi, context); } 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 f3d48c20e29a..f4008f38dbfc 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 @@ -408,6 +408,11 @@ public final class JavaErrorKinds { error(PsiLiteralExpression.class, "literal.text.block.unclosed").withRange(e -> TextRange.from(e.getTextLength(), 0)); public static final Simple LITERAL_TEXT_BLOCK_NO_NEW_LINE = error(PsiLiteralExpression.class, "literal.text.block.no.new.line").withRange(e -> TextRange.create(0, 3)); + + public static final Simple CALL_SUPER_ENUM_CONSTRUCTOR = error("call.super.enum.constructor"); + public static final Parameterized CALL_SUPER_QUALIFIER_NOT_INNER_CLASS = + parameterized(PsiExpression.class, PsiClass.class, "call.super.qualifier.not.inner.class") + .withRawDescription((psi, cls) -> message("call.super.qualifier.not.inner.class", formatClass(cls))); private static @NotNull Simple error(@NotNull String key) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AdaptExpressionTypeFixUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AdaptExpressionTypeFixUtil.java index 74a4d60575f8..1785d21fadb3 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AdaptExpressionTypeFixUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AdaptExpressionTypeFixUtil.java @@ -2,12 +2,9 @@ package com.intellij.codeInsight.daemon.impl.analysis; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.impl.quickfix.*; -import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.CommonIntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; -import com.intellij.modcommand.ModCommandAction; -import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; import com.intellij.psi.infos.MethodCandidateInfo; @@ -29,6 +26,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.function.Consumer; import static com.intellij.util.ObjectUtils.tryCast; @@ -39,8 +37,7 @@ final class AdaptExpressionTypeFixUtil { private AdaptExpressionTypeFixUtil() { } - private static void registerPatchParametersFixes(@NotNull HighlightInfo.Builder info, - @NotNull TextRange textRange, + private static void registerPatchParametersFixes(@NotNull Consumer info, @NotNull PsiMethodCallExpression call, @NotNull PsiMethod method, @NotNull PsiType expectedTypeByParent, @@ -57,7 +54,7 @@ final class AdaptExpressionTypeFixUtil { Set set = Set.of(typeParameter); if (!PsiTreeUtil.isAncestor(method, typeParameter, true)) { - registerPatchQualifierFixes(info, textRange, call, method, typeParameter, expectedTypeValue, parameters, set); + registerPatchQualifierFixes(info, call, method, typeParameter, expectedTypeValue, parameters, set); return; } @@ -81,21 +78,20 @@ final class AdaptExpressionTypeFixUtil { psiClassType.rawType().equalsToText(CommonClassNames.JAVA_LANG_CLASS) && typeParameter == getSoleTypeParameter(parameterType)) { if (expectedTypeValue instanceof PsiClassType classType && JavaGenericsUtil.isReifiableType(expectedTypeValue)) { - ReplaceExpressionAction fix = new ReplaceExpressionAction( + info.accept(new ReplaceExpressionAction( arg, classType.rawType().getCanonicalText() + ".class", - classType.rawType().getPresentableText() + ".class"); - info.registerFix(fix, null, null, null, null); + classType.rawType().getPresentableText() + ".class")); } } PsiType expectedArgType = desiredSubstitutor.substitute(parameterType); if (arg instanceof PsiLambdaExpression && parameterType instanceof PsiClassType) { - registerLambdaReturnFixes(info, textRange, (PsiLambdaExpression)arg, (PsiClassType)parameterType, expectedArgType, typeParameter); + registerLambdaReturnFixes(info, (PsiLambdaExpression)arg, (PsiClassType)parameterType, expectedArgType, typeParameter); return; } PsiType actualArgType = PsiPolyExpressionUtil.isPolyExpression(arg) ? substitutor.put(typeParameter, substitution.myActualType).substitute(parameterType) : arg.getType(); - registerExpectedTypeFixes(info, textRange, arg, expectedArgType, actualArgType); + registerExpectedTypeFixes(info, false, arg, expectedArgType, actualArgType); } private static @Nullable PsiParameter findWrongParameter(@NotNull PsiMethodCallExpression call, @@ -131,8 +127,7 @@ final class AdaptExpressionTypeFixUtil { return ContainerUtil.getOnlyItem(candidates); } - private static void registerPatchQualifierFixes(@NotNull HighlightInfo.Builder info, - @NotNull TextRange textRange, + private static void registerPatchQualifierFixes(@NotNull Consumer info, @NotNull PsiMethodCallExpression call, @NotNull PsiMethod method, @NotNull PsiTypeParameter typeParameter, @@ -154,12 +149,11 @@ final class AdaptExpressionTypeFixUtil { .createType(qualifierClass, classResolveResult.getSubstitutor().put(typeParameter, expectedTypeValue)); PsiType actualType = qualifierCall.getType(); if (actualType != null && !expectedQualifierType.equals(actualType)) { - registerPatchParametersFixes(info, textRange, qualifierCall, qualifierMethod, expectedQualifierType, actualType); + registerPatchParametersFixes(info, qualifierCall, qualifierMethod, expectedQualifierType, actualType); } } - private static void registerLambdaReturnFixes(@NotNull HighlightInfo.Builder info, - @NotNull TextRange textRange, + private static void registerLambdaReturnFixes(@NotNull Consumer info, @NotNull PsiLambdaExpression arg, @NotNull PsiClassType parameterType, @Nullable PsiType expectedArgType, @@ -188,7 +182,7 @@ final class AdaptExpressionTypeFixUtil { } PsiType expectedFnReturnType = LambdaUtil.getFunctionalInterfaceReturnType(expectedArgType); if (expectedFnReturnType == null) return; - registerExpectedTypeFixes(info, textRange, lambdaBody, expectedFnReturnType); + registerExpectedTypeFixes(info, false, lambdaBody, expectedFnReturnType); } /** @@ -198,7 +192,16 @@ final class AdaptExpressionTypeFixUtil { * @param expression expression whose type is incorrect * @param expectedType desired expression type. */ - static void registerExpectedTypeFixes(@NotNull HighlightInfo.Builder info, @NotNull TextRange textRange,@NotNull PsiExpression expression, @Nullable PsiType expectedType) { + static void registerExpectedTypeFixes(@NotNull Consumer info, + @NotNull PsiExpression expression, + @Nullable PsiType expectedType) { + registerExpectedTypeFixes(info, true, expression, expectedType); + } + + private static void registerExpectedTypeFixes(@NotNull Consumer info, + boolean wholeRange, + @NotNull PsiExpression expression, + @Nullable PsiType expectedType) { PsiType actualType; if (PsiPolyExpressionUtil.isPolyExpression(expression)) { actualType = ((PsiExpression)expression.copy()).getType(); @@ -206,7 +209,7 @@ final class AdaptExpressionTypeFixUtil { else { actualType = expression.getType(); } - registerExpectedTypeFixes(info, textRange, expression, expectedType, actualType); + registerExpectedTypeFixes(info, wholeRange, expression, expectedType, actualType); } /** @@ -217,35 +220,36 @@ final class AdaptExpressionTypeFixUtil { * @param expectedType desired expression type * @param actualType actual expression type */ - static void registerExpectedTypeFixes(@NotNull HighlightInfo.Builder info, - @NotNull TextRange textRange, + static void registerExpectedTypeFixes(@NotNull Consumer info, + @NotNull PsiExpression expression, + @Nullable PsiType expectedType, + @Nullable PsiType actualType) { + registerExpectedTypeFixes(info, true, expression, expectedType, actualType); + } + + private static void registerExpectedTypeFixes(@NotNull Consumer info, + boolean wholeRange, @NotNull PsiExpression expression, @Nullable PsiType expectedType, @Nullable PsiType actualType) { if (actualType == null || expectedType == null) return; boolean mentionsTypeArgument = mentionsTypeArgument(expression, actualType); expectedType = GenericsUtil.getVariableTypeByExpressionType(expectedType); - TextRange range = expression.getTextRange(); - String role = textRange.equals(range) ? null : getRole(expression); + String role = wholeRange ? null : getRole(expression); if (!mentionsTypeArgument) { - IntentionAction action3 = new WrapWithAdapterMethodCallFix(expectedType, expression, role); - info.registerFix(action3, null, null, null, null); - IntentionAction action2 = QuickFixFactory.getInstance().createWrapWithOptionalFix(expectedType, expression); - info.registerFix(action2, null, null, null, null); - var action1 = new WrapExpressionFix(expectedType, expression, role); - info.registerFix(action1, null, null, null, null); + info.accept(new WrapWithAdapterMethodCallFix(expectedType, expression, role)); + info.accept(QuickFixFactory.getInstance().createWrapWithOptionalFix(expectedType, expression)); + info.accept(new WrapExpressionFix(expectedType, expression, role)); PsiType castToType = suggestCastTo(expression, expectedType, actualType); if (castToType != null) { - ModCommandAction action = new AddTypeCastFix(castToType, expression, role); - info.registerFix(action, null, null, null, null); + info.accept(new AddTypeCastFix(castToType, expression, role)); } } if (expectedType instanceof PsiArrayType arrayType) { PsiType erasedValueType = TypeConversionUtil.erasure(actualType); if (erasedValueType != null && TypeConversionUtil.isAssignable(arrayType.getComponentType(), erasedValueType)) { - IntentionAction action = QuickFixFactory.getInstance().createSurroundWithArrayFix(null, expression); - info.registerFix(action, null, null, null, null); + info.accept(QuickFixFactory.getInstance().createSurroundWithArrayFix(null, expression)); } } HighlightFixUtil.registerCollectionToArrayFixAction(info, actualType, expectedType, expression); @@ -254,12 +258,12 @@ final class AdaptExpressionTypeFixUtil { if (qualifier != null) { PsiType type = qualifier.getType(); if (type != null && expectedType.isAssignableFrom(type)) { - info.registerFix(new ReplaceWithQualifierFix(call, role), null, null, null, null); + info.accept(new ReplaceWithQualifierFix(call, role)); } } PsiMethod argMethod = call.resolveMethod(); if (argMethod != null) { - registerPatchParametersFixes(info, textRange, call, argMethod, expectedType, actualType); + registerPatchParametersFixes(info, call, argMethod, expectedType, actualType); } } } 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 a8d84f37d6df..81779acf0f23 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 @@ -1043,22 +1043,6 @@ public final class GenericsHighlightUtil { return null; } - static HighlightInfo.Builder checkEnumSuperConstructorCall(@NotNull PsiMethodCallExpression expr) { - PsiReferenceExpression methodExpression = expr.getMethodExpression(); - PsiElement refNameElement = methodExpression.getReferenceNameElement(); - if (refNameElement != null && PsiKeyword.SUPER.equals(refNameElement.getText())) { - PsiMember constructor = PsiUtil.findEnclosingConstructorOrInitializer(expr); - if (constructor instanceof PsiMethod) { - PsiClass aClass = constructor.getContainingClass(); - if (aClass != null && aClass.isEnum()) { - String message = JavaErrorBundle.message("call.to.super.is.not.allowed.in.enum.constructor"); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expr).descriptionAndTooltip(message); - } - } - } - return null; - } - static HighlightInfo.Builder checkVarArgParameterIsLast(@NotNull PsiParameter parameter) { PsiElement declarationScope = parameter.getDeclarationScope(); if (declarationScope instanceof PsiMethod psiMethod) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index d8a59058c47e..2077cde974f2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -7,13 +7,11 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.util.*; -import com.intellij.util.JavaPsiConstructorUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -76,39 +74,6 @@ public final class HighlightClassUtil { return checkIllegalEnclosingUsage(placeToSearchEnclosingFrom, aClass, outerClass, element); } - static HighlightInfo.Builder checkSuperQualifierType(@NotNull Project project, @NotNull PsiMethodCallExpression superCall) { - if (!JavaPsiConstructorUtil.isSuperConstructorCall(superCall)) return null; - PsiMethod ctr = PsiTreeUtil.getParentOfType(superCall, PsiMethod.class, true, PsiMember.class); - if (ctr == null) return null; - PsiClass aClass = ctr.getContainingClass(); - if (aClass == null) return null; - PsiClass targetClass = aClass.getSuperClass(); - if (targetClass == null) return null; - PsiExpression qualifier = superCall.getMethodExpression().getQualifierExpression(); - if (qualifier != null) { - if (isRealInnerClass(targetClass)) { - PsiClass outerClass = targetClass.getContainingClass(); - if (outerClass != null) { - PsiClassType outerType = JavaPsiFacade.getElementFactory(project).createType(outerClass); - return HighlightUtil.checkAssignability(outerType, null, qualifier, qualifier); - } - } else { - String description = JavaErrorBundle.message("not.inner.class", HighlightUtil.formatClass(targetClass)); - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(qualifier).descriptionAndTooltip(description); - } - } - return null; - } - - /** JLS 8.1.3. Inner Classes and Enclosing Instances */ - private static boolean isRealInnerClass(PsiClass aClass) { - if (PsiUtil.isInnerClass(aClass)) return true; - if (!PsiUtil.isLocalOrAnonymousClass(aClass)) return false; - if (aClass.hasModifierProperty(PsiModifier.STATIC)) return false; // check for implicit staticness - PsiMember member = PsiTreeUtil.getParentOfType(aClass, PsiMember.class, true); - return member != null && !member.hasModifierProperty(PsiModifier.STATIC); - } - static HighlightInfo.Builder checkIllegalEnclosingUsage(@NotNull PsiElement place, @Nullable PsiClass aClass, @NotNull PsiClass outerClass, 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 e22c374ae038..ca05ede18aa8 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 @@ -7,6 +7,7 @@ import com.intellij.codeInsight.daemon.impl.quickfix.QualifyMethodCallFix; import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction; import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceAssignmentFromVoidWithStatementIntentionAction; import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceGetClassWithClassLiteralFix; +import com.intellij.codeInsight.intention.CommonIntentionAction; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper; @@ -35,13 +36,14 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; public final class HighlightFixUtil { private static final Logger LOG = Logger.getInstance(HighlightFixUtil.class); private static final CallMatcher COLLECTION_TO_ARRAY = CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "toArray").parameterCount(0); - static void registerCollectionToArrayFixAction(@NotNull HighlightInfo.Builder info, + static void registerCollectionToArrayFixAction(@NotNull Consumer info, @Nullable PsiType fromType, @Nullable PsiType toType, @NotNull PsiExpression expression) { @@ -60,8 +62,7 @@ public final class HighlightFixUtil { InheritanceUtil.isInheritor(fromType, CommonClassNames.JAVA_UTIL_COLLECTION)) { PsiType collectionItemType = JavaGenericsUtil.getCollectionItemType(fromType, expression.getResolveScope()); if (collectionItemType != null && arrayComponentType.isConvertibleFrom(collectionItemType)) { - IntentionAction action = QuickFixFactory.getInstance().createCollectionToArrayFix(collection, expression, (PsiArrayType)toType); - info.registerFix(action, null, null, null, null); + info.accept(QuickFixFactory.getInstance().createCollectionToArrayFix(collection, expression, (PsiArrayType)toType)); } } } @@ -234,25 +235,25 @@ public final class HighlightFixUtil { @Nullable PsiType itemType, @Nullable PsiExpression expr, @Nullable HighlightInfo.Builder highlightInfo) { + if (highlightInfo == null) return; for (IntentionAction action : getChangeVariableTypeFixes(parameter, itemType)) { - if (highlightInfo != null) { - highlightInfo.registerFix(action, null, null, null, null); - } + highlightInfo.registerFix(action, null, null, null, null); + } + IntentionAction fix = createChangeReturnTypeFix(expr, parameter.getType()); + if (fix != null) { + highlightInfo.registerFix(fix, null, null, null, null); } - registerChangeReturnTypeFix(highlightInfo, expr, parameter.getType()); } - static void registerChangeReturnTypeFix(@Nullable HighlightInfo.Builder highlightInfo, @Nullable PsiExpression expr, @NotNull PsiType toType) { + static @Nullable IntentionAction createChangeReturnTypeFix(@Nullable PsiExpression expr, @NotNull PsiType toType) { if (expr instanceof PsiMethodCallExpression) { PsiMethod method = ((PsiMethodCallExpression)expr).resolveMethod(); if (method != null) { - IntentionAction action = PriorityIntentionActionWrapper + return PriorityIntentionActionWrapper .lowPriority(QuickFixFactory.getInstance().createMethodReturnFix(method, toType, true)); - if (highlightInfo != null) { - highlightInfo.registerFix(action, null, null, null, null); - } } } + return null; } /** @@ -331,11 +332,10 @@ public final class HighlightFixUtil { } } - static void registerLambdaReturnTypeFixes(@Nullable HighlightInfo.Builder info, @NotNull TextRange range, PsiLambdaExpression lambda, PsiExpression expression) { - if (info == null) return; + static void registerLambdaReturnTypeFixes(@NotNull Consumer info, PsiLambdaExpression lambda, PsiExpression expression) { PsiType type = LambdaUtil.getFunctionalInterfaceReturnType(lambda); if (type != null) { - AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(info, range, expression, type); + AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(info, expression, type); } } 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 98d248d29af9..a45244acff1c 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 @@ -455,7 +455,8 @@ public final class HighlightMethodUtil { PsiType actualType = ((PsiExpression)methodCall.copy()).getType(); TextRange fixRange = getFixRange(list); if (expectedTypeByParent != null && actualType != null && !expectedTypeByParent.isAssignableFrom(actualType)) { - AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(builder, fixRange, methodCall, expectedTypeByParent, actualType); + AdaptExpressionTypeFixUtil.registerExpectedTypeFixes( + HighlightUtil.asConsumer(builder), methodCall, expectedTypeByParent, actualType); } HighlightFixUtil.registerQualifyMethodCallFix(resolveHelper.getReferencedMethodCandidates(methodCall, false), methodCall, list, builder); @@ -647,7 +648,8 @@ public final class HighlightMethodUtil { builder = HighlightUtil.createIncompatibleTypeHighlightInfo( expectedTypeByParent, actualType, fixRange, 0, XmlStringUtil.escapeString(errorMessage)); if (methodCall instanceof PsiExpression) { - AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(builder, fixRange, (PsiExpression)methodCall, expectedTypeByParent, actualType); + AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(HighlightUtil.asConsumer(builder), + (PsiExpression)methodCall, expectedTypeByParent, actualType); } PsiElement parent = PsiUtil.skipParenthesizedExprUp(methodCall.getParent()); if (parent instanceof PsiReturnStatement) { 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 f191b6d72f82..8bac204c176c 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 @@ -19,6 +19,7 @@ import com.intellij.core.JavaPsiBundle; import com.intellij.ide.IdeBundle; import com.intellij.java.analysis.JavaAnalysisBundle; import com.intellij.lang.injection.InjectedLanguageManager; +import com.intellij.modcommand.ModCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.LanguageLevelUtil; import com.intellij.openapi.module.Module; @@ -457,7 +458,7 @@ public final class HighlightUtil { return highlightInfo; } - private static boolean isCastIntentionApplicable(@NotNull PsiExpression expression, @Nullable PsiType toType) { + static boolean isCastIntentionApplicable(@NotNull PsiExpression expression, @Nullable PsiType toType) { while (expression instanceof PsiTypeCastExpression || expression instanceof PsiParenthesizedExpression) { if (expression instanceof PsiTypeCastExpression castExpression) { expression = castExpression.getOperand(); @@ -610,21 +611,34 @@ public final class HighlightUtil { return null; } HighlightInfo.Builder highlightInfo = createIncompatibleTypeHighlightInfo(lType, rType, textRange, navigationShift); - AddTypeArgumentsConditionalFix.register(highlightInfo, expression, lType); + AddTypeArgumentsConditionalFix.register(asConsumer(highlightInfo), expression, lType); if (rType != null && expression != null && isCastIntentionApplicable(expression, lType)) { IntentionAction action = getFixFactory().createAddTypeCastFix(lType, expression); highlightInfo.registerFix(action, null, null, null, null); } if (expression != null) { - AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(highlightInfo, textRange, expression, lType, rType); + AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(asConsumer(highlightInfo), expression, lType, rType); if (!(expression.getParent() instanceof PsiConditionalExpression && PsiTypes.voidType().equals(lType))) { - HighlightFixUtil.registerChangeReturnTypeFix(highlightInfo, expression, lType); + IntentionAction fix = HighlightFixUtil.createChangeReturnTypeFix(expression, lType); + if (fix != null) { + highlightInfo.registerFix(fix, null, null, null, null); + } } } - ChangeNewOperatorTypeFix.register(highlightInfo, expression, lType); + ModCommandAction fix = ChangeNewOperatorTypeFix.createFix(expression, lType); + if (fix != null) { + highlightInfo.registerFix(fix, null, null, null, null); + } return highlightInfo; } + static @NotNull Consumer asConsumer(HighlightInfo.Builder highlightInfo) { + if (highlightInfo == null) { + return fix -> {}; + } + return fix -> highlightInfo.registerFix(fix.asIntention(), null, null, null, null); + } + static HighlightInfo.Builder checkReturnFromSwitchExpr(@NotNull PsiReturnStatement statement) { if (PsiImplUtil.findEnclosingSwitchExpression(statement) != null) { String message = JavaErrorBundle.message("return.outside.switch.expr"); 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 d41c78d8e0a7..e38b0e079e5a 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 @@ -400,7 +400,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh info.registerFix(action, null, null, null, null); } if (element instanceof PsiExpression expr) { - HighlightFixUtil.registerLambdaReturnTypeFixes(info, element.getTextRange(), expression, expr); + HighlightFixUtil.registerLambdaReturnTypeFixes(HighlightUtil.asConsumer(info), expression, expr); } add(info); } @@ -845,8 +845,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh @Override public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { - if (!hasErrorResults()) add(GenericsHighlightUtil.checkEnumSuperConstructorCall(expression)); - if (!hasErrorResults()) add(HighlightClassUtil.checkSuperQualifierType(myFile.getProject(), expression)); + visitElement(expression); if (!hasErrorResults()) { try { HighlightMethodUtil.checkMethodCall(expression, getResolveHelper(), myLanguageLevel, myJavaSdkVersion, myFile, 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 7c8841cbafbb..a176b8bc3205 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 @@ -3,10 +3,7 @@ package com.intellij.codeInsight.daemon.impl.analysis; import com.intellij.codeInsight.ClassUtil; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationOnStaticMemberQualifyingTypeFix; -import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationToPackageInfoFileFix; -import com.intellij.codeInsight.daemon.impl.quickfix.MoveMembersIntoClassFix; -import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceVarWithExplicitTypeFix; +import com.intellij.codeInsight.daemon.impl.quickfix.*; import com.intellij.codeInsight.intention.CommonIntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; @@ -36,6 +33,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil.isCastIntentionApplicable; import static com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds.*; import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNullElse; @@ -146,6 +144,19 @@ final class JavaErrorFixProvider { return List.of(myFactory.createExtendsListFix(usedClass, throwableType, true)); } } + if (anchor instanceof PsiExpression expression) { + List registrar = new ArrayList<>(); + AddTypeArgumentsConditionalFix.register(registrar::add, expression, context.lType()); + if (context.rType() != null && isCastIntentionApplicable(expression, context.lType())) { + ContainerUtil.addIfNotNull(registrar, myFactory.createAddTypeCastFix(context.lType(), expression)); + } + AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(registrar::add, expression, context.lType(), context.rType()); + if (!(expression.getParent() instanceof PsiConditionalExpression && PsiTypes.voidType().equals(context.lType()))) { + ContainerUtil.addIfNotNull(registrar, HighlightFixUtil.createChangeReturnTypeFix(expression, context.lType())); + } + ContainerUtil.addIfNotNull(registrar, ChangeNewOperatorTypeFix.createFix(expression, context.lType())); + return registrar; + } return List.of(); }); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsConditionalFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsConditionalFix.java index ba651296e1e5..7d46deef45a1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsConditionalFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsConditionalFix.java @@ -1,13 +1,10 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInsight.daemon.impl.quickfix; -import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.codeInsight.intention.CommonIntentionAction; import com.intellij.codeInsight.intention.PriorityAction; import com.intellij.java.analysis.JavaAnalysisBundle; -import com.intellij.modcommand.ActionContext; -import com.intellij.modcommand.ModPsiUpdater; -import com.intellij.modcommand.Presentation; -import com.intellij.modcommand.PsiUpdateModCommandAction; +import com.intellij.modcommand.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; @@ -17,6 +14,8 @@ import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.function.Consumer; + public class AddTypeArgumentsConditionalFix extends PsiUpdateModCommandAction { private static final Logger LOG = Logger.getInstance(AddTypeArgumentsConditionalFix.class); @@ -73,7 +72,7 @@ public class AddTypeArgumentsConditionalFix extends PsiUpdateModCommandAction highlightInfo, @Nullable PsiExpression expression, @NotNull PsiType lType) { if (lType != PsiTypes.nullType() && expression instanceof PsiConditionalExpression) { final PsiExpression thenExpression = ((PsiConditionalExpression)expression).getThenExpression(); final PsiExpression elseExpression = ((PsiConditionalExpression)expression).getElseExpression(); @@ -94,8 +93,8 @@ public class AddTypeArgumentsConditionalFix extends PsiUpdateModCommandAction fixConsumer, PsiType lType, PsiExpression expression) { + final JavaResolveResult result = ((PsiMethodCallExpression)expression).resolveMethodGenerics(); final PsiMethod method = (PsiMethod)result.getElement(); if (method != null) { final PsiType returnType = method.getReturnType(); @@ -103,19 +102,18 @@ public class AddTypeArgumentsConditionalFix extends PsiUpdateModCommandActionpublic B() {} + public B() {} } -class C extends A {} +class C extends A {} class A1 { A1(String... i){}