IDEA-386630 [java-highlighting] Failed bounds check of type parameters on constructor calls leads to an error on the wrong part

GitOrigin-RevId: 06e1223d74cc9e040245cffe33b66a42ba75ec7f
This commit is contained in:
7410
2026-03-26 22:27:35 +00:00
committed by intellij-monorepo-bot
parent 3077e0155e
commit bfaf4a4a18
4 changed files with 77 additions and 25 deletions
@@ -32,6 +32,7 @@ import com.intellij.psi.PsiArrayType;
import com.intellij.psi.PsiAssignmentExpression;
import com.intellij.psi.PsiBreakStatement;
import com.intellij.psi.PsiCall;
import com.intellij.psi.PsiCallExpression;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassObjectAccessExpression;
import com.intellij.psi.PsiClassType;
@@ -424,7 +425,7 @@ final class ExpressionChecker {
}
void checkInferredTypeArguments(@NotNull PsiTypeParameterListOwner listOwner,
@NotNull PsiMethodCallExpression call,
@NotNull PsiCallExpression call,
@NotNull PsiSubstitutor substitutor) {
PsiTypeParameter[] typeParameters = listOwner.getTypeParameters();
Pair<PsiTypeParameter, PsiType> inferredTypeArgument = GenericsUtil.findTypeParameterWithBoundError(
@@ -1268,24 +1269,15 @@ final class ExpressionChecker {
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();
}
if (staticFactory instanceof MethodCandidateInfo info && info.isApplicable()) {
result = info;
if (constructor == null) {
constructor = info.getElement();
}
else {
applicable = false;
}
}
else {
applicable = result != null && result.isApplicable();
}
}
catch (IndexNotReadyException ignored) {
@@ -1300,24 +1292,39 @@ final class ExpressionChecker {
constructorCall, new JavaErrorKinds.UnresolvedConstructorContext(aClass, results)));
return;
}
if (constructorCall instanceof PsiNewExpression newExpression) {
PsiReferenceParameterList typeArgumentList = newExpression.getTypeArgumentList();
myVisitor.myGenericsChecker.checkReferenceTypeArgumentList(constructor, typeArgumentList, result.getSubstitutor());
}
if (classReference != null && !constructor.isDefaultConstructor() &&
(!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;
if (result.isApplicable()) {
checkVarargParameterErasureToBeAccessible(result, constructorCall);
if (myVisitor.hasErrorResults()) return;
checkIncompatibleType(constructorCall, result, constructorCall);
}
else if (result.isTypeArgumentsApplicable()) {
checkIncompatibleCall(list, result);
}
else if (constructorCall instanceof PsiNewExpression newExpression) {
PsiReferenceParameterList typeArgumentList = newExpression.getTypeArgumentList();
myVisitor.myGenericsChecker.checkReferenceTypeArgumentList(constructor, typeArgumentList, result.getSubstitutor());
if (myVisitor.hasErrorResults()) return;
PsiSubstitutor applicabilitySubstitutor = result.getSubstitutor(false);
if (typeArgumentList.getTypeArguments().length == 0 && constructor.hasTypeParameters()) {
checkInferredTypeArguments(constructor, newExpression, applicabilitySubstitutor);
}
else {
myVisitor.myGenericsChecker.checkReferenceTypeArgumentList(constructor, typeArgumentList, applicabilitySubstitutor);
}
}
checkVarargParameterErasureToBeAccessible(result, constructorCall);
if (myVisitor.hasErrorResults()) return;
checkIncompatibleType(constructorCall, result, constructorCall);
}
private static boolean callingProtectedConstructorFromDerivedClass(@NotNull PsiConstructorCall place,
@@ -33,6 +33,7 @@ import com.intellij.psi.PsiAssertStatement;
import com.intellij.psi.PsiAssignmentExpression;
import com.intellij.psi.PsiBreakStatement;
import com.intellij.psi.PsiCall;
import com.intellij.psi.PsiCallExpression;
import com.intellij.psi.PsiCaseLabelElement;
import com.intellij.psi.PsiCaseLabelElementList;
import com.intellij.psi.PsiCatchSection;
@@ -730,15 +731,15 @@ public final class JavaErrorKinds {
parameterized(PsiMethodCallExpression.class, IncompatibleIntersectionContext.class, "type.parameter.incompatible.upper.bounds")
.withRange((call, ctx) -> getRange(call))
.withDescription((call, ctx) -> message("type.parameter.incompatible.upper.bounds", ctx.parameter().getName(), ctx.message()));
public static final Parameterized<PsiMethodCallExpression, TypeParameterBoundMismatchContext>
public static final Parameterized<PsiCallExpression, TypeParameterBoundMismatchContext>
TYPE_PARAMETER_INFERRED_TYPE_NOT_WITHIN_EXTEND_BOUND =
parameterized(PsiMethodCallExpression.class, TypeParameterBoundMismatchContext.class,
parameterized(PsiCallExpression.class, TypeParameterBoundMismatchContext.class,
"type.parameter.inferred.type.not.within.extend.bound")
.withDescription((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>
public static final Parameterized<PsiCallExpression, TypeParameterBoundMismatchContext>
TYPE_PARAMETER_INFERRED_TYPE_NOT_WITHIN_IMPLEMENT_BOUND =
parameterized(PsiMethodCallExpression.class, TypeParameterBoundMismatchContext.class,
parameterized(PsiCallExpression.class, TypeParameterBoundMismatchContext.class,
"type.parameter.inferred.type.not.within.implement.bound")
.withDescription((call, ctx) -> message("type.parameter.inferred.type.not.within.implement.bound", formatClass(ctx.parameter()),
formatType(ctx.bound()), formatType(ctx.actualType())));
@@ -0,0 +1,41 @@
import java.util.function.Function;
class A {
private <T extends Number> A(T t) {}
private static <T extends Number> void use(T t) {}
static {
new <<error descr="Type parameter 'java.lang.String' is not within its bound; should extend 'java.lang.Number'">String</error>>A();
A.<<error descr="Type parameter 'java.lang.String' is not within its bound; should extend 'java.lang.Number'">String</error>>use();
}
}
class B {
static {
new <error descr="Wrong number of type arguments: 2; required: 1">< String , Number ></error> <error descr="'A(java.lang.String)' has private access in 'A'">A</error> ( ) ;
A . <error descr="Wrong number of type arguments: 2; required: 1">< String , Number ></error> <error descr="'use(java.lang.String)' has private access in 'A'">use</error> ( ) ;
}
}
class C {
static {
<error descr="Inferred type 'B' for type parameter 'B' is not within its bound; should extend 'C.Builder<C.Alfa>'">new C(C::string)</error>;
<error descr="Inferred type 'B' for type parameter 'B' is not within its bound; should extend 'C.Builder<C.Alfa>'">C.use(C::string)</error>;
}
private static <T extends Alfa, B extends Builder<T>> void use(final Function<B, String> f1) {}
private <T extends Alfa, B extends Builder<T>> C(final Function<B, String> f1) {}
private static String string(final Builder<? extends Beta> builder) {
return "";
}
static class Alfa {}
static class Beta extends Alfa {}
static class Builder<T> {}
}
@@ -1213,4 +1213,7 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
@TestFor(issues = "IDEA-385574")
public void testIDEA385574(){ doTest(); }
@TestFor(issues = "IDEA-386630")
public void testIDEA386630(){ doTest(); }
}