[java-highlighting] Do not allow unchecked conversion for explicit new expression types

Fixes IDEA-326353 Generics error is not displayed in new expression

GitOrigin-RevId: 661bf57ed3451096fcee245204dd30adae49e9e1
This commit is contained in:
Tagir Valeev
2025-03-20 18:59:14 +00:00
committed by intellij-monorepo-bot
parent 51407fc59f
commit cc8b90376c
4 changed files with 46 additions and 2 deletions
@@ -468,8 +468,17 @@ public final class GenericsUtil {
//Given a generic type declaration C<F1,...,Fn> (n > 0), the direct supertypes of the parameterized type C<R1,...,Rn> where at least one of the Ri is a wildcard
//type argument, are the direct supertypes of the parameterized type C<X1,...,Xn> which is the result of applying capture conversion to C<R1,...,Rn>.
PsiType capturedType = PsiUtil.captureToplevelWildcards(type, referenceParameterList);
//allow unchecked conversions in method calls but not in type declaration
return checkNotInBounds(capturedType, bound, PsiTreeUtil.getParentOfType(referenceParameterList, PsiCallExpression.class) != null);
//allow unchecked conversions in method calls, new expression args, or in diamond types, but not in other places
boolean uncheckedConversionByDefault;
PsiElement parent = referenceParameterList.getParent();
if (parent instanceof PsiReferenceExpression || parent instanceof PsiNewExpression) {
uncheckedConversionByDefault = true;
}
else {
PsiTypeElement[] elements = referenceParameterList.getTypeParameterElements();
uncheckedConversionByDefault = elements.length == 1 && elements[0].getType() instanceof PsiDiamondType;
}
return checkNotInBounds(capturedType, bound, uncheckedConversionByDefault);
}
public static boolean checkNotInBounds(PsiType type, PsiType bound, boolean uncheckedConversionByDefault) {
@@ -17,4 +17,12 @@ class EnumBug {
EnumSet<<error descr="Type parameter 'EnumBug.Option' is not within its bound; should extend 'EnumBug.Enum<EnumBug.Option>'">Option</error>> enumSetRaw = EnumSet.<Option>noneOf(Option.class);
}
void consume(Runnable r) {}
void test() {
EnumSet<<error descr="Type parameter 'EnumBug.Option' is not within its bound; should extend 'EnumBug.Enum<EnumBug.Option>'">Option</error>> set = null;
consume(() -> {
EnumSet<<error descr="Type parameter 'EnumBug.Option' is not within its bound; should extend 'EnumBug.Enum<EnumBug.Option>'">Option</error>> set1 = null;
});
}
}
@@ -0,0 +1,26 @@
class InvalidGenericSubclass {
public static void main(String[] args) {
BaseInput<?, ?> input = new BaseInput<<error descr="Type parameter 'InvalidGenericSubclass.BaseInput' is not within its bound; should extend 'InvalidGenericSubclass.BaseInput<InvalidGenericSubclass.BaseInput,InvalidGenericSubclass.BaseInput.BaseOutput>'">BaseInput</error>, BaseInput.BaseOutput>() {
};
BaseInput<?, ?> input2 = InvalidGenericSubclass.<BaseInput, BaseInput.BaseOutput>create();
new <BaseInput, BaseInput.BaseOutput>InvalidGenericSubclass();
}
interface Input<InputType extends Input<InputType, OutputType>, OutputType extends Input.Output<InputType, OutputType>> {
interface Output<OutputType extends Input<OutputType, InputType>, InputType extends Output<OutputType, InputType>> {
}
}
interface BaseInput<BaseInputType extends BaseInput<BaseInputType, BaseOutputType>, BaseOutputType extends BaseInput.BaseOutput<BaseInputType, BaseOutputType>> extends Input<BaseInputType, BaseOutputType> {
interface BaseOutput<BaseInputType extends BaseInput<BaseInputType, BaseOutputType>, BaseOutputType extends BaseOutput<BaseInputType, BaseOutputType>> extends Output<BaseInputType, BaseOutputType> {
}
}
static <BaseInputType extends BaseInput<BaseInputType, BaseOutputType>, BaseOutputType extends BaseInput.BaseOutput<BaseInputType, BaseOutputType>> BaseInput<BaseInputType, BaseOutputType> create() {
return null;
}
<BaseInputType extends BaseInput<BaseInputType, BaseOutputType>, BaseOutputType extends BaseInput.BaseOutput<BaseInputType, BaseOutputType>> InvalidGenericSubclass() {}
}
@@ -1204,4 +1204,5 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
public void testIgnoreErasureForProperTypeBound() { doTest(); }
public void testInferenceErrorAttribution() {doTest();}
public void testLocalClassParameters() {doTest();}
public void testRawAtFBoundAtNew() { doTest(); }
}