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 65c2c546b0a0..66c4b23e1ba8 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 @@ -102,6 +102,7 @@ public final class HighlightUtil { private static final String SERIAL_PERSISTENT_FIELDS_FIELD_NAME = "serialPersistentFields"; public static final TokenSet BRACKET_TOKENS = TokenSet.create(JavaTokenType.LBRACKET, JavaTokenType.RBRACKET); + private static final String ANONYMOUS = "anonymous"; static { ourClassIncompatibleModifiers.put(PsiModifier.ABSTRACT, Set.of(PsiModifier.FINAL)); @@ -509,7 +510,7 @@ public final class HighlightUtil { } return null; } - + static HighlightInfo.Builder checkVarTypeApplicability(@NotNull PsiVariable variable) { if (variable instanceof PsiLocalVariable && variable.getTypeElement().isInferredType()) { PsiElement parent = variable.getParent(); @@ -837,7 +838,7 @@ public final class HighlightUtil { static HighlightInfo.Builder checkUnderscore(@NotNull PsiIdentifier identifier, @NotNull LanguageLevel languageLevel) { if ("_".equals(identifier.getText())) { PsiElement parent = identifier.getParent(); - if (languageLevel.isAtLeast(LanguageLevel.JDK_1_9) && !(parent instanceof PsiUnnamedPattern) && + if (languageLevel.isAtLeast(LanguageLevel.JDK_1_9) && !(parent instanceof PsiUnnamedPattern) && !(parent instanceof PsiVariable var && var.isUnnamed())) { String text = HighlightingFeature.UNNAMED_PATTERNS_AND_VARIABLES.isSufficient(languageLevel) ? JavaErrorBundle.message("underscore.identifier.error.unnamed") @@ -1140,8 +1141,8 @@ public final class HighlightUtil { else { if (PsiModifier.STATIC.equals(modifier) || privateOrProtected || PsiModifier.PACKAGE_LOCAL.equals(modifier)) { isAllowed = modifierOwnerParent instanceof PsiClass && - (PsiModifier.STATIC.equals(modifier) || - PsiUtil.isLanguageLevel16OrHigher(modifierOwnerParent) || + (PsiModifier.STATIC.equals(modifier) || + PsiUtil.isLanguageLevel16OrHigher(modifierOwnerParent) || ((PsiClass)modifierOwnerParent).getQualifiedName() != null) || FileTypeUtils.isInServerPageFile(modifierOwnerParent) || // non-physical dummy holder might not have FQN @@ -1161,7 +1162,7 @@ public final class HighlightUtil { isAllowed &= !PsiModifier.ABSTRACT.equals(modifier); } - if (aClass.getContainingClass() instanceof PsiAnonymousClass && + if (aClass.getContainingClass() instanceof PsiAnonymousClass && privateOrProtected && !PsiUtil.isLanguageLevel16OrHigher(modifierOwnerParent)) { isAllowed = false; } @@ -1809,8 +1810,8 @@ public final class HighlightUtil { String message = JavaErrorBundle.message("exception.already.caught.warn", formatTypes(caughtCopy), caughtCopy.size()); HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(catchSection).descriptionAndTooltip(message); - IntentionAction action = isMultiCatch ? - getFixFactory().createDeleteMultiCatchFix(catchTypeElement) : + IntentionAction action = isMultiCatch ? + getFixFactory().createDeleteMultiCatchFix(catchTypeElement) : getFixFactory().createDeleteCatchFix(parameter); builder.registerFix(action, null, null, null, null); errorSink.accept(builder); @@ -3172,19 +3173,26 @@ public final class HighlightUtil { @NotNull TextRange textRange, int navigationShift, @NotNull String reason) { - lType = PsiUtil.convertAnonymousToBaseType(lType); - rType = rType == null ? null : PsiUtil.convertAnonymousToBaseType(rType); + PsiType baseLType = PsiUtil.convertAnonymousToBaseType(lType); + PsiType baseRType = rType == null ? null : PsiUtil.convertAnonymousToBaseType(rType); String styledReason = reason.isEmpty() ? "" : String .format("
%s
", reason); - String toolTip = createIncompatibleTypesTooltip(lType, rType, - (lRawType, lTypeArguments, rRawType, rTypeArguments) -> - JavaErrorBundle - .message("incompatible.types.html.tooltip", lRawType, lTypeArguments, rRawType, + String toolTip = createIncompatibleTypesTooltip(baseLType, baseRType, + (lRawType, lTypeArguments, rRawType, rTypeArguments) -> { + PairTypeResult result = getDifferentAnonymousTypes(lType, rType, lRawType, rRawType, baseLType, baseRType); + return JavaErrorBundle + .message("incompatible.types.html.tooltip", result.lRawType(), lTypeArguments, + result.rRawType(), rTypeArguments, styledReason, - "#" + ColorUtil.toHex(UIUtil.getContextHelpForeground()))); + "#" + ColorUtil.toHex(UIUtil.getContextHelpForeground())); + }); + + String lRawType = JavaHighlightUtil.formatType(baseLType); + String rRawType = JavaHighlightUtil.formatType(baseRType); + PairTypeResult result = getDifferentAnonymousTypes(lType, rType, lRawType, rRawType, baseLType, baseRType); String description = JavaErrorBundle.message( - "incompatible.types", JavaHighlightUtil.formatType(lType), JavaHighlightUtil.formatType(rType)); + "incompatible.types", result.lRawType(), result.rRawType()); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(textRange) .description(description) @@ -3192,6 +3200,38 @@ public final class HighlightUtil { .navigationShift(navigationShift); } + /** + * Gets the different types if they are equal and one of them or both is anonymous types + * + * @param lType the left type + * @param rType the right type + * @param lRawType the left raw type (base type of anonymous type) + * @param rRawType the right raw type (base type of anonymous type) + * @param baseLType the base left type (text representation) + * @param baseRType the base right type (text representation) + * @return a PairTypeResult object representing the different types + */ + @NotNull + private static PairTypeResult getDifferentAnonymousTypes(@NotNull PsiType lType, + @Nullable PsiType rType, + @NotNull String lRawType, + @NotNull String rRawType, + @NotNull PsiType baseLType, + @Nullable PsiType baseRType) { + if (lRawType.equals(rRawType)) { + if (!lType.equals(baseLType)) { + lRawType = ANONYMOUS + " " + lRawType; + } + if (rType != null && !rType.equals(baseRType)) { + rRawType = ANONYMOUS + " " + rRawType; + } + } + return new PairTypeResult(lRawType, rRawType); + } + + private record PairTypeResult(@NotNull String lRawType, @NotNull String rRawType) { + } + public static HighlightInfo.Builder checkArrayType(PsiTypeElement type) { int dimensions = 0; for (PsiElement child = type.getFirstChild(); child != null; child = child.getNextSibling()) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeObject.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeObject.java new file mode 100644 index 000000000000..f025784df631 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeObject.java @@ -0,0 +1,6 @@ +class TypeObject { + void test() { + var x = new Object() {}; + x = new Object(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/LambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/LambdaHighlightingTest.java index 71290e64b55c..0644be49a047 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/LambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/LambdaHighlightingTest.java @@ -16,8 +16,12 @@ package com.intellij.java.codeInsight.daemon.lambda; import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; +import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection; import com.intellij.pom.java.LanguageLevel; +import com.intellij.ui.ColorUtil; +import com.intellij.util.ui.UIUtil; +import org.junit.Assert; public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase { private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/highlighting/"; @@ -34,6 +38,23 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase { public void testReassignUsedVars() { doTest(); } public void testLambdaContext() { doTest(); } public void testReturnTypeCompatibility() { doTest(); } + public void testTypeObject() { + doTest(); + String toolTipForeground = ColorUtil.toHtmlColor(UIUtil.getToolTipForeground()); + String greyed = ColorUtil.toHtmlColor(UIUtil.getContextHelpForeground()); + String expected = "" + + "" + + "" + + "" + + "" + + "
Required type:anonymous Object
Provided:Object
" + + ""; + + doHighlighting() + .stream() + .filter(info -> info.type == HighlightInfoType.ERROR) + .forEach(info -> Assert.assertEquals(expected, info.getToolTip())); + } public void testTypeArgsConsistency() { doTest(); } public void testTypeArgsConsistencyMisc1() { doTest(); } public void testTypeArgsConsistencyMisc2() { doTest(); }