[java-highlighting] Finish checkMethodCall migration

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

GitOrigin-RevId: 68d3ae802d6340854838c680979aba531a778488
This commit is contained in:
Tagir Valeev
2025-01-22 12:50:33 +00:00
committed by intellij-monorepo-bot
parent 0be0a316f7
commit 7f391af965
11 changed files with 362 additions and 151 deletions
@@ -130,6 +130,14 @@ type.parameter.on.annotation.member=@interface members may not have type paramet
type.parameter.on.annotation=@interface may not have type parameters
type.parameter.duplicate=Duplicate type parameter: ''{0}''
type.parameter.incompatible.upper.bounds=Type parameter {0} has incompatible upper bounds: {1}
type.parameter.inferred.type.not.within.extend.bound=Inferred type ''{2}'' for type parameter ''{0}'' is not within its bound; should extend ''{1}''
type.parameter.inferred.type.not.within.implement.bound=Inferred type ''{2}'' for type parameter ''{0}'' is not within its bound; should implement ''{1}''
type.parameter.type.not.within.extend.bound=Type parameter ''{0}'' is not within its bound; should extend ''{1}''
type.parameter.type.not.within.implement.bound=Type parameter ''{0}'' is not within its bound; should implement ''{1}''
type.parameter.absent.class=Type ''{0}'' does not have type parameters
type.parameter.absent.method=Method ''{0}'' does not have type parameters
type.parameter.count.mismatch=Wrong number of type arguments: {0}; required: {1}
type.parameter.actual.inferred.mismatch=Actual type argument and inferred type contradict each other
method.duplicate=''{0}'' is already defined in ''{1}''
method.throws.class.name.expected=Class name expected
@@ -176,6 +184,9 @@ new.expression.qualified.static.class=Qualified new of static class
new.expression.qualified.anonymous.implements.interface=Anonymous class implements interface; cannot have qualifier for new
new.expression.qualified.qualified.class.reference=Qualified class reference is not allowed in qualified new
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
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
@@ -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.util.Pair;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.impl.IncompleteModelUtil;
@@ -18,10 +19,7 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static com.intellij.util.ObjectUtils.tryCast;
@@ -344,16 +342,48 @@ final class ExpressionChecker {
MethodCandidateInfo candidateInfo = resolveResult instanceof MethodCandidateInfo ? (MethodCandidateInfo)resolveResult : null;
PsiMethod resolvedMethod = candidateInfo != null ? candidateInfo.getElement() : null;
if (!resolveResult.isAccessible() || !resolveResult.isStaticsScopeCorrect()) {
}
else if (candidateInfo != null && !candidateInfo.isApplicable()) {
if (candidateInfo.isTypeArgumentsApplicable()) {
checkIncompatibleCall(list, candidateInfo);
if (resolveResult.isAccessible() && resolveResult.isStaticsScopeCorrect()) {
if (candidateInfo != null && !candidateInfo.isApplicable()) {
if (candidateInfo.isTypeArgumentsApplicable()) {
checkIncompatibleCall(list, candidateInfo);
}
else {
PsiReferenceParameterList typeArgumentList = methodCall.getTypeArgumentList();
PsiSubstitutor applicabilitySubstitutor = candidateInfo.getSubstitutor(false);
if (typeArgumentList.getTypeArguments().length == 0 && resolvedMethod.hasTypeParameters()) {
checkInferredTypeArguments(resolvedMethod, methodCall, applicabilitySubstitutor);
}
else {
myVisitor.myGenericsChecker.checkParameterizedReferenceTypeArguments(resolved, referenceToMethod, applicabilitySubstitutor);
}
}
}
else {
myVisitor.report(JavaErrorKinds.CALL_EXPECTED.create(methodCall));
}
}
else {
myVisitor.report(JavaErrorKinds.CALL_EXPECTED.create(methodCall));
}
}
if (!myVisitor.hasErrorResults()) {
myVisitor.myGenericsChecker.checkParameterizedReferenceTypeArguments(resolved, referenceToMethod, substitutor);
}
}
void checkInferredTypeArguments(@NotNull PsiTypeParameterListOwner listOwner,
@NotNull PsiMethodCallExpression call,
@NotNull PsiSubstitutor substitutor) {
PsiTypeParameter[] typeParameters = listOwner.getTypeParameters();
Pair<PsiTypeParameter, PsiType> inferredTypeArgument = GenericsUtil.findTypeParameterWithBoundError(
typeParameters, substitutor, call, false);
if (inferredTypeArgument != null) {
PsiType extendsType = inferredTypeArgument.second;
PsiTypeParameter typeParameter = inferredTypeArgument.first;
PsiClass boundClass = extendsType instanceof PsiClassType classType ? classType.resolve() : null;
var kind = boundClass == null || typeParameter.isInterface() == boundClass.isInterface()
? JavaErrorKinds.TYPE_PARAMETER_INFERRED_TYPE_NOT_WITHIN_EXTEND_BOUND
: JavaErrorKinds.TYPE_PARAMETER_INFERRED_TYPE_NOT_WITHIN_IMPLEMENT_BOUND;
myVisitor.report(kind.create(call, new JavaErrorKinds.TypeParameterBoundMismatchContext(
typeParameter, extendsType, Objects.requireNonNull(substitutor.substitute(typeParameter)))));
}
}
@@ -4,6 +4,7 @@ package com.intellij.java.codeserver.highlighting;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vfs.VirtualFile;
@@ -12,6 +13,8 @@ import com.intellij.psi.impl.IncompleteModelUtil;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -250,4 +253,167 @@ final class GenericsChecker {
}
}
}
void checkParameterizedReferenceTypeArguments(@Nullable PsiElement resolved,
@NotNull PsiJavaCodeReferenceElement referenceElement,
@NotNull PsiSubstitutor substitutor) {
if (!(resolved instanceof PsiTypeParameterListOwner typeParameterListOwner)) return;
checkReferenceTypeArgumentList(typeParameterListOwner, referenceElement.getParameterList(), substitutor);
}
void checkReferenceTypeArgumentList(@NotNull PsiTypeParameterListOwner typeParameterListOwner,
@Nullable PsiReferenceParameterList referenceParameterList,
@NotNull PsiSubstitutor substitutor) {
PsiDiamondType.DiamondInferenceResult inferenceResult = null;
PsiTypeElement[] referenceElements = null;
if (referenceParameterList != null) {
referenceElements = referenceParameterList.getTypeParameterElements();
if (referenceElements.length == 1 && referenceElements[0].getType() instanceof PsiDiamondType diamondType) {
if (!typeParameterListOwner.hasTypeParameters()) {
myVisitor.report(JavaErrorKinds.NEW_EXPRESSION_DIAMOND_NOT_APPLICABLE.create(referenceParameterList));
return;
}
inferenceResult = diamondType.resolveInferredTypes();
String errorMessage = inferenceResult.getErrorMessage();
if (errorMessage != null) {
PsiType expectedType = detectExpectedType(referenceParameterList);
if (!(inferenceResult.failedToInfer() && expectedType instanceof PsiClassType classType && classType.isRaw())) {
if (inferenceResult == PsiDiamondType.DiamondInferenceResult.ANONYMOUS_INNER_RESULT ||
inferenceResult == PsiDiamondType.DiamondInferenceResult.EXPLICIT_CONSTRUCTOR_TYPE_ARGS) {
myVisitor.report(JavaErrorKinds.NEW_EXPRESSION_DIAMOND_INFERENCE_FAILURE.create(referenceParameterList, inferenceResult));
return;
}
}
}
PsiElement parent = referenceParameterList.getParent().getParent();
if (parent instanceof PsiAnonymousClass anonymousClass &&
ContainerUtil.exists(anonymousClass.getMethods(),
method -> !method.hasModifierProperty(PsiModifier.PRIVATE) && method.findSuperMethods().length == 0)) {
myVisitor.report(JavaErrorKinds.NEW_EXPRESSION_DIAMOND_ANONYMOUS_INNER_NON_PRIVATE.create(referenceParameterList));
return;
}
}
}
PsiTypeParameter[] typeParameters = typeParameterListOwner.getTypeParameters();
int targetParametersNum = typeParameters.length;
int refParametersNum = referenceParameterList == null ? 0 : referenceParameterList.getTypeArguments().length;
if (targetParametersNum != refParametersNum && refParametersNum != 0) {
if (targetParametersNum == 0) {
boolean shouldSuppress = PsiTreeUtil.getParentOfType(referenceParameterList, PsiCall.class) != null &&
typeParameterListOwner instanceof PsiMethod psiMethod &&
(myVisitor.sdkVersion().isAtLeast(JavaSdkVersion.JDK_1_7) || hasSuperMethodsWithTypeParams(psiMethod));
if (!shouldSuppress) {
if (typeParameterListOwner instanceof PsiMethod psiMethod) {
myVisitor.report(JavaErrorKinds.TYPE_PARAMETER_ABSENT_METHOD.create(referenceParameterList, psiMethod));
}
else if (typeParameterListOwner instanceof PsiClass psiClass) {
myVisitor.report(JavaErrorKinds.TYPE_PARAMETER_ABSENT_CLASS.create(referenceParameterList, psiClass));
}
return;
}
}
else {
myVisitor.report(JavaErrorKinds.TYPE_PARAMETER_COUNT_MISMATCH.create(referenceParameterList, typeParameterListOwner));
return;
}
}
// bounds check
if (targetParametersNum > 0 && refParametersNum != 0) {
if (inferenceResult != null) {
PsiType[] types = inferenceResult.getTypes();
for (int i = 0; i < typeParameters.length; i++) {
checkTypeParameterWithinItsBound(typeParameters[i], substitutor, types[i], referenceElements[0], referenceParameterList);
if (myVisitor.hasErrorResults()) return;
}
}
else {
for (int i = 0; i < typeParameters.length; i++) {
PsiTypeElement typeElement = referenceElements[i];
checkTypeParameterWithinItsBound(typeParameters[i], substitutor, typeElement.getType(), typeElement, referenceParameterList);
if (myVisitor.hasErrorResults()) return;
}
}
}
}
private void checkTypeParameterWithinItsBound(@NotNull PsiTypeParameter classParameter,
@NotNull PsiSubstitutor substitutor,
@NotNull PsiType type,
@NotNull PsiTypeElement typeElement2Highlight,
@Nullable PsiReferenceParameterList referenceParameterList) {
PsiClass referenceClass = type instanceof PsiClassType classType ? classType.resolve() : null;
PsiType psiType = substitutor.substitute(classParameter);
if (psiType instanceof PsiClassType && !(PsiUtil.resolveClassInType(psiType) instanceof PsiTypeParameter)) {
if (GenericsUtil.checkNotInBounds(type, psiType, referenceParameterList)) {
myVisitor.report(JavaErrorKinds.TYPE_PARAMETER_ACTUAL_INFERRED_MISMATCH.create(typeElement2Highlight));
return;
}
}
PsiClassType[] bounds = classParameter.getSuperTypes();
for (PsiType bound : bounds) {
bound = substitutor.substitute(bound);
if (!bound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && GenericsUtil.checkNotInBounds(type, bound, referenceParameterList)) {
PsiClass boundClass = bound instanceof PsiClassType classType ? classType.resolve() : null;
boolean extend = boundClass == null ||
referenceClass == null ||
referenceClass.isInterface() == boundClass.isInterface() ||
referenceClass instanceof PsiTypeParameter;
var kind = extend
? JavaErrorKinds.TYPE_PARAMETER_TYPE_NOT_WITHIN_EXTEND_BOUND
: JavaErrorKinds.TYPE_PARAMETER_TYPE_NOT_WITHIN_IMPLEMENT_BOUND;
myVisitor.report(kind.create(typeElement2Highlight, new JavaErrorKinds.TypeParameterBoundMismatchContext(
classParameter, bound, type)));
}
}
}
private static PsiType detectExpectedType(@NotNull PsiReferenceParameterList referenceParameterList) {
PsiNewExpression newExpression = Objects.requireNonNull(PsiTreeUtil.getParentOfType(referenceParameterList, PsiNewExpression.class));
PsiElement parent = newExpression.getParent();
PsiType expectedType = null;
if (parent instanceof PsiVariable psiVariable && newExpression.equals(psiVariable.getInitializer())) {
expectedType = psiVariable.getType();
}
else if (parent instanceof PsiAssignmentExpression expression && newExpression.equals(expression.getRExpression())) {
expectedType = expression.getLExpression().getType();
}
else if (parent instanceof PsiReturnStatement) {
PsiElement method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class, PsiLambdaExpression.class);
if (method instanceof PsiMethod psiMethod) {
expectedType = psiMethod.getReturnType();
}
}
else if (parent instanceof PsiExpressionList) {
PsiElement pParent = parent.getParent();
if (pParent instanceof PsiCallExpression callExpression) {
PsiExpressionList argumentList = callExpression.getArgumentList();
if (parent.equals(argumentList)) {
PsiMethod method = callExpression.resolveMethod();
if (method != null) {
PsiExpression[] expressions = argumentList.getExpressions();
int idx = ArrayUtilRt.find(expressions, newExpression);
if (idx > -1) {
PsiParameter parameter = method.getParameterList().getParameter(idx);
if (parameter != null) {
expectedType = parameter.getType();
}
}
}
}
}
}
return expectedType;
}
private static boolean hasSuperMethodsWithTypeParams(@NotNull PsiMethod method) {
for (PsiMethod superMethod : method.findDeepestSuperMethods()) {
if (superMethod.hasTypeParameters()) return true;
}
return false;
}
}
@@ -178,4 +178,9 @@ final class JavaErrorFormatUtil {
if (element instanceof PsiLabeledStatement statement) return statement.getName() + ':';
return ElementDescriptionUtil.getElementDescription(element, HighlightUsagesDescriptionLocation.INSTANCE);
}
static @NotNull String formatClassOrType(@NotNull PsiType type) {
PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(type);
return psiClass == null ? type.getPresentableText() : formatClass(psiClass);
}
}
@@ -385,6 +385,41 @@ public final class JavaErrorKinds {
parameterized(PsiMethodCallExpression.class, IncompatibleIntersectionContext.class, "type.parameter.incompatible.upper.bounds")
.withRange((call, ctx) -> getRange(call))
.withRawDescription((call, ctx) -> message("type.parameter.incompatible.upper.bounds", ctx.parameter().getName(), ctx.message()));
public static final Parameterized<PsiMethodCallExpression, TypeParameterBoundMismatchContext>
TYPE_PARAMETER_INFERRED_TYPE_NOT_WITHIN_EXTEND_BOUND =
parameterized(PsiMethodCallExpression.class, TypeParameterBoundMismatchContext.class,
"type.parameter.inferred.type.not.within.extend.bound")
.withRawDescription((call, ctx) -> message("type.parameter.inferred.type.not.within.extend.bound", formatClass(ctx.parameter()),
formatType(ctx.bound()), formatType(ctx.actualType())));
public static final Parameterized<PsiMethodCallExpression, TypeParameterBoundMismatchContext>
TYPE_PARAMETER_INFERRED_TYPE_NOT_WITHIN_IMPLEMENT_BOUND =
parameterized(PsiMethodCallExpression.class, TypeParameterBoundMismatchContext.class,
"type.parameter.inferred.type.not.within.implement.bound")
.withRawDescription((call, ctx) -> message("type.parameter.inferred.type.not.within.implement.bound", formatClass(ctx.parameter()),
formatType(ctx.bound()), formatType(ctx.actualType())));
public static final Parameterized<PsiTypeElement, TypeParameterBoundMismatchContext> TYPE_PARAMETER_TYPE_NOT_WITHIN_EXTEND_BOUND =
parameterized(PsiTypeElement.class, TypeParameterBoundMismatchContext.class, "type.parameter.type.not.within.extend.bound")
.withRawDescription((call, ctx) -> message("type.parameter.type.not.within.extend.bound",
formatClassOrType(ctx.actualType()),
formatType(ctx.bound())));
public static final Parameterized<PsiTypeElement, TypeParameterBoundMismatchContext> TYPE_PARAMETER_TYPE_NOT_WITHIN_IMPLEMENT_BOUND =
parameterized(PsiTypeElement.class, TypeParameterBoundMismatchContext.class, "type.parameter.type.not.within.implement.bound")
.withRawDescription((call, ctx) -> message("type.parameter.type.not.within.implement.bound",
formatClassOrType(ctx.actualType()),
formatType(ctx.bound())));
public static final Parameterized<PsiReferenceParameterList, PsiClass> TYPE_PARAMETER_ABSENT_CLASS =
parameterized(PsiReferenceParameterList.class, PsiClass.class, "type.parameter.absent.class")
.withRawDescription((list, cls) -> message("type.parameter.absent.class", formatClass(cls)));
public static final Parameterized<PsiReferenceParameterList, PsiMethod> TYPE_PARAMETER_ABSENT_METHOD =
parameterized(PsiReferenceParameterList.class, PsiMethod.class, "type.parameter.absent.method")
.withRawDescription((list, method) -> message("type.parameter.absent.method", formatMethod(method)));
public static final Parameterized<PsiReferenceParameterList, PsiTypeParameterListOwner> TYPE_PARAMETER_COUNT_MISMATCH =
parameterized(PsiReferenceParameterList.class, PsiTypeParameterListOwner.class, "type.parameter.count.mismatch")
.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")
@@ -567,6 +602,13 @@ public final class JavaErrorKinds {
error("new.expression.qualified.qualified.class.reference");
public static final Simple<PsiReferenceParameterList> NEW_EXPRESSION_DIAMOND_NOT_ALLOWED =
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 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 Parameterized<PsiReferenceParameterList, PsiClass> REFERENCE_TYPE_ARGUMENT_STATIC_CLASS =
parameterized(PsiReferenceParameterList.class, PsiClass.class, "reference.type.argument.static.class")
@@ -764,4 +806,10 @@ public final class JavaErrorKinds {
}
public record IncompatibleIntersectionContext(@NotNull PsiTypeParameter parameter, @NotNull @Nls String message) {}
public record TypeParameterBoundMismatchContext(@NotNull PsiTypeParameter parameter,
@NotNull PsiType bound,
@NotNull PsiType actualType) {
}
}
@@ -11,7 +11,6 @@ import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper;
import com.intellij.core.JavaPsiBundle;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.openapi.roots.FileIndexFacade;
@@ -26,7 +25,6 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.ObjectUtils;
@@ -45,33 +43,6 @@ public final class GenericsHighlightUtil {
private GenericsHighlightUtil() { }
static HighlightInfo.Builder checkInferredTypeArguments(@NotNull PsiTypeParameterListOwner listOwner,
@NotNull PsiElement call,
@NotNull PsiSubstitutor substitutor) {
PsiTypeParameter[] typeParameters = listOwner.getTypeParameters();
Pair<PsiTypeParameter, PsiType> inferredTypeArgument = GenericsUtil.findTypeParameterWithBoundError(typeParameters, substitutor,
call, false);
if (inferredTypeArgument != null) {
PsiType extendsType = inferredTypeArgument.second;
PsiTypeParameter typeParameter = inferredTypeArgument.first;
PsiClass boundClass = extendsType instanceof PsiClassType classType ? classType.resolve() : null;
@NonNls String messageKey = boundClass == null || typeParameter.isInterface() == boundClass.isInterface()
? "generics.inferred.type.for.type.parameter.is.not.within.its.bound.extend"
: "generics.inferred.type.for.type.parameter.is.not.within.its.bound.implement";
String description = JavaErrorBundle.message(
messageKey,
HighlightUtil.formatClass(typeParameter),
JavaHighlightUtil.formatType(extendsType),
JavaHighlightUtil.formatType(substitutor.substitute(typeParameter))
);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(call).descriptionAndTooltip(description);
}
return null;
}
static HighlightInfo.Builder checkParameterizedReferenceTypeArguments(@Nullable PsiElement resolved,
@NotNull PsiJavaCodeReferenceElement referenceElement,
@NotNull PsiSubstitutor substitutor,
@@ -169,7 +140,7 @@ public final class GenericsHighlightUtil {
.highPriority(QuickFixFactory.getInstance().createDeleteFix(referenceParameterList));
builder.registerFix(action, null, null, null, null);
}
registerVariableParameterizedTypeFixes(builder, (PsiVariable)variable, referenceParameterList, javaSdkVersion);
HighlightFixUtil.registerVariableParameterizedTypeFixes(HighlightUtil.asConsumer(builder), (PsiVariable)variable, referenceParameterList);
}
}
}
@@ -333,8 +304,8 @@ public final class GenericsHighlightUtil {
if (aClass instanceof PsiTypeParameter) {
HighlightInfo.Builder info =
HighlightMethodUtil.checkMethodIncompatibleReturnType(signature, signature.getSuperSignatures(), true,
HighlightNamesUtil.getClassDeclarationTextRange(aClass),
null);
HighlightNamesUtil.getClassDeclarationTextRange(aClass)
);
if (info != null) {
overrideEquivalentMethodsErrors.put(aClass, info);
}
@@ -1076,42 +1047,6 @@ public final class GenericsHighlightUtil {
return containingClass != null && PsiUtil.typeParametersIterator(containingClass).hasNext();
}
private static void registerVariableParameterizedTypeFixes(@Nullable HighlightInfo.Builder builder,
@NotNull PsiVariable variable,
@NotNull PsiReferenceParameterList parameterList,
@NotNull JavaSdkVersion version) {
PsiType type = variable.getType();
if (!(type instanceof PsiClassType classType) || builder == null) return;
if (DumbService.getInstance(variable.getProject()).isDumb()) return;
String shortName = classType.getClassName();
PsiManager manager = parameterList.getManager();
JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
PsiShortNamesCache shortNamesCache = PsiShortNamesCache.getInstance(parameterList.getProject());
PsiClass[] classes = shortNamesCache.getClassesByName(shortName, GlobalSearchScope.allScope(manager.getProject()));
PsiElementFactory factory = facade.getElementFactory();
for (PsiClass aClass : classes) {
if (aClass == null) {
LOG.error("null class returned for " + shortName);
continue;
}
if (checkReferenceTypeArgumentList(aClass, parameterList, PsiSubstitutor.EMPTY, false, version) == null) {
PsiType[] actualTypeParameters = parameterList.getTypeArguments();
PsiTypeParameter[] classTypeParameters = aClass.getTypeParameters();
Map<PsiTypeParameter, PsiType> map = new HashMap<>();
for (int j = 0; j < Math.min(classTypeParameters.length, actualTypeParameters.length); j++) {
PsiTypeParameter classTypeParameter = classTypeParameters[j];
PsiType actualTypeParameter = actualTypeParameters[j];
map.put(classTypeParameter, actualTypeParameter);
}
PsiSubstitutor substitutor = factory.createSubstitutor(map);
PsiType suggestedType = factory.createType(aClass, substitutor);
HighlightFixUtil.registerChangeVariableTypeFixes(variable, suggestedType, variable.getInitializer(), builder);
}
}
}
static HighlightInfo.Builder checkMemberSignatureTypesAccessibility(@NotNull PsiReferenceExpression ref) {
String message = null;
@@ -14,11 +14,16 @@ import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.lang.jvm.util.JvmUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
@@ -31,10 +36,7 @@ import com.siyeh.ig.psiutils.VariableAccessUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.function.Consumer;
public final class HighlightFixUtil {
@@ -713,7 +715,7 @@ public final class HighlightFixUtil {
}
}
static void registerCallInferenceFixes(@NotNull PsiMethodCallExpression callExpression, @NotNull Consumer<CommonIntentionAction> info) {
static void registerCallInferenceFixes(@NotNull PsiMethodCallExpression callExpression, @NotNull Consumer<? super CommonIntentionAction> info) {
JavaResolveResult result = callExpression.getMethodExpression().advancedResolve(true);
if (!(result instanceof MethodCandidateInfo resolveResult)) return;
PsiMethod method = resolveResult.getElement();
@@ -749,4 +751,41 @@ public final class HighlightFixUtil {
}
}
}
static void registerVariableParameterizedTypeFixes(@NotNull Consumer<? super CommonIntentionAction> info,
@NotNull PsiVariable variable,
@NotNull PsiReferenceParameterList parameterList) {
PsiType type = variable.getType();
if (!(type instanceof PsiClassType classType)) return;
if (DumbService.getInstance(variable.getProject()).isDumb()) return;
String shortName = classType.getClassName();
PsiFile file = parameterList.getContainingFile();
Project project = file.getProject();
JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
PsiShortNamesCache shortNamesCache = PsiShortNamesCache.getInstance(project);
PsiClass[] classes = shortNamesCache.getClassesByName(shortName, GlobalSearchScope.allScope(project));
PsiElementFactory factory = facade.getElementFactory();
JavaSdkVersion version = Objects.requireNonNullElse(JavaVersionService.getInstance().getJavaSdkVersion(file),
JavaSdkVersion.fromLanguageLevel(PsiUtil.getLanguageLevel(file)));
for (PsiClass aClass : classes) {
if (aClass == null) {
continue;
}
if (GenericsHighlightUtil.checkReferenceTypeArgumentList(aClass, parameterList, PsiSubstitutor.EMPTY, false, version) == null) {
PsiType[] actualTypeParameters = parameterList.getTypeArguments();
PsiTypeParameter[] classTypeParameters = aClass.getTypeParameters();
Map<PsiTypeParameter, PsiType> map = new HashMap<>();
for (int j = 0; j < Math.min(classTypeParameters.length, actualTypeParameters.length); j++) {
PsiTypeParameter classTypeParameter = classTypeParameters[j];
PsiType actualTypeParameter = actualTypeParameters[j];
map.put(classTypeParameter, actualTypeParameter);
}
PsiSubstitutor substitutor = factory.createSubstitutor(map);
PsiType suggestedType = factory.createType(aClass, substitutor);
registerChangeVariableTypeFixes(variable, suggestedType, variable.getInitializer(), info);
}
}
}
}
@@ -80,7 +80,7 @@ public final class HighlightMethodUtil {
static HighlightInfo.Builder checkMethodIncompatibleReturnType(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures,
boolean includeRealPositionInfo,
@Nullable TextRange textRange, @Nullable Ref<? super String> description) {
@Nullable TextRange textRange) {
PsiMethod method = methodSignature.getMethod();
PsiType returnType = methodSignature.getSubstitutor().substitute(method.getReturnType());
PsiClass aClass = method.getContainingClass();
@@ -104,7 +104,7 @@ public final class HighlightMethodUtil {
}
HighlightInfo.Builder info = checkSuperMethodSignature(
superMethod, superMethodSignature, superReturnType, method, methodSignature, returnType,
textRange, PsiUtil.getLanguageLevel(aClass), description);
textRange, PsiUtil.getLanguageLevel(aClass));
if (info != null) {
return info;
}
@@ -120,8 +120,7 @@ public final class HighlightMethodUtil {
@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull PsiType returnType,
@NotNull TextRange range,
@NotNull LanguageLevel languageLevel,
@Nullable Ref<? super String> description) {
@NotNull LanguageLevel languageLevel) {
PsiClass superContainingClass = superMethod.getContainingClass();
if (superContainingClass != null &&
CommonClassNames.JAVA_LANG_OBJECT.equals(superContainingClass.getQualifiedName()) &&
@@ -155,20 +154,17 @@ public final class HighlightMethodUtil {
}
return createIncompatibleReturnTypeMessage(method, superMethod, substitutedSuperReturnType, returnType,
JavaErrorBundle.message("incompatible.return.type"), range,
description);
JavaErrorBundle.message("incompatible.return.type"), range
);
}
private static @NotNull HighlightInfo.Builder createIncompatibleReturnTypeMessage(@NotNull PsiMethod method,
private static HighlightInfo.@NotNull Builder createIncompatibleReturnTypeMessage(@NotNull PsiMethod method,
@NotNull PsiMethod superMethod,
@NotNull PsiType substitutedSuperReturnType,
@NotNull PsiType returnType,
@NotNull @Nls String detailMessage,
@NotNull TextRange textRange, @Nullable Ref<? super String> descriptionH) {
@NotNull TextRange textRange) {
String description = MessageFormat.format("{0}; {1}", createClashMethodMessage(method, superMethod, true), detailMessage);
if (descriptionH != null) {
descriptionH.set(description);
}
HighlightInfo.Builder errorResult =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description);
if (method instanceof LightRecordMethod recordMethod) {
@@ -193,49 +189,6 @@ public final class HighlightMethodUtil {
return errorResult;
}
static void checkMethodCall(@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiResolveHelper resolveHelper,
@NotNull JavaSdkVersion javaSdkVersion,
@NotNull Consumer<? super HighlightInfo.Builder> errorSink) {
PsiExpressionList list = methodCall.getArgumentList();
PsiReferenceExpression referenceToMethod = methodCall.getMethodExpression();
JavaResolveResult[] results = referenceToMethod.multiResolve(true);
JavaResolveResult resolveResult = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
PsiElement resolved = resolveResult.getElement();
boolean isDummy = isDummyConstructorCall(methodCall, resolveHelper, list, referenceToMethod);
if (isDummy) return;
HighlightInfo.Builder builder = null;
PsiSubstitutor substitutor = resolveResult.getSubstitutor();
if (!(resolved instanceof PsiMethod) || !resolveResult.isValidResult()) {
MethodCandidateInfo candidateInfo = resolveResult instanceof MethodCandidateInfo ? (MethodCandidateInfo)resolveResult : null;
PsiMethod resolvedMethod = candidateInfo != null ? candidateInfo.getElement() : null;
if (resolveResult.isAccessible() && resolveResult.isStaticsScopeCorrect()) {
if (candidateInfo != null && !candidateInfo.isApplicable()) {
if (!candidateInfo.isTypeArgumentsApplicable()) {
PsiReferenceParameterList typeArgumentList = methodCall.getTypeArgumentList();
PsiSubstitutor applicabilitySubstitutor = candidateInfo.getSubstitutor(false);
if (typeArgumentList.getTypeArguments().length == 0 && resolvedMethod.hasTypeParameters()) {
builder = GenericsHighlightUtil.checkInferredTypeArguments(resolvedMethod, methodCall, applicabilitySubstitutor);
}
else {
builder = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(resolved, referenceToMethod, applicabilitySubstitutor, javaSdkVersion);
}
}
}
}
}
if (builder == null) {
builder = GenericsHighlightUtil.checkParameterizedReferenceTypeArguments(resolved, referenceToMethod, substitutor, javaSdkVersion);
}
if (builder != null) {
errorSink.accept(builder);
}
}
/**
* 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
@@ -770,15 +770,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
visitElement(expression);
if (!hasErrorResults()) {
try {
HighlightMethodUtil.checkMethodCall(expression, getResolveHelper(), myJavaSdkVersion,
myErrorSink);
}
catch (IndexNotReadyException ignored) {
}
}
if (!hasErrorResults()) add(HighlightMethodUtil.checkConstructorCallProblems(expression));
if (!hasErrorResults()) add(HighlightMethodUtil.checkSuperAbstractMethodDirectCall(expression));
@@ -23,6 +23,7 @@ import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.Service;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.impl.light.LightRecordMethod;
@@ -80,7 +81,7 @@ 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)) {
STATEMENT_CASE_OUTSIDE_SWITCH, NEW_EXPRESSION_DIAMOND_NOT_APPLICABLE)) {
fix(kind, genericRemover);
}
@@ -241,6 +242,40 @@ final class JavaErrorFixProvider {
fix(NEW_EXPRESSION_QUALIFIED_ANONYMOUS_IMPLEMENTS_INTERFACE, error -> myFactory.createRemoveNewQualifierFix(error.psi(), null));
fix(NEW_EXPRESSION_QUALIFIED_QUALIFIED_CLASS_REFERENCE,
error -> myFactory.createDeleteFix(error.psi(), QuickFixBundle.message("remove.qualifier.fix")));
fix(NEW_EXPRESSION_DIAMOND_INFERENCE_FAILURE,
error -> {
if (error.context() == PsiDiamondType.DiamondInferenceResult.ANONYMOUS_INNER_RESULT &&
!PsiUtil.isLanguageLevel9OrHigher(error.psi())) {
return myFactory.createIncreaseLanguageLevelFix(LanguageLevel.JDK_1_9);
}
return null;
});
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);
JavaFixProvider<PsiTypeElement, TypeParameterBoundMismatchContext> addBoundFix = error -> {
if (error.context().bound() instanceof PsiClassType bound) {
PsiClass psiClass = bound.resolve();
if (psiClass != null) {
return myFactory.createExtendsListFix(psiClass, bound, true);
}
}
return null;
};
fix(TYPE_PARAMETER_TYPE_NOT_WITHIN_EXTEND_BOUND, addBoundFix);
fix(TYPE_PARAMETER_TYPE_NOT_WITHIN_IMPLEMENT_BOUND, addBoundFix);
multi(TYPE_PARAMETER_ABSENT_CLASS, error -> {
PsiReferenceParameterList referenceParameterList = error.psi();
PsiElement grandParent = referenceParameterList.getParent().getParent();
if (!(grandParent instanceof PsiTypeElement)) return List.of();
if (!(PsiTreeUtil.skipParentsOfType(grandParent, PsiTypeElement.class) instanceof PsiVariable variable)) return List.of();
List<CommonIntentionAction> registrar = new ArrayList<>();
if (error.context().getTypeParameters().length == 0) {
registrar.add(PriorityIntentionActionWrapper.highPriority(myFactory.createDeleteFix(referenceParameterList)));
}
HighlightFixUtil.registerVariableParameterizedTypeFixes(registrar::add, variable, referenceParameterList);
return registrar;
});
fix(LITERAL_CHARACTER_TOO_LONG, error -> myFactory.createConvertToStringLiteralAction());
fix(LITERAL_CHARACTER_EMPTY, error -> myFactory.createConvertToStringLiteralAction());
fix(PATTERN_TYPE_PATTERN_EXPECTED, error -> {
@@ -12,8 +12,6 @@ annotation.not.allowed.in.permit.list=Annotations not allowed in 'permits' list
generics.holder.type=Type
generics.holder.method=Method
generics.inferred.type.for.type.parameter.is.not.within.its.bound.extend=Inferred type ''{2}'' for type parameter ''{0}'' is not within its bound; should extend ''{1}''
generics.inferred.type.for.type.parameter.is.not.within.its.bound.implement=Inferred type ''{2}'' for type parameter ''{0}'' is not within its bound; should implement ''{1}''
generics.type.parameter.is.not.within.its.bound.extend=Type parameter ''{0}'' is not within its bound; should extend ''{1}''
generics.type.parameter.is.not.within.its.bound.implement=Type parameter ''{0}'' is not within its bound; should implement ''{1}''