[java-highlighting] checkConstructorCall migrated; more proper HTML in tooltips

Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: bc39e165c886c5c0c131a3e6770f3a688f00e798
This commit is contained in:
Tagir Valeev
2025-01-23 12:39:11 +00:00
committed by intellij-monorepo-bot
parent ccc61915e9
commit b0614fa46d
19 changed files with 318 additions and 625 deletions
@@ -164,12 +164,8 @@ constructor.ambiguous.implicit.call=Ambiguous implicit constructor call: both ''
constructor.no.default=There is no parameterless constructor available in ''{0}''
type.incompatible=Incompatible types. Found: ''{1}'', required: ''{0}''
# {0} - left raw type, {1} - required type arguments row, {2} - right raw type, {3} - found type arguments row, {4} - reason, {5} - greyed title color
type.incompatible.html.tooltip=\
<html><body><table>\
<tr><td style="padding: 0px 16px 8px 4px;" class="{5}">Required type:</td><td style="padding: 0px 4px 8px 0px;">{0}</td>{1}</tr>\
<tr><td style="padding: 0px 16px 0px 4px;" class="{5}">Provided:</td><td style="padding: 0px 4px 0px 0px;">{2}</td>{3}</tr>\
</table>{4}</body></html>
type.incompatible.tooltip.required.type=Required type:
type.incompatible.tooltip.provided.type=Provided:
type.incompatible.reason.ambiguous.method.reference=<br/>reason: method reference is ambiguous: both ''{0}'' and ''{1}'' match
type.incompatible.reason.inference=<br/>reason: {0}
type.void.not.allowed='void' type is not allowed here
@@ -187,6 +183,9 @@ new.expression.diamond.not.allowed=Diamond operator is not allowed here
new.expression.diamond.not.applicable=Diamond operator is not applicable for non-parameterized types
new.expression.diamond.inference.failure={0}
new.expression.diamond.anonymous.inner.non.private=Cannot use '<>' due to non-private method which doesn't override or implement a method from a supertype
new.expression.anonymous.implements.interface.with.type.arguments=Anonymous class implements interface; cannot have type arguments
new.expression.arguments.to.default.constructor.call=Default constructor is invoked with arguments
new.expression.unresolved.constructor=Cannot resolve constructor ''{0}''
reference.type.argument.static.class=Type arguments are not allowed here because class ''{0}'' is static
reference.type.needs.type.arguments=Improperly formed type: ''{0}'' needs type arguments because its qualifier has type arguments
@@ -239,8 +238,6 @@ call.formal.varargs.element.type.inaccessible.here=Formal varargs element type {
call.type.inference.error={0}
call.wrong.arguments=''{0}'' in ''{1}'' cannot be applied to ''{2}''
call.wrong.arguments.count.mismatch=Expected {0, choice, 0#no arguments|1#1 argument|1<{0} arguments} but found {1}
call.type.mismatch.tooltip.required.type=Required type
call.type.mismatch.tooltip.provided.type=Provided
array.illegal.initializer=Illegal initializer for ''{0}''
array.initializer.not.allowed=Array initializer is not allowed here
@@ -6,6 +6,7 @@ import com.intellij.core.JavaPsiBundle;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext;
import com.intellij.java.codeserver.highlighting.errors.JavaMismatchedCallContext;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.util.Pair;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
@@ -15,6 +16,7 @@ import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -466,16 +468,17 @@ final class ExpressionChecker {
}
PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
checkConstructorCall(typeResult, expression, classType, classReference, expression.getArgumentList());
checkConstructorCall(typeResult, expression, classType, classReference);
}
void checkAmbiguousConstructorCall(@NotNull PsiJavaCodeReferenceElement ref, PsiElement resolved) {
if (resolved instanceof PsiClass psiClass &&
ref.getParent() instanceof PsiNewExpression newExpression && psiClass.getConstructors().length > 0) {
if (newExpression.resolveMethod() == null && !PsiTreeUtil.findChildrenOfType(newExpression.getArgumentList(), PsiFunctionalExpression.class).isEmpty()) {
PsiExpressionList argumentList = newExpression.getArgumentList();
if (newExpression.resolveMethod() == null && !PsiTreeUtil.findChildrenOfType(argumentList, PsiFunctionalExpression.class).isEmpty()) {
PsiType type = newExpression.getType();
if (type instanceof PsiClassType classType) {
checkConstructorCall(classType.resolveGenerics(), newExpression, type, newExpression.getClassReference(), ref);
checkConstructorCall(classType.resolveGenerics(), newExpression, type, newExpression.getClassReference());
}
}
}
@@ -614,11 +617,9 @@ final class ExpressionChecker {
}
void checkConstructorCall(@NotNull PsiClassType.ClassResolveResult typeResolveResult,
@NotNull PsiConstructorCall constructorCall,
@NotNull PsiType type,
@Nullable PsiJavaCodeReferenceElement classReference,
@Nullable PsiElement elementToHighlight) {
if (elementToHighlight == null) return;
@NotNull PsiConstructorCall constructorCall,
@NotNull PsiType type,
@Nullable PsiJavaCodeReferenceElement classReference) {
PsiExpressionList list = constructorCall.getArgumentList();
if (list == null) return;
PsiClass aClass = typeResolveResult.getElement();
@@ -634,5 +635,101 @@ final class ExpressionChecker {
if (classReference != null && !resolveHelper.isAccessible(aClass, constructorCall, accessObjectClass)) {
myVisitor.myModifierChecker.reportAccessProblem(classReference, aClass, typeResolveResult);
}
PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length == 0) {
if (!list.isEmpty()) {
myVisitor.report(JavaErrorKinds.NEW_EXPRESSION_ARGUMENTS_TO_DEFAULT_CONSTRUCTOR_CALL.create(constructorCall));
}
else if (classReference != null && aClass.hasModifierProperty(PsiModifier.PROTECTED) &&
callingProtectedConstructorFromDerivedClass(constructorCall, aClass)) {
myVisitor.myModifierChecker.reportAccessProblem(classReference, aClass, typeResolveResult);
}
else if (aClass.isInterface() && constructorCall instanceof PsiNewExpression newExpression) {
PsiReferenceParameterList typeArgumentList = newExpression.getTypeArgumentList();
if (typeArgumentList.getTypeArguments().length > 0) {
myVisitor.report(JavaErrorKinds.NEW_EXPRESSION_ANONYMOUS_IMPLEMENTS_INTERFACE_WITH_TYPE_ARGUMENTS.create(typeArgumentList));
}
}
return;
}
PsiElement place = list;
if (constructorCall instanceof PsiNewExpression newExpression) {
PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass();
if (anonymousClass != null) place = anonymousClass;
}
JavaResolveResult[] results = resolveHelper.multiResolveConstructor((PsiClassType)type, list, place);
MethodCandidateInfo result = null;
if (results.length == 1) result = (MethodCandidateInfo)results[0];
PsiMethod constructor = result == null ? null : result.getElement();
boolean applicable = true;
try {
PsiDiamondType diamondType =
constructorCall instanceof PsiNewExpression newExpression ? PsiDiamondType.getDiamondType(newExpression) : null;
JavaResolveResult staticFactory = diamondType != null ? diamondType.getStaticFactory() : null;
if (staticFactory instanceof MethodCandidateInfo info) {
if (info.isApplicable()) {
result = info;
if (constructor == null) {
constructor = info.getElement();
}
}
else {
applicable = false;
}
}
else {
applicable = result != null && result.isApplicable();
}
}
catch (IndexNotReadyException ignored) {
}
if (constructor == null) {
if (IncompleteModelUtil.isIncompleteModel(list) &&
ContainerUtil.exists(results, r -> r instanceof MethodCandidateInfo info && info.isPotentiallyCompatible() == ThreeState.YES) &&
ContainerUtil.exists(list.getExpressions(), e -> IncompleteModelUtil.mayHaveUnknownTypeDueToPendingReference(e))) {
return;
}
myVisitor.report(JavaErrorKinds.NEW_EXPRESSION_UNRESOLVED_CONSTRUCTOR.create(
constructorCall, new JavaErrorKinds.UnresolvedConstructorContext(aClass, results)));
return;
}
if (classReference != null &&
(!result.isAccessible() ||
constructor.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass))) {
myVisitor.myModifierChecker.reportAccessProblem(classReference, constructor, result);
return;
}
if (!applicable) {
checkIncompatibleCall(list, result);
if (myVisitor.hasErrorResults()) return;
}
else if (constructorCall instanceof PsiNewExpression newExpression) {
PsiReferenceParameterList typeArgumentList = newExpression.getTypeArgumentList();
myVisitor.myGenericsChecker.checkReferenceTypeArgumentList(constructor, typeArgumentList, result.getSubstitutor());
if (myVisitor.hasErrorResults()) return;
}
checkVarargParameterErasureToBeAccessible(result, constructorCall);
if (myVisitor.hasErrorResults()) return;
checkIncompatibleType(constructorCall, result, constructorCall);
}
private static boolean callingProtectedConstructorFromDerivedClass(@NotNull PsiConstructorCall place,
@NotNull PsiClass constructorClass) {
// indirect instantiation via anonymous class is ok
if (place instanceof PsiNewExpression newExpression && newExpression.getAnonymousClass() != null) return false;
PsiElement curElement = place;
PsiClass containingClass = constructorClass.getContainingClass();
while (true) {
PsiClass aClass = PsiTreeUtil.getParentOfType(curElement, PsiClass.class);
if (aClass == null) return false;
curElement = aClass;
if ((aClass.isInheritor(constructorClass, true) || containingClass != null && aClass.isInheritor(containingClass, true))
&& !JavaPsiFacade.getInstance(aClass.getProject()).arePackagesTheSame(aClass, constructorClass)) {
return true;
}
}
}
}
@@ -176,7 +176,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) {
PsiClass containingClass = Objects.requireNonNull(enumConstant.getContainingClass());
PsiClassType type = JavaPsiFacade.getElementFactory(myProject).createType(containingClass);
myExpressionChecker.checkConstructorCall(type.resolveGenerics(), enumConstant, type, null, enumConstant.getArgumentList());
myExpressionChecker.checkConstructorCall(type.resolveGenerics(), enumConstant, type, null);
}
}
@@ -181,6 +181,7 @@ final class ModifierChecker {
void reportAccessProblem(@NotNull PsiJavaCodeReferenceElement ref,
@NotNull PsiModifierListOwner resolved,
@NotNull JavaResolveResult result) {
result = withElement(result, resolved);
if (resolved.hasModifierProperty(PsiModifier.PRIVATE)) {
myVisitor.report(JavaErrorKinds.ACCESS_PRIVATE.create(ref, result));
return;
@@ -193,7 +194,7 @@ final class ModifierChecker {
PsiClass packageLocalClass = JavaPsiModifierUtil.getPackageLocalClassInTheMiddle(ref);
if (packageLocalClass != null) {
result = getDelegate(result, packageLocalClass);
result = withElement(result, packageLocalClass);
}
if (resolved.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || packageLocalClass != null) {
@@ -210,12 +211,12 @@ final class ModifierChecker {
// TODO: JPMS
}
private static @NotNull JavaResolveResult getDelegate(@NotNull JavaResolveResult original, @NotNull PsiClass newClass) {
return new PsiClassType.ClassResolveResult() {
private static @NotNull JavaResolveResult withElement(@NotNull JavaResolveResult original, @NotNull PsiElement newElement) {
if (newElement == original.getElement()) return original;
return new JavaResolveResult() {
@Override
public PsiClass getElement() {
return newClass;
public PsiElement getElement() {
return newElement;
}
@Override
@@ -225,27 +226,27 @@ final class ModifierChecker {
@Override
public boolean isPackagePrefixPackageReference() {
return false;
return original.isPackagePrefixPackageReference();
}
@Override
public boolean isAccessible() {
return false;
return original.isAccessible();
}
@Override
public boolean isStaticsScopeCorrect() {
return true;
return original.isStaticsScopeCorrect();
}
@Override
public PsiElement getCurrentFileResolveScope() {
return null;
return original.getCurrentFileResolveScope();
}
@Override
public boolean isValidResult() {
return true;
return original.isValidResult();
}
};
}
@@ -67,7 +67,8 @@ final class JavaErrorFormatUtil {
return symbolName == null ? "?" : symbolName;
}
static @NotNull String formatArgumentTypes(@NotNull PsiExpressionList list, boolean shortNames) {
static @NotNull String formatArgumentTypes(@Nullable PsiExpressionList list, boolean shortNames) {
if (list == null) return "";
StringBuilder builder = new StringBuilder();
builder.append("(");
PsiExpression[] args = list.getExpressions();
@@ -418,8 +418,6 @@ public final class JavaErrorKinds {
.withRawDescription((list, owner) -> message("type.parameter.count.mismatch", list.getTypeArgumentCount(),
owner.getTypeParameters().length));
public static final Simple<PsiTypeElement> TYPE_PARAMETER_ACTUAL_INFERRED_MISMATCH = error("type.parameter.actual.inferred.mismatch");
public static final Simple<PsiReferenceParameterList> NEW_EXPRESSION_DIAMOND_NOT_APPLICABLE =
error("new.expression.diamond.not.applicable");
public static final Simple<PsiMethod> METHOD_DUPLICATE =
error(PsiMethod.class, "method.duplicate")
@@ -592,6 +590,8 @@ public final class JavaErrorKinds {
public static final Simple<PsiReferenceExpression> EXPRESSION_EXPECTED = error("expression.expected");
public static final Simple<PsiReferenceParameterList> NEW_EXPRESSION_DIAMOND_NOT_APPLICABLE =
error("new.expression.diamond.not.applicable");
public static final Simple<PsiNewExpression> NEW_EXPRESSION_QUALIFIED_MALFORMED =
error("new.expression.qualified.malformed");
public static final Parameterized<PsiNewExpression, PsiClass> NEW_EXPRESSION_QUALIFIED_STATIC_CLASS =
@@ -604,11 +604,21 @@ public final class JavaErrorKinds {
error("new.expression.diamond.not.allowed");
public static final Simple<PsiReferenceParameterList> NEW_EXPRESSION_DIAMOND_ANONYMOUS_INNER_NON_PRIVATE =
error("new.expression.diamond.anonymous.inner.non.private");
public static final Simple<PsiReferenceParameterList> NEW_EXPRESSION_ANONYMOUS_IMPLEMENTS_INTERFACE_WITH_TYPE_ARGUMENTS =
error("new.expression.anonymous.implements.interface.with.type.arguments");
public static final Parameterized<PsiReferenceParameterList, PsiDiamondType.DiamondInferenceResult>
NEW_EXPRESSION_DIAMOND_INFERENCE_FAILURE =
parameterized(PsiReferenceParameterList.class, PsiDiamondType.DiamondInferenceResult.class, "new.expression.diamond.inference.failure")
.withRawDescription(
(list, inferenceResult) -> message("new.expression.diamond.inference.failure", inferenceResult.getErrorMessage()));
public static final Simple<PsiConstructorCall> NEW_EXPRESSION_ARGUMENTS_TO_DEFAULT_CONSTRUCTOR_CALL =
error(PsiConstructorCall.class, "new.expression.arguments.to.default.constructor.call")
.withAnchor(call -> call.getArgumentList());
public static final Parameterized<PsiConstructorCall, UnresolvedConstructorContext> NEW_EXPRESSION_UNRESOLVED_CONSTRUCTOR =
parameterized(PsiConstructorCall.class, UnresolvedConstructorContext.class, "new.expression.unresolved.constructor")
.withAnchor((call, ctx) -> call.getArgumentList())
.withRawDescription((call, ctx) -> message("new.expression.unresolved.constructor",
ctx.psiClass().getName() + formatArgumentTypes(call.getArgumentList(), true)));
public static final Parameterized<PsiReferenceParameterList, PsiClass> REFERENCE_TYPE_ARGUMENT_STATIC_CLASS =
parameterized(PsiReferenceParameterList.class, PsiClass.class, "reference.type.argument.static.class")
@@ -832,4 +842,8 @@ public final class JavaErrorKinds {
@NotNull PsiType actualType) {
}
public record UnresolvedConstructorContext(@NotNull PsiClass psiClass, @NotNull JavaResolveResult @NotNull [] results) {
}
}
@@ -14,6 +14,8 @@ import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.openapi.util.text.HtmlChunk.*;
public record JavaIncompatibleTypeErrorContext(@NotNull PsiType lType, @Nullable PsiType rType,
@Nullable @Nls String reasonForIncompatibleTypes) {
private static final @NlsSafe String ANONYMOUS = "anonymous ";
@@ -44,23 +46,48 @@ public record JavaIncompatibleTypeErrorContext(@NotNull PsiType lType, @Nullable
}
private @NotNull HtmlChunk createTooltip(@NotNull @Nls String reason) {
String styledReason = reason.isEmpty() ? "" :
String.format("<table><tr><td style=''padding-top: 10px; padding-left: 4px;''>%s</td></tr></table>", reason);
HtmlChunk styledReason = reason.isEmpty() ? empty() :
tag("table").child(
tag("tr").child(
tag("td").style("padding-top: 10px; padding-left: 4px;").addRaw(reason)));
IncompatibleTypesTooltipComposer tooltipComposer = (lTypeString, lTypeArguments, rTypeString, rTypeArguments) ->
HtmlChunk.raw(JavaCompilationErrorBundle.message("type.incompatible.html.tooltip",
lTypeString, lTypeArguments,
rTypeString, rTypeArguments,
styledReason, JavaCompilationError.JAVA_DISPLAY_GRAYED));
createRequiredProvidedTypeMessage(lTypeString, lTypeArguments, rTypeString, rTypeArguments, styledReason);
return createIncompatibleTypesTooltip(tooltipComposer);
}
@NotNull HtmlChunk createDescription() {
PsiType baseLType = PsiUtil.convertAnonymousToBaseType(lType);
PsiType baseRType = rType == null ? null : PsiUtil.convertAnonymousToBaseType(rType);
boolean leftAnonymous = PsiUtil.resolveClassInClassTypeOnly(lType) instanceof PsiAnonymousClass;
String lTypeString = JavaErrorFormatUtil.formatType(leftAnonymous ? lType : baseLType);
String rTypeString = JavaErrorFormatUtil.formatType(leftAnonymous ? rType : baseRType);
return HtmlChunk.raw(JavaCompilationErrorBundle.message("type.incompatible", lTypeString, rTypeString));
return raw(JavaCompilationErrorBundle.message("type.incompatible", lTypeString, rTypeString));
}
static @NotNull HtmlChunk createRequiredProvidedTypeMessage(@NotNull HtmlChunk lType,
@Nls @NotNull String lTypeArguments,
@NotNull HtmlChunk rType,
@Nls @NotNull String rTypeArguments,
@NotNull HtmlChunk styledReason) {
return html().child(
body().children(
tag("table").children(
tag("tr").children(
tag("td").style("padding: 0px 16px 8px 4px;").setClass(JavaCompilationError.JAVA_DISPLAY_GRAYED)
.addText(JavaCompilationErrorBundle.message("type.incompatible.tooltip.required.type")),
tag("td").style("padding: 0px 4px 8px 0px;").child(lType),
raw(lTypeArguments)
),
tag("tr").children(
tag("td").style("padding: 0px 16px 0px 4px;").setClass(JavaCompilationError.JAVA_DISPLAY_GRAYED)
.addText(JavaCompilationErrorBundle.message("type.incompatible.tooltip.provided.type")),
tag("td").style("padding: 0px 4px 0px 0px;").child(rType),
raw(rTypeArguments)
)
),
styledReason
)
);
}
@NotNull
@@ -109,9 +136,9 @@ public record JavaIncompatibleTypeErrorContext(@NotNull PsiType lType, @Nullable
PsiType rRawType = rType instanceof PsiClassType classType ? classType.rawType() : rType;
boolean assignable = rRawType == null || TypeConversionUtil.isAssignable(lRawType, rRawType);
boolean shortType = showShortType(lRawType, rRawType);
return consumer.consume(redIfNotMatch(lRawType, true, shortType).toString(),
return consumer.consume(redIfNotMatch(lRawType, true, shortType),
requiredRow.toString(),
redIfNotMatch(rRawType, assignable, shortType).toString(),
redIfNotMatch(rRawType, assignable, shortType),
foundRow.toString());
}
@@ -132,7 +159,7 @@ public record JavaIncompatibleTypeErrorContext(@NotNull PsiType lType, @Nullable
}
static @NotNull @NlsSafe HtmlChunk redIfNotMatch(@Nullable PsiType type, boolean matches, boolean shortType) {
if (type == null) return HtmlChunk.empty();
if (type == null) return empty();
String typeText;
if (shortType || type instanceof PsiCapturedWildcardType) {
typeText = PsiUtil.resolveClassInClassTypeOnly(type) instanceof PsiAnonymousClass
@@ -142,7 +169,7 @@ public record JavaIncompatibleTypeErrorContext(@NotNull PsiType lType, @Nullable
else {
typeText = type.getCanonicalText();
}
return HtmlChunk.span()
return span()
.setClass(matches ? JavaCompilationError.JAVA_DISPLAY_INFORMATION : JavaCompilationError.JAVA_DISPLAY_ERROR).addText(typeText);
}
@@ -159,9 +186,9 @@ public record JavaIncompatibleTypeErrorContext(@NotNull PsiType lType, @Nullable
interface IncompatibleTypesTooltipComposer {
@NotNull
@NlsContexts.Tooltip
HtmlChunk consume(@NotNull @NlsSafe String lRawType,
HtmlChunk consume(@NotNull HtmlChunk lRawType,
@NotNull @NlsSafe String lTypeArguments,
@NotNull @NlsSafe String rRawType,
@NotNull HtmlChunk rRawType,
@NotNull @NlsSafe String rTypeArguments);
/**
@@ -3,7 +3,6 @@ package com.intellij.java.codeserver.highlighting.errors;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil;
import com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.text.HtmlBuilder;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.psi.*;
@@ -82,6 +81,23 @@ public record JavaMismatchedCallContext(@NotNull PsiExpressionList list,
return message.wrapWithHtmlBody();
}
private @NotNull HtmlChunk createOneArgMismatchTooltip(PsiExpression @NotNull [] expressions, PsiParameter @NotNull [] parameters) {
PsiExpression wrongArg = mismatchedExpressions.get(0);
PsiType argType = wrongArg != null ? wrongArg.getType() : null;
if (argType != null) {
int idx = ArrayUtil.find(expressions, wrongArg);
if (idx > parameters.length - 1 && !parameters[parameters.length - 1].isVarArgs()) return HtmlChunk.empty();
PsiType paramType =
candidate.getSubstitutor().substitute(PsiTypesUtil.getParameterType(parameters, idx, candidate.isVarargs()));
String errorMessage = candidate.getInferenceErrorMessage();
HtmlChunk reason = getTypeMismatchErrorHtml(errorMessage);
return new JavaIncompatibleTypeErrorContext(paramType, argType).createIncompatibleTypesTooltip(
(lRawType, lTypeArguments, rRawType, rTypeArguments) ->
createRequiredProvidedTypeMessage(lRawType, lTypeArguments, rRawType, rTypeArguments, reason));
}
return HtmlChunk.empty();
}
private static @NotNull HtmlChunk getTypeMismatchTable(@Nullable MethodCandidateInfo info,
@NotNull PsiSubstitutor substitutor,
PsiParameter @NotNull [] parameters,
@@ -90,10 +106,10 @@ public record JavaMismatchedCallContext(@NotNull PsiExpressionList list,
HtmlChunk.Element td = HtmlChunk.tag("td");
HtmlChunk requiredHeader = td.style("padding-left: 16px; padding-right: 24px;")
.setClass(JavaCompilationError.JAVA_DISPLAY_GRAYED)
.addText(JavaCompilationErrorBundle.message("call.type.mismatch.tooltip.required.type"));
.addText(JavaCompilationErrorBundle.message("type.incompatible.tooltip.required.type"));
HtmlChunk providedHeader = td.style("padding-right: 28px;")
.setClass(JavaCompilationError.JAVA_DISPLAY_GRAYED)
.addText(JavaCompilationErrorBundle.message("call.type.mismatch.tooltip.provided.type"));
.addText(JavaCompilationErrorBundle.message("type.incompatible.tooltip.provided.type"));
table.append(HtmlChunk.tag("tr").children(td, requiredHeader, providedHeader));
String parameterNameStyle = "padding:1px 4px 1px 4px;";
@@ -132,16 +148,16 @@ public record JavaMismatchedCallContext(@NotNull PsiExpressionList list,
}
return table.wrapWith("table");
}
private static @NotNull HtmlChunk mismatchedExpressionType(PsiType parameterType, @NotNull PsiExpression expression) {
return new JavaIncompatibleTypeErrorContext(parameterType, expression.getType()).createIncompatibleTypesTooltip(
new IncompatibleTypesTooltipComposer() {
@Override
public @NotNull HtmlChunk consume(@NotNull @NlsSafe String lRawType,
@NotNull @NlsSafe String lTypeArguments,
@NotNull @NlsSafe String rRawType,
@NotNull @NlsSafe String rTypeArguments) {
return new HtmlBuilder().appendRaw(rRawType).appendRaw(rTypeArguments).toFragment();
@Override
public @NotNull HtmlChunk consume(@NotNull HtmlChunk lRawType,
@NotNull String lTypeArguments,
@NotNull HtmlChunk rRawType,
@NotNull String rTypeArguments) {
return new HtmlBuilder().append(rRawType).appendRaw(rTypeArguments).toFragment();
}
@Override
@@ -150,25 +166,6 @@ public record JavaMismatchedCallContext(@NotNull PsiExpressionList list,
}
});
}
private @NotNull HtmlChunk createOneArgMismatchTooltip(PsiExpression @NotNull [] expressions, PsiParameter @NotNull [] parameters) {
PsiExpression wrongArg = mismatchedExpressions.get(0);
PsiType argType = wrongArg != null ? wrongArg.getType() : null;
if (argType != null) {
int idx = ArrayUtil.find(expressions, wrongArg);
if (idx > parameters.length - 1 && !parameters[parameters.length - 1].isVarArgs()) return HtmlChunk.empty();
PsiType paramType =
candidate.getSubstitutor().substitute(PsiTypesUtil.getParameterType(parameters, idx, candidate.isVarargs()));
String errorMessage = candidate.getInferenceErrorMessage();
HtmlChunk reason = getTypeMismatchErrorHtml(errorMessage);
return new JavaIncompatibleTypeErrorContext(paramType, argType).createIncompatibleTypesTooltip(
(lRawType, lTypeArguments, rRawType, rTypeArguments) ->
HtmlChunk.raw(JavaCompilationErrorBundle.message("type.incompatible.html.tooltip",
lRawType, lTypeArguments, rRawType, rTypeArguments, reason,
JavaCompilationError.JAVA_DISPLAY_GRAYED)));
}
return HtmlChunk.empty();
}
private static @NotNull HtmlChunk createMismatchedArgumentCountTooltip(int expected, int actual) {
return HtmlChunk.text(JavaCompilationErrorBundle.message("call.wrong.arguments.count.mismatch", expected, actual))
@@ -642,21 +642,16 @@ public final class HighlightFixUtil {
static void registerFixesOnInvalidConstructorCall(@NotNull Consumer<? super CommonIntentionAction> info,
@NotNull PsiConstructorCall constructorCall,
@Nullable PsiJavaCodeReferenceElement classReference,
@NotNull PsiExpressionList list,
@NotNull PsiClass aClass,
PsiMethod @NotNull [] constructors,
JavaResolveResult @NotNull [] results) {
if (classReference != null) {
ConstructorParametersFixer.registerFixActions(classReference, constructorCall, info);
ChangeTypeArgumentsFix.registerIntentions(results, list, info, aClass);
}
else if (aClass.isEnum()) {
ConstructorParametersFixer.registerFixActions(aClass, PsiSubstitutor.EMPTY, constructorCall, info);
}
ConstructorParametersFixer.registerFixActions(constructorCall, info);
ChangeTypeArgumentsFix.registerIntentions(results, constructorCall, info, aClass);
PsiMethod[] constructors = aClass.getConstructors();
ChangeStringLiteralToCharInMethodCallFix.registerFixes(constructors, constructorCall, info);
IntentionAction action = QuickFixFactory.getInstance().createSurroundWithArrayFix(constructorCall, null);
info.accept(action);
PsiExpressionList list = constructorCall.getArgumentList();
if (list == null) return;
if (!PermuteArgumentsFix.registerFix(info, constructorCall, toMethodCandidates(results))) {
registerChangeMethodSignatureFromUsageIntentions(results, list, info);
}
@@ -12,15 +12,10 @@ import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixUpdater;
import com.intellij.codeInspection.LocalQuickFixOnPsiElementAsIntentionAdapter;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
import com.intellij.openapi.editor.colors.EditorColorsUtil;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.HtmlBuilder;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.JavaFeature;
@@ -33,11 +28,10 @@ import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.ui.ColorUtil;
import com.intellij.util.*;
import com.intellij.util.JavaPsiConstructorUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.StartupUiUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.util.XmlStringUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.intellij.lang.annotations.Language;
@@ -46,10 +40,8 @@ import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.text.MessageFormat;
import java.util.*;
import java.util.List;
import java.util.function.Consumer;
import static com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil.asConsumer;
@@ -189,90 +181,6 @@ public final class HighlightMethodUtil {
return errorResult;
}
/**
* collect highlightInfos per each wrong argument; fixes would be set for the first one with fixRange: methodCall
* @return highlight info for the first wrong arg expression
*/
private static List<HighlightInfo.Builder> createIncompatibleCallHighlightInfo(@NotNull PsiExpressionList list,
@NotNull MethodCandidateInfo candidateInfo) {
if (PsiTreeUtil.hasErrorElements(list)) return null;
PsiMethod resolvedMethod = candidateInfo.getElement();
PsiSubstitutor substitutor = candidateInfo.getSubstitutor();
String methodName = HighlightMessageUtil.getSymbolName(resolvedMethod, substitutor);
PsiClass parent = resolvedMethod.getContainingClass();
String containerName = parent == null ? "" : HighlightMessageUtil.getSymbolName(parent, substitutor);
String argTypes = buildArgTypesList(list, false);
String description = JavaErrorBundle.message("wrong.method.arguments", methodName, containerName, argTypes);
String toolTip = null;
List<PsiExpression> mismatchedExpressions;
if (parent != null) {
PsiExpression[] expressions = list.getExpressions();
PsiParameter[] parameters = resolvedMethod.getParameterList().getParameters();
mismatchedExpressions = mismatchedArgs(expressions, substitutor, parameters, candidateInfo.isVarargs());
if (mismatchedExpressions.size() == 1 && parameters.length > 0) {
toolTip = createOneArgMismatchTooltip(candidateInfo, mismatchedExpressions, expressions, parameters);
}
if (toolTip == null) {
if ((parameters.length == 0 || !parameters[parameters.length - 1].isVarArgs()) &&
parameters.length != expressions.length) {
toolTip = createMismatchedArgumentCountTooltip(parameters.length, expressions.length);
description = JavaAnalysisBundle.message("arguments.count.mismatch", parameters.length, expressions.length);
}
else if (mismatchedExpressions.isEmpty()) {
if (IncompleteModelUtil.isIncompleteModel(list)) return null;
toolTip = XmlStringUtil.escapeString(description);
}
else {
toolTip = createMismatchedArgumentsHtmlTooltip(candidateInfo, list);
}
}
}
else {
mismatchedExpressions = Collections.emptyList();
toolTip = XmlStringUtil.escapeString(description);
}
if (mismatchedExpressions.size() == list.getExpressions().length || mismatchedExpressions.isEmpty()) {
if (list.getTextRange().isEmpty()) {
return List.of(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(ObjectUtils.notNull(list.getPrevSibling(), list))
.description(description)
.escapedToolTip(toolTip));
}
return List.of(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).description(description).escapedToolTip(toolTip).navigationShift(1));
}
else {
List<HighlightInfo.Builder> infos = new ArrayList<>();
for (PsiExpression wrongArg : mismatchedExpressions) {
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(wrongArg)
.description(description)
.escapedToolTip(toolTip);
infos.add(info);
}
return infos;
}
}
private static @NlsContexts.Tooltip String createOneArgMismatchTooltip(@NotNull MethodCandidateInfo candidateInfo,
@NotNull List<? extends PsiExpression> mismatchedExpressions,
PsiExpression @NotNull [] expressions,
PsiParameter @NotNull [] parameters) {
PsiExpression wrongArg = mismatchedExpressions.get(0);
PsiType argType = wrongArg != null ? wrongArg.getType() : null;
if (argType != null) {
int idx = ArrayUtil.find(expressions, wrongArg);
if (idx > parameters.length - 1 && !parameters[parameters.length - 1].isVarArgs()) return null;
PsiType paramType = candidateInfo.getSubstitutor().substitute(PsiTypesUtil.getParameterType(parameters, idx, candidateInfo.isVarargs()));
String errorMessage = candidateInfo.getInferenceErrorMessage();
HtmlChunk reason = getTypeMismatchErrorHtml(errorMessage);
return HighlightUtil.createIncompatibleTypesTooltip(
paramType, argType, (lRawType, lTypeArguments, rRawType, rTypeArguments) ->
JavaErrorBundle.message("incompatible.types.html.tooltip",
lRawType, lTypeArguments, rRawType, rTypeArguments, reason, ColorUtil.toHtmlColor(UIUtil.getContextHelpForeground())));
}
return null;
}
static HighlightInfo.Builder createIncompatibleTypeHighlightInfo(@NotNull PsiCall methodCall,
@NotNull MethodCandidateInfo resolveResult,
@NotNull PsiElement elementToHighlight) {
@@ -380,20 +288,6 @@ public final class HighlightMethodUtil {
return JavaErrorBundle.message("static.interface.method.call.qualifier");
}
private static @NotNull List<PsiExpression> mismatchedArgs(PsiExpression @NotNull [] expressions,
PsiSubstitutor substitutor,
PsiParameter @NotNull [] parameters,
boolean varargs) {
List<PsiExpression> result = new ArrayList<>();
for (int i = 0; i < Math.max(parameters.length, expressions.length); i++) {
if (parameters.length == 0 || !assignmentCompatible(i, parameters, expressions, substitutor, varargs)) {
result.add(i < expressions.length ? expressions[i] : null);
}
}
return result;
}
static boolean isDummyConstructorCall(@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiResolveHelper resolveHelper,
@NotNull PsiExpressionList list,
@@ -641,132 +535,6 @@ public final class HighlightMethodUtil {
return ms.toString();
}
private static @NotNull @NlsContexts.Tooltip String createMismatchedArgumentsHtmlTooltip(@NotNull MethodCandidateInfo info, @NotNull PsiExpressionList list) {
PsiMethod method = info.getElement();
PsiSubstitutor substitutor = info.getSubstitutor();
PsiParameter[] parameters = method.getParameterList().getParameters();
return createMismatchedArgumentsHtmlTooltip(list, info, parameters, substitutor);
}
@Language("HTML")
private static @NotNull @NlsContexts.Tooltip String createMismatchedArgumentsHtmlTooltip(@NotNull PsiExpressionList list,
@Nullable MethodCandidateInfo info,
PsiParameter @NotNull [] parameters,
@NotNull PsiSubstitutor substitutor) {
PsiExpression[] expressions = list.getExpressions();
if ((parameters.length == 0 || !parameters[parameters.length - 1].isVarArgs()) &&
parameters.length != expressions.length) {
return createMismatchedArgumentCountTooltip(parameters.length, expressions.length);
}
HtmlBuilder message = new HtmlBuilder();
message.append(getTypeMismatchTable(info, substitutor, parameters, expressions));
String errorMessage = info != null ? info.getInferenceErrorMessage() : null;
message.append(getTypeMismatchErrorHtml(errorMessage));
return message.wrapWithHtmlBody().toString();
}
private static @NotNull @NlsContexts.Tooltip String createMismatchedArgumentCountTooltip(int expected, int actual) {
return HtmlChunk.text(JavaAnalysisBundle.message("arguments.count.mismatch", expected, actual)).wrapWith("html").toString();
}
private static @NotNull HtmlChunk getTypeMismatchErrorHtml(@Nls String errorMessage) {
if (errorMessage == null) {
return HtmlChunk.empty();
}
return HtmlChunk.tag("td").style("padding-left: 4px; padding-top: 10;")
.addText(JavaAnalysisBundle.message("type.mismatch.reason", errorMessage))
.wrapWith("tr").wrapWith("table");
}
private static @NotNull HtmlChunk getTypeMismatchTable(@Nullable MethodCandidateInfo info,
@NotNull PsiSubstitutor substitutor,
PsiParameter @NotNull [] parameters,
PsiExpression[] expressions) {
String greyedColor = ColorUtil.toHtmlColor(UIUtil.getContextHelpForeground());
HtmlBuilder table = new HtmlBuilder();
HtmlChunk.Element td = HtmlChunk.tag("td");
HtmlChunk requiredHeader = td.style("color: " + greyedColor + "; padding-left: 16px; padding-right: 24px;")
.addText(JavaAnalysisBundle.message("required.type"));
HtmlChunk providedHeader = td.style("color: " + greyedColor + "; padding-right: 28px;")
.addText(JavaAnalysisBundle.message("provided.type"));
table.append(HtmlChunk.tag("tr").children(td, requiredHeader, providedHeader));
String parameterNameStyle = String.format("color: %s; font-size:%dpt; padding:1px 4px 1px 4px;",
greyedColor,
StartupUiUtil.getLabelFont().getSize() - (SystemInfo.isWindows ? 0 : 1));
Color paramBgColor = EditorColorsUtil.getGlobalOrDefaultColorScheme()
.getAttributes(DefaultLanguageHighlighterColors.INLINE_PARAMETER_HINT)
.getBackgroundColor();
if (paramBgColor != null) {
parameterNameStyle += "background-color: " + ColorUtil.toHtmlColor(paramBgColor) + ";";
}
boolean varargAdded = false;
for (int i = 0; i < Math.max(parameters.length, expressions.length); i++) {
boolean varargs = info != null && info.isVarargs();
if (assignmentCompatible(i, parameters, expressions, substitutor, varargs)) continue;
PsiParameter parameter = null;
if (i < parameters.length) {
parameter = parameters[i];
varargAdded = parameter.isVarArgs();
}
else if (!varargAdded) {
parameter = parameters[parameters.length - 1];
varargAdded = true;
}
PsiType parameterType = substitutor.substitute(PsiTypesUtil.getParameterType(parameters, i, varargs));
PsiExpression expression = i < expressions.length ? expressions[i] : null;
boolean showShortType = HighlightUtil.showShortType(parameterType,
expression != null ? expression.getType() : null);
HtmlChunk.Element nameCell = td;
HtmlChunk.Element typeCell = td.style("padding-left: 16px; padding-right: 24px;");
if (parameter != null) {
nameCell = nameCell.child(td.style(parameterNameStyle).addText(parameter.getName() + ":")
.wrapWith("tr").wrapWith("table"));
typeCell = typeCell.child(HighlightUtil.redIfNotMatch(substitutor.substitute(parameter.getType()), true, showShortType));
}
HtmlChunk.Element mismatchedCell = td.style("padding-right: 28px;");
if (expression != null) {
mismatchedCell = mismatchedCell.child(mismatchedExpressionType(parameterType, expression));
}
table.append(HtmlChunk.tag("tr").children(nameCell, typeCell, mismatchedCell));
}
return table.wrapWith("table");
}
private static @NotNull @Nls HtmlChunk mismatchedExpressionType(PsiType parameterType, @NotNull PsiExpression expression) {
return HtmlChunk.raw(HighlightUtil.createIncompatibleTypesTooltip(parameterType, expression.getType(), new HighlightUtil.IncompatibleTypesTooltipComposer() {
@Override
public @NotNull String consume(@NotNull @NlsSafe String lRawType,
@NotNull @NlsSafe String lTypeArguments,
@NotNull @NlsSafe String rRawType,
@NotNull @NlsSafe String rTypeArguments) {
return rRawType + rTypeArguments;
}
@Override
public boolean skipTypeArgsColumns() {
return true;
}
}));
}
private static boolean assignmentCompatible(int i,
PsiParameter @NotNull [] parameters,
PsiExpression @NotNull [] expressions,
@NotNull PsiSubstitutor substitutor,
boolean varargs) {
PsiExpression expression = i < expressions.length ? expressions[i] : null;
if (expression == null) return true;
PsiType paramType = substitutor.substitute(PsiTypesUtil.getParameterType(parameters, i, varargs));
return paramType != null && TypeConversionUtil.areTypesAssignmentCompatible(paramType, expression) ||
IncompleteModelUtil.isIncompleteModel(expression) && IncompleteModelUtil.isPotentiallyConvertible(paramType, expression);
}
static HighlightInfo.Builder checkAbstractMethodInConcreteClass(@NotNull PsiMethod method, @NotNull PsiElement elementToHighlight) {
HighlightInfo.Builder errorResult = null;
PsiClass aClass = method.getContainingClass();
@@ -905,234 +673,6 @@ public final class HighlightMethodUtil {
return range;
}
static void checkNewExpression(@NotNull Project project, @NotNull PsiNewExpression expression,
@Nullable PsiType type,
@NotNull JavaSdkVersion javaSdkVersion, @NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
if (!(type instanceof PsiClassType classType)) return;
PsiClassType.ClassResolveResult typeResult = classType.resolveGenerics();
PsiClass aClass = typeResult.getElement();
if (aClass == null) return;
if (aClass instanceof PsiAnonymousClass anonymousClass) {
classType = anonymousClass.getBaseClassType();
typeResult = classType.resolveGenerics();
aClass = typeResult.getElement();
if (aClass == null) return;
}
PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
checkConstructorCall(project, typeResult, expression, classType, classReference, javaSdkVersion, expression.getArgumentList(), errorSink);
}
static void checkAmbiguousConstructorCall(@NotNull Project project, PsiJavaCodeReferenceElement ref,
PsiElement resolved,
PsiElement parent,
JavaSdkVersion version, @NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
if (resolved instanceof PsiClass psiClass &&
parent instanceof PsiNewExpression newExpression && psiClass.getConstructors().length > 0) {
if (newExpression.resolveMethod() == null && !PsiTreeUtil.findChildrenOfType(newExpression.getArgumentList(), PsiFunctionalExpression.class).isEmpty()) {
PsiType type = newExpression.getType();
if (type instanceof PsiClassType classType) {
checkConstructorCall(project, classType.resolveGenerics(), newExpression, type, newExpression.getClassReference(), version, ref,
errorSink);
}
}
}
}
static void checkConstructorCall(@NotNull Project project, @NotNull PsiClassType.ClassResolveResult typeResolveResult,
@NotNull PsiConstructorCall constructorCall,
@NotNull PsiType type,
@Nullable PsiJavaCodeReferenceElement classReference,
@NotNull JavaSdkVersion javaSdkVersion,
@Nullable PsiElement elementToHighlight,
@NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
if (elementToHighlight == null) return;
PsiExpressionList list = constructorCall.getArgumentList();
if (list == null) return;
PsiClass aClass = typeResolveResult.getElement();
if (aClass == null) return;
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper();
PsiClass accessObjectClass = null;
if (constructorCall instanceof PsiNewExpression newExpression) {
PsiExpression qualifier = newExpression.getQualifier();
if (qualifier != null) {
accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement();
}
}
PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length == 0) {
if (!list.isEmpty()) {
String constructorName = aClass.getName();
String argTypes = buildArgTypesList(list, false);
String description = JavaErrorBundle.message("wrong.constructor.arguments", constructorName + "()", argTypes);
String tooltip = createMismatchedArgumentsHtmlTooltip(list, null, PsiParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).description(description).escapedToolTip(tooltip).navigationShift(+1);
if (classReference != null) {
ConstructorParametersFixer.registerFixActions(classReference, constructorCall, asConsumer(info));
}
TextRange textRange = constructorCall.getTextRange();
QuickFixAction.registerQuickFixActions(
info, textRange, QuickFixFactory.getInstance().createCreateConstructorFromUsageFixes(constructorCall)
);
RemoveRedundantArgumentsFix.registerIntentions(list, asConsumer(info));
errorSink.accept(info);
return;
}
if (classReference != null && aClass.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass)) {
HighlightInfo.Builder info = buildAccessProblem(classReference, aClass, typeResolveResult);
errorSink.accept(info);
}
else if (aClass.isInterface() && constructorCall instanceof PsiNewExpression newExpression) {
PsiReferenceParameterList typeArgumentList = newExpression.getTypeArgumentList();
if (typeArgumentList.getTypeArguments().length > 0) {
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeArgumentList)
.descriptionAndTooltip(JavaErrorBundle.message("anonymous.class.implements.interface.cannot.have.type.arguments"));
errorSink.accept(info);
}
}
return;
}
PsiElement place = list;
if (constructorCall instanceof PsiNewExpression newExpression) {
PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass();
if (anonymousClass != null) place = anonymousClass;
}
JavaResolveResult[] results = resolveHelper.multiResolveConstructor((PsiClassType)type, list, place);
MethodCandidateInfo result = null;
if (results.length == 1) result = (MethodCandidateInfo)results[0];
PsiMethod constructor = result == null ? null : result.getElement();
boolean applicable = true;
try {
PsiDiamondType diamondType = constructorCall instanceof PsiNewExpression newExpression ? PsiDiamondType.getDiamondType(newExpression) : null;
JavaResolveResult staticFactory = diamondType != null ? diamondType.getStaticFactory() : null;
if (staticFactory instanceof MethodCandidateInfo info) {
if (info.isApplicable()) {
result = info;
if (constructor == null) {
constructor = info.getElement();
}
}
else {
applicable = false;
}
}
else {
applicable = result != null && result.isApplicable();
}
}
catch (IndexNotReadyException ignored) {
}
boolean reported = false;
if (constructor == null) {
if (IncompleteModelUtil.isIncompleteModel(list) &&
ContainerUtil.exists(results, r -> r instanceof MethodCandidateInfo info && info.isPotentiallyCompatible() == ThreeState.YES) &&
ContainerUtil.exists(list.getExpressions(), e -> IncompleteModelUtil.mayHaveUnknownTypeDueToPendingReference(e))) {
return;
}
String name = aClass.getName();
name += buildArgTypesList(list, true);
String description = JavaErrorBundle.message("cannot.resolve.constructor", name);
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(elementToHighlight).descriptionAndTooltip(description);
WrapExpressionFix.registerWrapAction(results, list.getExpressions(), asConsumer(info));
HighlightFixUtil.registerFixesOnInvalidConstructorCall(asConsumer(info), constructorCall, classReference, list, aClass, constructors, results);
errorSink.accept(info);
reported = true;
}
else if (classReference != null &&
(!result.isAccessible() ||
constructor.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass))) {
HighlightInfo.Builder info = buildAccessProblem(classReference, constructor, result);
errorSink.accept(info);
reported = true;
}
else if (!applicable) {
List<HighlightInfo.Builder> infos = createIncompatibleCallHighlightInfo(list, result);
if (infos != null) {
JavaResolveResult[] methodCandidates = results;
if (constructorCall instanceof PsiNewExpression newExpression) {
methodCandidates = resolveHelper.getReferencedMethodCandidates(newExpression, true);
}
for (HighlightInfo.Builder info : infos) {
HighlightFixUtil.registerFixesOnInvalidConstructorCall(asConsumer(info), constructorCall, classReference, list, aClass, constructors,
methodCandidates);
HighlightFixUtil.registerMethodReturnFixAction(asConsumer(info), result, constructorCall);
errorSink.accept(info);
}
reported = true;
}
}
else if (constructorCall instanceof PsiNewExpression newExpression) {
PsiReferenceParameterList typeArgumentList = newExpression.getTypeArgumentList();
HighlightInfo.Builder info = GenericsHighlightUtil.checkReferenceTypeArgumentList(constructor, typeArgumentList, result.getSubstitutor(), false, javaSdkVersion);
if (info != null) {
errorSink.accept(info);
reported = true;
}
}
HighlightInfo.Builder info = result == null || reported ? null : checkVarargParameterErasureToBeAccessible(result, constructorCall);
if (result != null && info == null && !reported) {
info = createIncompatibleTypeHighlightInfo(constructorCall, result, constructorCall);
}
errorSink.accept(info);
}
/**
* If the compile-time declaration is applicable by variable arity invocation,
* then where the last formal parameter type of the invocation type of the method is Fn[],
* it is a compile-time error if the type which is the erasure of Fn is not accessible at the point of invocation.
*/
private static HighlightInfo.Builder checkVarargParameterErasureToBeAccessible(@NotNull MethodCandidateInfo info, @NotNull PsiCall place) {
PsiMethod method = info.getElement();
if (info.isVarargs() || method.isVarArgs() && !PsiUtil.isLanguageLevel8OrHigher(place)) {
PsiParameter[] parameters = method.getParameterList().getParameters();
PsiType componentType = ((PsiEllipsisType)parameters[parameters.length - 1].getType()).getComponentType();
PsiType substitutedTypeErasure = TypeConversionUtil.erasure(info.getSubstitutor().substitute(componentType));
PsiClass targetClass = PsiUtil.resolveClassInClassTypeOnly(substitutedTypeErasure);
if (targetClass != null && !PsiUtil.isAccessible(targetClass, place, null)) {
PsiExpressionList argumentList = place.getArgumentList();
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.descriptionAndTooltip(JavaErrorBundle.message("formal.varargs.element.type.inaccessible.here",
PsiFormatUtil.formatClass(targetClass, PsiFormatUtilBase.SHOW_FQ_NAME)))
.range(argumentList != null ? argumentList : place);
}
}
return null;
}
private static @NotNull HighlightInfo.Builder buildAccessProblem(@NotNull PsiJavaCodeReferenceElement ref,
@NotNull PsiJvmMember resolved,
@NotNull JavaResolveResult result) {
String description = HighlightUtil.accessProblemDescription(ref, resolved, result);
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(ref).descriptionAndTooltip(description).navigationShift(+1);
if (result.isStaticsScopeCorrect()) {
HighlightFixUtil.registerAccessQuickFixAction(asConsumer(info), resolved, ref, result.getCurrentFileResolveScope());
}
return info;
}
private static boolean callingProtectedConstructorFromDerivedClass(@NotNull PsiConstructorCall place, @NotNull PsiClass constructorClass) {
// indirect instantiation via anonymous class is ok
if (place instanceof PsiNewExpression newExpression && newExpression.getAnonymousClass() != null) return false;
PsiElement curElement = place;
PsiClass containingClass = constructorClass.getContainingClass();
while (true) {
PsiClass aClass = PsiTreeUtil.getParentOfType(curElement, PsiClass.class);
if (aClass == null) return false;
curElement = aClass;
if ((aClass.isInheritor(constructorClass, true) || containingClass != null && aClass.isInheritor(containingClass, true))
&& !JavaPsiFacade.getInstance(aClass.getProject()).arePackagesTheSame(aClass, constructorClass)) {
return true;
}
}
}
private static @NotNull String buildArgTypesList(@NotNull PsiExpressionList list, boolean shortNames) {
StringBuilder builder = new StringBuilder();
builder.append("(");
@@ -503,17 +503,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
@Override
public void visitEnumConstant(@NotNull PsiEnumConstant enumConstant) {
super.visitEnumConstant(enumConstant);
if (!hasErrorResults()) {
PsiClass containingClass = Objects.requireNonNull(enumConstant.getContainingClass());
PsiClassType type = JavaPsiFacade.getElementFactory(getProject()).createType(containingClass);
HighlightMethodUtil.checkConstructorCall(getProject(), type.resolveGenerics(), enumConstant, type, null, myJavaSdkVersion,
enumConstant.getArgumentList(), myErrorSink);
}
}
@Override
public void visitExpression(@NotNull PsiExpression expression) {
ProgressManager.checkCanceled(); // visitLiteralExpression is invoked very often in array initializers
@@ -803,11 +792,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
PsiType type = expression.getType();
if (!hasErrorResults()) add(GenericsHighlightUtil.checkTypeParameterInstantiation(expression));
if (!hasErrorResults()) add(GenericsHighlightUtil.checkGenericArrayCreation(expression, type));
try {
if (!hasErrorResults()) HighlightMethodUtil.checkNewExpression(getProject(), expression, type, myJavaSdkVersion, myErrorSink);
}
catch (IndexNotReadyException ignored) {
}
if (!hasErrorResults()) visitExpression(expression);
@@ -865,9 +849,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!hasErrorResults() && resolved instanceof PsiModifierListOwner) {
PreviewFeatureUtil.checkPreviewFeature(ref, myPreviewFeatureVisitor);
}
if (!hasErrorResults()) {
HighlightMethodUtil.checkAmbiguousConstructorCall(getProject(), ref, resolved, ref.getParent(), myJavaSdkVersion, myErrorSink);
}
}
}
@@ -22,6 +22,7 @@ import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
@@ -81,7 +82,8 @@ final class JavaErrorFixProvider {
CLASS_IMPLICIT_INITIALIZER, CLASS_IMPLICIT_PACKAGE,
RECORD_EXTENDS, ENUM_EXTENDS, RECORD_PERMITS, ENUM_PERMITS, ANNOTATION_PERMITS,
NEW_EXPRESSION_DIAMOND_NOT_ALLOWED, REFERENCE_TYPE_ARGUMENT_STATIC_CLASS,
STATEMENT_CASE_OUTSIDE_SWITCH, NEW_EXPRESSION_DIAMOND_NOT_APPLICABLE)) {
STATEMENT_CASE_OUTSIDE_SWITCH, NEW_EXPRESSION_DIAMOND_NOT_APPLICABLE,
NEW_EXPRESSION_ANONYMOUS_IMPLEMENTS_INTERFACE_WITH_TYPE_ARGUMENTS)) {
fix(kind, genericRemover);
}
@@ -251,6 +253,29 @@ final class JavaErrorFixProvider {
}
return null;
});
multi(NEW_EXPRESSION_ARGUMENTS_TO_DEFAULT_CONSTRUCTOR_CALL, error -> {
PsiConstructorCall constructorCall = error.psi();
List<CommonIntentionAction> registrar = new ArrayList<>();
PsiJavaCodeReferenceElement classReference =
constructorCall instanceof PsiNewExpression newExpression ? newExpression.getClassOrAnonymousClassReference() : null;
if (classReference != null) {
ConstructorParametersFixer.registerFixActions(constructorCall, registrar::add);
}
registrar.addAll(QuickFixFactory.getInstance().createCreateConstructorFromUsageFixes(constructorCall));
RemoveRedundantArgumentsFix.registerIntentions(requireNonNull(constructorCall.getArgumentList()), registrar::add);
return registrar;
});
multi(NEW_EXPRESSION_UNRESOLVED_CONSTRUCTOR, error -> {
PsiConstructorCall constructorCall = error.psi();
PsiExpressionList list = constructorCall.getArgumentList();
List<CommonIntentionAction> registrar = new ArrayList<>();
if (list != null) {
JavaResolveResult[] results = error.context().results();
WrapExpressionFix.registerWrapAction(results, list.getExpressions(), registrar::add);
HighlightFixUtil.registerFixesOnInvalidConstructorCall(registrar::add, constructorCall, error.context().psiClass(), results);
}
return registrar;
});
fix(TYPE_PARAMETER_ABSENT_CLASS, error -> myFactory.createChangeClassSignatureFromUsageFix(error.context(), error.psi()));
fix(TYPE_PARAMETER_COUNT_MISMATCH,
error -> error.context() instanceof PsiClass cls ? myFactory.createChangeClassSignatureFromUsageFix(cls, error.psi()) : null);
@@ -323,7 +348,7 @@ final class JavaErrorFixProvider {
private void createAccessFixes() {
JavaFixesProvider<PsiJavaCodeReferenceElement, JavaResolveResult> accessFix = error -> {
List<CommonIntentionAction> registrar = new ArrayList<>();
if (error.context().getElement() instanceof PsiJvmMember member) {
if (error.context().isStaticsScopeCorrect() && error.context().getElement() instanceof PsiJvmMember member) {
HighlightFixUtil.registerAccessQuickFixAction(registrar::add, member, error.psi(), null);
}
return registrar;
@@ -397,21 +422,39 @@ final class JavaErrorFixProvider {
multi(CALL_WRONG_ARGUMENTS, error -> {
JavaMismatchedCallContext context = error.context();
List<CommonIntentionAction> registrar = new ArrayList<>();
if (context.list().getParent() instanceof PsiMethodCallExpression methodCall) {
PsiExpressionList list = context.list();
Project project = error.project();
PsiResolveHelper resolveHelper = PsiResolveHelper.getInstance(project);
MethodCandidateInfo candidate = context.candidate();
PsiElement parent = list.getParent();
if (parent instanceof PsiAnonymousClass) {
parent = parent.getParent();
}
if (parent instanceof PsiMethodCallExpression methodCall) {
PsiType expectedTypeByParent = InferenceSession.getTargetTypeByParent(methodCall);
PsiType actualType = ((PsiExpression)methodCall.copy()).getType();
if (expectedTypeByParent != null && actualType != null && !expectedTypeByParent.isAssignableFrom(actualType)) {
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(registrar::add, methodCall, expectedTypeByParent, actualType);
}
PsiResolveHelper resolveHelper = PsiResolveHelper.getInstance(error.project());
HighlightFixUtil.registerQualifyMethodCallFix(
resolveHelper.getReferencedMethodCandidates(methodCall, false), methodCall, context.list(), registrar::add);
HighlightFixUtil.registerMethodCallIntentions(registrar::add, methodCall, context.list());
MethodCandidateInfo candidate = context.candidate();
resolveHelper.getReferencedMethodCandidates(methodCall, false), methodCall, list, registrar::add);
HighlightFixUtil.registerMethodCallIntentions(registrar::add, methodCall, list);
HighlightFixUtil.registerMethodReturnFixAction(registrar::add, candidate, methodCall);
HighlightFixUtil.registerTargetTypeFixesBasedOnApplicabilityInference(methodCall, candidate, candidate.getElement(), registrar::add);
HighlightFixUtil.registerImplementsExtendsFix(registrar::add, methodCall, candidate.getElement());
}
if (parent instanceof PsiConstructorCall constructorCall) {
JavaResolveResult[] methodCandidates = JavaResolveResult.EMPTY_ARRAY;
PsiClass aClass = requireNonNull(candidate.getElement().getContainingClass());
if (constructorCall instanceof PsiNewExpression newExpression) {
methodCandidates = resolveHelper.getReferencedMethodCandidates(newExpression, true);
} else if (constructorCall instanceof PsiEnumConstant enumConstant) {
PsiClassType type = JavaPsiFacade.getElementFactory(project).createType(aClass);
methodCandidates = resolveHelper.multiResolveConstructor(type, list, enumConstant);
}
HighlightFixUtil.registerFixesOnInvalidConstructorCall(registrar::add, constructorCall, aClass, methodCandidates);
HighlightFixUtil.registerMethodReturnFixAction(registrar::add, candidate, constructorCall);
}
return registrar;
});
}
@@ -14,7 +14,6 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -107,27 +106,20 @@ public class ChangeTypeArgumentsFix extends PsiUpdateModCommandAction<PsiNewExpr
public static void registerIntentions(JavaResolveResult @NotNull [] candidates,
@NotNull PsiExpressionList list,
@NotNull PsiConstructorCall call,
@NotNull Consumer<? super CommonIntentionAction> info,
PsiClass psiClass) {
if (candidates.length == 0) return;
if (!(call instanceof PsiNewExpression newExpression)) return;
PsiExpressionList list = newExpression.getArgumentList();
if (list == null) return;
PsiExpression[] expressions = list.getExpressions();
for (JavaResolveResult candidate : candidates) {
registerIntention(expressions, info, psiClass, candidate, list);
}
}
private static void registerIntention(PsiExpression @NotNull [] expressions,
@NotNull Consumer<? super CommonIntentionAction> info,
PsiClass psiClass,
@NotNull JavaResolveResult candidate,
@NotNull PsiElement context) {
if (!candidate.isStaticsScopeCorrect()) return;
PsiMethod method = (PsiMethod)candidate.getElement();
if (method != null && BaseIntentionAction.canModify(method)) {
PsiNewExpression newExpression = PsiTreeUtil.getParentOfType(context, PsiNewExpression.class);
if (newExpression == null) return;
info.accept(new ChangeTypeArgumentsFix(method, psiClass, expressions, newExpression));
if (!candidate.isStaticsScopeCorrect()) continue;
PsiMethod method = (PsiMethod)candidate.getElement();
if (method != null && BaseIntentionAction.canModify(method)) {
info.accept(new ChangeTypeArgumentsFix(method, psiClass, expressions, newExpression));
}
}
}
}
@@ -18,20 +18,28 @@ import org.jetbrains.annotations.NotNull;
import java.util.function.Consumer;
public final class ConstructorParametersFixer {
public static void registerFixActions(@NotNull PsiJavaCodeReferenceElement ctrRef,
@NotNull PsiConstructorCall constructorCall,
public static void registerFixActions(@NotNull PsiConstructorCall constructorCall,
@NotNull Consumer<? super CommonIntentionAction> info) {
JavaResolveResult resolved = ctrRef.advancedResolve(false);
PsiClass aClass = (PsiClass) resolved.getElement();
PsiSubstitutor substitutor = resolved.getSubstitutor();
if (aClass == null) return;
registerFixActions(aClass, substitutor, constructorCall, info);
if (constructorCall instanceof PsiNewExpression newExpression) {
PsiJavaCodeReferenceElement ctrRef = newExpression.getClassOrAnonymousClassReference();
if (ctrRef == null) return;
JavaResolveResult resolved = ctrRef.advancedResolve(false);
PsiClass aClass = (PsiClass) resolved.getElement();
PsiSubstitutor substitutor = resolved.getSubstitutor();
if (aClass == null) return;
registerFixActions(aClass, substitutor, constructorCall, info);
} else if (constructorCall instanceof PsiEnumConstant enumConstant) {
PsiClass containingClass = enumConstant.getContainingClass();
if (containingClass != null) {
registerFixActions(containingClass, PsiSubstitutor.EMPTY, constructorCall, info);
}
}
}
public static void registerFixActions(@NotNull PsiClass aClass,
@NotNull PsiSubstitutor substitutor,
@NotNull PsiConstructorCall constructorCall,
@NotNull Consumer<? super CommonIntentionAction> info) {
private static void registerFixActions(@NotNull PsiClass aClass,
@NotNull PsiSubstitutor substitutor,
@NotNull PsiConstructorCall constructorCall,
@NotNull Consumer<? super CommonIntentionAction> info) {
PsiMethod[] methods = aClass.getConstructors();
CandidateInfo[] candidates = new CandidateInfo[methods.length];
for (int i = 0; i < candidates.length; i++) {
@@ -3,6 +3,6 @@ class MyTest {
}
{
new MyTest(1, <error descr="'MyTest(int, int, int)' in 'MyTest' cannot be applied to '(int, java.lang.String)'">""</error>);
new MyTest(1, <error descr="Expected 3 arguments but found 2">""</error>);
}
}
@@ -10,6 +10,6 @@ interface Either {
class Main {
{
new <error descr="'Left(L)' has private access in 'Either.Left'">Either.Left<></error>("");
new Either.<error descr="'Left(L)' has private access in 'Either.Left'">Left</error><>("");
}
}
@@ -1,14 +1,14 @@
class Test {
{
Holder h = null;
Result<String> r1 = <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">new Result<>(h);</error>
Result<String> r1 = new <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">Result<></error>(h);
Result<String> r2 = Result.<error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">create</error>(h);
Holder dataHolder = null;
Result<String> r3 = <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">new Result<>(new Holder<>(dataHolder));</error>
Result<String> r3 = new <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">Result<></error>(new Holder<>(dataHolder));
Result<String> r4 = Result.<error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">create</error>(new Holder<>(dataHolder));
Result<String> r5 = <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">new Result<>(Holder.create(dataHolder));</error>
Result<String> r5 = new <error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">Result<></error>(Holder.create(dataHolder));
Result<String> r6 = Result.<error descr="Incompatible types. Found: 'Result<Holder>', required: 'Result<java.lang.String>'">create</error>(Holder.create(dataHolder));
}
@@ -4,7 +4,7 @@ import java.util.function.Supplier;
class OverloadCast {
public void runMe() {
new <error descr="Cannot resolve constructor 'OverloadCast(<method reference>, <lambda expression>)'">OverloadCast</error>(WhitespaceTokenizer::<error descr="Cannot resolve constructor 'WhitespaceTokenizer'">new</error>, src -> new LowerCaseFilter<error descr="'LowerCaseFilter(OverloadCast.TokenStream)' in 'OverloadCast.LowerCaseFilter' cannot be applied to '(<lambda parameter>)'">(src)</error>);
new OverloadCast<error descr="Cannot resolve constructor 'OverloadCast(<method reference>, <lambda expression>)'">(WhitespaceTokenizer::<error descr="Cannot resolve constructor 'WhitespaceTokenizer'">new</error>, src -> new LowerCaseFilter<error descr="'LowerCaseFilter(OverloadCast.TokenStream)' in 'OverloadCast.LowerCaseFilter' cannot be applied to '(<lambda parameter>)'">(src)</error>)</error>;
<error descr="Ambiguous method call: both 'OverloadCast.overloadCast(Supplier<Tokenizer>, Function<TokenStream, TokenFilter>)' and 'OverloadCast.overloadCast(Function<TokenStream, TokenFilter>, Function<String, String>)' match">overloadCast</error>(WhitespaceTokenizer::<error descr="Cannot resolve constructor 'WhitespaceTokenizer'">new</error>, src -> new LowerCaseFilter<error descr="'LowerCaseFilter(OverloadCast.TokenStream)' in 'OverloadCast.LowerCaseFilter' cannot be applied to '(<lambda parameter>)'">(src)</error>);
}
@@ -1059,10 +1059,10 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
String red = ColorUtil.toHtmlColor(NamedColorUtil.getErrorForeground());
String expected = "<html><table>" +
"<tr>" +
"<td style='padding: 0px 16px 8px 4px;color: " + greyed + "'>Required type:</td>" +
"<td style='padding: 0px 4px 8px 0px;'><font color=\"" + toolTipForeground + "\">int</font></td></tr>" +
"<tr><td style='padding: 0px 16px 0px 4px;color: " + greyed + "'>Provided:</td>" +
"<td style='padding: 0px 4px 0px 0px;'><font color=\"" + red + "\">String</font></td></tr>" +
"<td style=\"padding: 0px 16px 8px 4px; color: " + greyed + "\">Required type:</td>" +
"<td style=\"padding: 0px 4px 8px 0px;\"><span style=\"color: " + toolTipForeground + "\">int</span></td></tr>" +
"<tr><td style=\"padding: 0px 16px 0px 4px; color: " + greyed + "\">Provided:</td>" +
"<td style=\"padding: 0px 4px 0px 0px;\"><span style=\"color: " + red + "\">String</span></td></tr>" +
"</table>" +
"</html>";
@@ -1079,11 +1079,11 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
String red = ColorUtil.toHtmlColor(NamedColorUtil.getErrorForeground());
String expected = "<html><table>" +
"<tr>" +
"<td style=\"padding: 0px 16px 8px 4px;\" style=\"color: " + greyed + "\">Required type:</td>" +
"<td style=\"padding: 0px 16px 8px 4px; color: " + greyed + "\">Required type:</td>" +
"<td style=\"padding: 0px 4px 8px 0px;\"><span style=\"color: " + toolTipForeground + "\">String</span></td>" +
"</tr>" +
"<tr>" +
"<td style=\"padding: 0px 16px 0px 4px;\" style=\"color: " + greyed + "\">Provided:</td>" +
"<td style=\"padding: 0px 16px 0px 4px; color: " + greyed + "\">Provided:</td>" +
"<td style=\"padding: 0px 4px 0px 0px;\"><span style=\"color: " + red + "\">int</span></td>" +
"</tr>" +
"</table></html>";
@@ -1103,8 +1103,8 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
.getAttributes(DefaultLanguageHighlighterColors.INLINE_PARAMETER_HINT)
.getBackgroundColor());
String expected = "<html><table>" +
"<tr><td/><td style=\"padding-left: 16px; padding-right: 24px; color: " + greyed + "\">Required type</td>" +
"<td style=\"padding-right: 28px; color: " + greyed + "\">Provided</td></tr>" +
"<tr><td/><td style=\"padding-left: 16px; padding-right: 24px; color: " + greyed + "\">Required type:</td>" +
"<td style=\"padding-right: 28px; color: " + greyed + "\">Provided:</td></tr>" +
"<tr><td><table><tr><td style=\"padding:1px 4px 1px 4px; color: " + greyed + "; background-color: " + paramBgColor + "\">list:</td></tr></table></td>" +
"<td style=\"padding-left: 16px; padding-right: 24px;\"><span style=\"color: " + toolTipForeground + "\">String...</span></td>" +
"<td style=\"padding-right: 28px;\"><span style=\"color: " + red + "\">int</span></td></tr>" +
@@ -1124,10 +1124,10 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
String red = ColorUtil.toHtmlColor(NamedColorUtil.getErrorForeground());
String expected = "<html><table>" +
"<tr>" +
"<td style=\"padding: 0px 16px 8px 4px;\" style=\"color: "+greyed+"\">Required type:</td>" +
"<td style=\"padding: 0px 16px 8px 4px; color: "+greyed+"\">Required type:</td>" +
"<td style=\"padding: 0px 4px 8px 0px;\"><span style=\"color: "+toolTipForeground+"\">CharSequence</span></td>" +
"</tr>" +
"<tr><td style=\"padding: 0px 16px 0px 4px;\" style=\"color: "+greyed+"\">Provided:</td>" +
"<tr><td style=\"padding: 0px 16px 0px 4px; color: "+greyed+"\">Provided:</td>" +
"<td style=\"padding: 0px 4px 0px 0px;\"><span style=\"color: "+red+"\">int</span></td></tr>" +
"</table></html>";
@@ -1144,12 +1144,12 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
String red = ColorUtil.toHtmlColor(NamedColorUtil.getErrorForeground());
String expected = "<html><table>" +
"<tr>" +
"<td style=\"padding: 0px 16px 8px 4px;\" style=\"color: "+greyed+"\">Required type:</td>" +
"<td style=\"padding: 0px 16px 8px 4px; color: "+greyed+"\">Required type:</td>" +
"<td style=\"padding: 0px 4px 8px 0px;\"><span style=\"color: "+toolTipForeground+"\">Class</span></td>" +
"<td style='padding: 0px 0px 8px 0px;'>&lt;<span style=\"color: "+toolTipForeground+"\">capture of ?</span>&gt;</td>" +
"</tr>" +
"<tr>" +
"<td style=\"padding: 0px 16px 0px 4px;\" style=\"color: "+greyed+"\">Provided:</td>" +
"<td style=\"padding: 0px 16px 0px 4px; color: "+greyed+"\">Provided:</td>" +
"<td style=\"padding: 0px 4px 0px 0px;\"><span style=\"color: "+toolTipForeground+"\">Class</span></td>" +
"<td style='padding: 0px 0px 0px 0px;'>&lt;<span style=\"color: "+red+"\">capture of ?</span>&gt;</td></tr>" +
"</table></html>";
@@ -1171,8 +1171,8 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
String expected = "<html><table>" +
"<tr>" +
"<td/>" +
"<td style=\"padding-left: 16px; padding-right: 24px; color: " + greyed + "\">Required type</td>" +
"<td style=\"padding-right: 28px; color: " + greyed + "\">Provided</td></tr>" +
"<td style=\"padding-left: 16px; padding-right: 24px; color: " + greyed + "\">Required type:</td>" +
"<td style=\"padding-right: 28px; color: " + greyed + "\">Provided:</td></tr>" +
"<tr>" +
"<td><table><tr><td style=\"padding:1px 4px 1px 4px; color: " + greyed + "; background-color: " + paramBgColor + "\">integerList:</td></tr></table></td>" +
"<td style=\"padding-left: 16px; padding-right: 24px;\"><span style=\"color: " + toolTipForeground + "\">List&lt;Integer&gt;</span></td>" +