Java: improve incompatible types reporting

- only report type with "anonymous" prefix when the left side is anonymous
- use short names in the tool tip when fq names would be identical as well
- use error color for the entire type name, including the anonymous prefix

GitOrigin-RevId: f1555687adb0ab9e7c5850719165a7a508ecf181
This commit is contained in:
Bas Leijdekkers
2024-02-11 16:31:14 +00:00
committed by intellij-monorepo-bot
parent 8c259fa269
commit 0cae65939b
4 changed files with 52 additions and 59 deletions
@@ -144,7 +144,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";
private static final @NlsSafe String ANONYMOUS = "anonymous ";
private HighlightUtil() { }
@@ -3208,26 +3208,24 @@ public final class HighlightUtil {
@NotNull
static HighlightInfo.Builder createIncompatibleTypeHighlightInfo(@NotNull PsiType lType,
@Nullable PsiType rType,
@NotNull TextRange textRange,
int navigationShift,
@NotNull String reason) {
@Nullable PsiType rType,
@NotNull TextRange textRange,
int navigationShift,
@NotNull String reason) {
PsiType baseLType = PsiUtil.convertAnonymousToBaseType(lType);
PsiType baseRType = rType == null ? null : PsiUtil.convertAnonymousToBaseType(rType);
boolean leftAnonymous = PsiUtil.resolveClassInClassTypeOnly(lType) instanceof PsiAnonymousClass;
String styledReason = reason.isEmpty() ? "" :
String.format("<table><tr><td style=''padding-top: 10px; padding-left: 4px;''>%s</td></tr></table>", reason);
IncompatibleTypesTooltipComposer tooltipComposer = (lTypeString, lTypeArguments, rTypeString, rTypeArguments) -> {
lTypeString = addAnonymousIfNecessary(lType, lTypeString);
rTypeString = addAnonymousIfNecessary(rType, rTypeString);
return JavaErrorBundle.message("incompatible.types.html.tooltip",
lTypeString, lTypeArguments,
rTypeString, rTypeArguments,
styledReason, "#" + ColorUtil.toHex(UIUtil.getContextHelpForeground()));
};
String toolTip = createIncompatibleTypesTooltip(baseLType, baseRType, tooltipComposer);
IncompatibleTypesTooltipComposer tooltipComposer = (lTypeString, lTypeArguments, rTypeString, rTypeArguments) ->
JavaErrorBundle.message("incompatible.types.html.tooltip",
lTypeString, lTypeArguments,
rTypeString, rTypeArguments,
styledReason, "#" + ColorUtil.toHex(UIUtil.getContextHelpForeground()));
String toolTip = createIncompatibleTypesTooltip(leftAnonymous ? lType : baseLType, leftAnonymous ? rType : baseRType, tooltipComposer);
String lTypeString = JavaHighlightUtil.formatType(lType);
String rTypeString = JavaHighlightUtil.formatType(rType);
String lTypeString = JavaHighlightUtil.formatType(leftAnonymous ? lType : baseLType);
String rTypeString = JavaHighlightUtil.formatType(leftAnonymous ? rType : baseRType);
String description = JavaErrorBundle.message("incompatible.types", lTypeString, rTypeString);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(textRange)
@@ -3236,22 +3234,6 @@ public final class HighlightUtil {
.navigationShift(navigationShift);
}
/**
*
* @param type the type
* @param typeString the raw type (base type of anonymous type)
* @return String representation if `anonymous` is requried
*/
@NotNull
private static String addAnonymousIfNecessary(@Nullable PsiType type,
@NotNull String typeString) {
if (type instanceof PsiClassType lClassType &&
lClassType.resolve() instanceof PsiAnonymousClass) {
typeString = ANONYMOUS + " " + typeString;
}
return typeString;
}
public static HighlightInfo.Builder checkArrayType(PsiTypeElement type) {
int dimensions = 0;
for (PsiElement child = type.getFirstChild(); child != null; child = child.getNextSibling()) {
@@ -3321,21 +3303,17 @@ public final class HighlightUtil {
String closeBrace = i == typeParamColumns - 1 ? "&gt;" : ",";
boolean showShortType = showShortType(lSubstitutedType, rSubstitutedType);
requiredRow.append(skipColumns ? ""
: "<td style='padding: 0px 0px 8px 0px;'>")
requiredRow.append(skipColumns ? "" : "<td style='padding: 0px 0px 8px 0px;'>")
.append(lTypeParams.length == 0 ? "" : openBrace)
.append(redIfNotMatch(lSubstitutedType, true, showShortType))
.append(i < lTypeParams.length ? closeBrace : "")
.append(skipColumns ? ""
: "</td>");
.append(skipColumns ? "" : "</td>");
foundRow.append(skipColumns ? ""
: "<td style='padding: 0px 0px 0px 0px;'>")
foundRow.append(skipColumns ? "" : "<td style='padding: 0px 0px 0px 0px;'>")
.append(rTypeParams.length == 0 ? "" : openBrace)
.append(redIfNotMatch(rSubstitutedType, matches, showShortType))
.append(i < rTypeParams.length ? closeBrace : "")
.append(skipColumns ? ""
: "</td>");
.append(skipColumns ? "" : "</td>");
}
PsiType lRawType = lType instanceof PsiClassType ? ((PsiClassType)lType).rawType() : lType;
PsiType rRawType = rType instanceof PsiClassType ? ((PsiClassType)rType).rawType() : rType;
@@ -3350,7 +3328,9 @@ public final class HighlightUtil {
static boolean showShortType(@Nullable PsiType lType, @Nullable PsiType rType) {
if (Comparing.equal(lType, rType)) return true;
return lType != null && rType != null && !Comparing.strEqual(lType.getPresentableText(), rType.getPresentableText());
return lType != null && rType != null &&
(!lType.getPresentableText().equals(rType.getPresentableText()) ||
lType.getCanonicalText().equals(rType.getCanonicalText()));
}
private static @NotNull String getReasonForIncompatibleTypes(PsiType rType) {
@@ -3385,15 +3365,19 @@ public final class HighlightUtil {
@NotNull
static @NlsSafe HtmlChunk redIfNotMatch(@Nullable PsiType type, boolean matches, boolean shortType) {
if (type == null) return HtmlChunk.empty();
Color color;
if (matches) {
color = ExperimentalUI.isNewUI() ? JBUI.CurrentTheme.Editor.Tooltip.FOREGROUND : UIUtil.getToolTipForeground();
String typeText;
if (shortType || type instanceof PsiCapturedWildcardType) {
typeText = PsiUtil.resolveClassInClassTypeOnly(type) instanceof PsiAnonymousClass
? ANONYMOUS + type.getPresentableText()
: type.getPresentableText();
}
else {
color = NamedColorUtil.getErrorForeground();
typeText = type.getCanonicalText();
}
return HtmlChunk.tag("font").attr("color", ColorUtil.toHtmlColor(color))
.addText(shortType || type instanceof PsiCapturedWildcardType ? type.getPresentableText() : type.getCanonicalText());
Color color = matches
? ExperimentalUI.isNewUI() ? JBUI.CurrentTheme.Editor.Tooltip.FOREGROUND : UIUtil.getToolTipForeground()
: NamedColorUtil.getErrorForeground();
return HtmlChunk.tag("font").attr("color", ColorUtil.toHtmlColor(color)).addText(typeText);
}
@@ -0,0 +1,7 @@
class X {}
class Y {
public void main(String[] args) {
var x = new X();
<error descr="Incompatible types. Found: 'Y', required: 'X'">x = new Y() {}</error>;
}
}
@@ -1,10 +1,7 @@
class TypeObjectWithAnotherInitializer {
class X {}
class Y {
public void main(String[] args) {
var x = new X() {};
<error descr="Incompatible types. Found: 'TypeObjectWithAnotherInitializer.Y', required: 'anonymous TypeObjectWithAnotherInitializer.X'">x = new Y()</error>;
}
class X {}
class Y {
public void main(String[] args) {
var x = new X() {};
<error descr="Incompatible types. Found: 'Y', required: 'anonymous X'">x = new Y()</error>;
}
}
@@ -99,12 +99,14 @@ public class LightAdvLVTIHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testTypeObject() {
doTest();
String toolTipForeground = ColorUtil.toHtmlColor(UIUtil.getToolTipForeground());
String errorTipForeground = ColorUtil.toHtmlColor(UIUtil.getErrorForeground());
String greyed = ColorUtil.toHtmlColor(UIUtil.getContextHelpForeground());
String expected = "<html><table>" +
"<tr>" +
"<td style='padding: 0px 16px 8px 4px;color: "+greyed+"'>Required type:</td>" +
"<td style='padding: 0px 4px 8px 0px;'>anonymous <font color=\""+toolTipForeground+"\">Object</font></td></tr>" +
"<tr><td style='padding: 0px 16px 0px 4px;color: "+greyed+"'>Provided:</td><td style='padding: 0px 4px 0px 0px;'><font color=\""+toolTipForeground+"\">Object</font></td></tr>" +
"<td style='padding: 0px 4px 8px 0px;'><font color=\""+toolTipForeground+"\">anonymous Object</font></td></tr>" +
"<tr><td style='padding: 0px 16px 0px 4px;color: "+greyed+"'>Provided:</td>" +
"<td style='padding: 0px 4px 0px 0px;'><font color=\""+errorTipForeground+"\">Object</font></td></tr>" +
"</table>" +
"</html>";
@@ -122,8 +124,9 @@ public class LightAdvLVTIHighlightingTest extends LightDaemonAnalyzerTestCase {
String expected = "<html><table>" +
"<tr>" +
"<td style='padding: 0px 16px 8px 4px;color: "+greyed+"'>Required type:</td>" +
"<td style='padding: 0px 4px 8px 0px;'>anonymous <font color=\""+toolTipForeground+"\">X</font></td></tr>" +
"<tr><td style='padding: 0px 16px 0px 4px;color: "+greyed+"'>Provided:</td><td style='padding: 0px 4px 0px 0px;'><font color=\""+errorTipForeground+"\">Y</font></td></tr>" +
"<td style='padding: 0px 4px 8px 0px;'><font color=\""+toolTipForeground+"\">anonymous X</font></td></tr>" +
"<tr><td style='padding: 0px 16px 0px 4px;color: "+greyed+"'>Provided:</td>" +
"<td style='padding: 0px 4px 0px 0px;'><font color=\""+errorTipForeground+"\">Y</font></td></tr>" +
"</table>" +
"</html>";
@@ -132,6 +135,8 @@ public class LightAdvLVTIHighlightingTest extends LightDaemonAnalyzerTestCase {
.filter(info -> info.type == HighlightInfoType.ERROR)
.forEach(info -> Assert.assertEquals(expected, info.getToolTip()));
}
public void testLeftSideNotAnonymous() { doTest(); }
@Override
protected Sdk getProjectJDK() {