mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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:
committed by
intellij-monorepo-bot
parent
ccc61915e9
commit
b0614fa46d
+5
-8
@@ -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
|
||||
|
||||
+105
-8
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-11
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+2
-1
@@ -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();
|
||||
|
||||
+16
-2
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+41
-14
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
+26
-29
@@ -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))
|
||||
|
||||
+5
-10
@@ -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);
|
||||
}
|
||||
|
||||
+7
-467
@@ -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("(");
|
||||
|
||||
-19
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-7
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
+9
-17
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-11
@@ -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++) {
|
||||
|
||||
+1
-1
@@ -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>);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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><>("");
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -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));
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -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>);
|
||||
}
|
||||
|
||||
|
||||
+14
-14
@@ -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;'><<span style=\"color: "+toolTipForeground+"\">capture of ?</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: "+toolTipForeground+"\">Class</span></td>" +
|
||||
"<td style='padding: 0px 0px 0px 0px;'><<span style=\"color: "+red+"\">capture of ?</span>></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<Integer></span></td>" +
|
||||
|
||||
Reference in New Issue
Block a user