KT-14498: Properly check variance in expanded types.

This commit is contained in:
Dmitry Petrov
2016-10-27 16:28:34 +03:00
parent 49bf1e5917
commit ed60674d13
14 changed files with 234 additions and 59 deletions
@@ -118,6 +118,8 @@ public interface Errors {
DiagnosticFactory1<KtTypeProjection, ClassifierDescriptor> REDUNDANT_PROJECTION = DiagnosticFactory1.create(WARNING, VARIANCE_IN_PROJECTION);
DiagnosticFactory1<PsiElement, VarianceConflictDiagnosticData> TYPE_VARIANCE_CONFLICT =
DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory1<PsiElement, VarianceConflictDiagnosticData> TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE =
DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<PsiElement> FINITE_BOUNDS_VIOLATION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, String> FINITE_BOUNDS_VIOLATION_IN_JAVA = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<PsiElement> EXPANSIVE_INHERITANCE = DiagnosticFactory0.create(ERROR);
@@ -546,21 +546,25 @@ public class DefaultErrorMessages {
MAP.put(MISPLACED_TYPE_PARAMETER_CONSTRAINTS, "If a type parameter has multiple constraints, they all need to be placed in the 'where' clause");
MultiRenderer<VarianceConflictDiagnosticData> varianceConflictDataRenderer = new MultiRenderer<VarianceConflictDiagnosticData>() {
@NotNull
@Override
public String[] render(@NotNull VarianceConflictDiagnosticData data) {
RenderingContext context =
of(data.getTypeParameter(), data.getTypeParameter().getVariance(), data.getOccurrencePosition(),
data.getContainingType());
return new String[] {
NAME.render(data.getTypeParameter(), context),
RENDER_POSITION_VARIANCE.render(data.getTypeParameter().getVariance(), context),
RENDER_POSITION_VARIANCE.render(data.getOccurrencePosition(), context),
RENDER_TYPE.render(data.getContainingType(), context)
};
}
};
MAP.put(TYPE_VARIANCE_CONFLICT, "Type parameter {0} is declared as ''{1}'' but occurs in ''{2}'' position in type {3}",
new MultiRenderer<VarianceConflictDiagnosticData>() {
@NotNull
@Override
public String[] render(@NotNull VarianceConflictDiagnosticData data) {
RenderingContext context =
of(data.getTypeParameter(), data.getTypeParameter().getVariance(), data.getOccurrencePosition(), data.getContainingType());
return new String[] {
NAME.render(data.getTypeParameter(), context),
RENDER_POSITION_VARIANCE.render(data.getTypeParameter().getVariance(), context),
RENDER_POSITION_VARIANCE.render(data.getOccurrencePosition(), context),
RENDER_TYPE.render(data.getContainingType(), context)
};
}
});
varianceConflictDataRenderer);
MAP.put(TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE, "Type parameter {0} is declared as ''{1}'' but occurs in ''{2}'' position in abbreviated type {3}",
varianceConflictDataRenderer);
MAP.put(FINITE_BOUNDS_VIOLATION, "This type parameter violates the Finite Bound Restriction");
MAP.put(FINITE_BOUNDS_VIOLATION_IN_JAVA, "Violation of Finite Bound Restriction for {0}", STRING);
@@ -18,9 +18,7 @@ package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl
import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.*
@@ -28,20 +28,27 @@ import org.jetbrains.kotlin.types.*
interface TypeBinding<out P : PsiElement> : TypeHolder<TypeBinding<P>> {
val psiElement: P
val isInAbbreviation: Boolean
override val arguments: List<TypeArgumentBinding<P>?>
}
interface TypeArgumentBinding<out P: PsiElement> : TypeHolderArgument<TypeBinding<P>>
fun KtTypeReference.createTypeBinding(trace: BindingContext): TypeBinding<KtTypeElement>? {
val jetType = trace[BindingContext.TYPE, this]
val type = trace[BindingContext.TYPE, this]
val psiElement = typeElement
if (jetType == null || psiElement == null) {
return null
}
else {
return ExplicitTypeBinding(trace, psiElement, jetType)
}
return if (type == null || psiElement == null)
null
else
createTypeBindingFromPsi(trace, psiElement, type)
}
private fun createTypeBindingFromPsi(trace: BindingContext, psiElement: KtTypeElement, type: KotlinType): TypeBinding<KtTypeElement> {
val abbreviatedType = type.getAbbreviatedType()
return if (abbreviatedType != null)
AbbreviatedTypeBinding(type, psiElement)
else
ExplicitTypeBinding(trace, psiElement, type)
}
fun KtCallableDeclaration.createTypeBindingForReturnType(trace: BindingContext): TypeBinding<PsiElement>? {
@@ -65,30 +72,30 @@ private class ExplicitTypeBinding(
override val psiElement: KtTypeElement,
override val type: KotlinType
) : TypeBinding<KtTypeElement> {
override val isInAbbreviation: Boolean get() = false
override val arguments: List<TypeArgumentBinding<KtTypeElement>?>
get() {
val psiTypeArguments = psiElement.typeArgumentsAsTypes
assert(type.getAbbreviatedType() == null) { "Non-abbreviated type expected: $type" }
val isErrorBinding = run {
val sizeIsEqual = psiTypeArguments.size == type.arguments.size
&& psiTypeArguments.size == type.constructor.parameters.size
type.isError || !sizeIsEqual
}
return psiTypeArguments.indices.map { index: Int ->
// todo fix for List<*>
val jetTypeReference = psiTypeArguments[index]
val jetTypeElement = jetTypeReference?.typeElement
if (jetTypeElement == null) return@map null
return psiTypeArguments.indices.map { index: Int ->
// todo fix for List<*>
val jetTypeReference = psiTypeArguments[index]
val jetTypeElement = jetTypeReference?.typeElement ?: return@map null
if (isErrorBinding) {
val nextJetType = trace[BindingContext.TYPE, jetTypeReference]
if (nextJetType == null) return@map null
if (isErrorBinding) {
val nextJetType = trace[BindingContext.TYPE, jetTypeReference] ?: return@map null
return@map TypeArgumentBindingImpl(
TypeProjectionImpl(nextJetType),
null,
ExplicitTypeBinding(trace, jetTypeElement, nextJetType)
createTypeBindingFromPsi(trace, jetTypeElement, nextJetType)
)
}
@@ -96,17 +103,34 @@ private class ExplicitTypeBinding(
return@map TypeArgumentBindingImpl(
typeProjection,
type.constructor.parameters[index],
ExplicitTypeBinding(trace, jetTypeElement, typeProjection.type)
createTypeBindingFromPsi(trace, jetTypeElement, typeProjection.type)
)
}
}
}
private class AbbreviatedTypeBinding(
override val type: KotlinType,
override val psiElement: KtTypeElement
): TypeBinding<KtTypeElement> {
override val isInAbbreviation: Boolean get() = true
override val arguments: List<TypeArgumentBinding<KtTypeElement>?>
get() = type.arguments.mapIndexed { index, argument ->
TypeArgumentBindingImpl(
argument,
type.constructor.parameters[index],
AbbreviatedTypeBinding(argument.type, psiElement)
)
}
}
private class NoTypeElementBinding<out P : PsiElement>(
private val trace: BindingContext,
override val psiElement: P,
override val type: KotlinType
): TypeBinding<P> {
override val isInAbbreviation: Boolean get() = false
override val arguments: List<TypeArgumentBinding<P>?>
get() {
@@ -16,30 +16,23 @@
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure.EnrichedProjectionKind.*
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure.*
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.resolve.typeBinding.TypeBinding
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyAccessorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.resolve.typeBinding.TypeBinding
import org.jetbrains.kotlin.resolve.typeBinding.createTypeBinding
import org.jetbrains.kotlin.resolve.typeBinding.createTypeBindingForReturnType
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.Variance.*
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyAccessorDescriptorImpl
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checkTypePosition
import org.jetbrains.kotlin.types.getAbbreviatedType
class ManualVariance(val descriptor: TypeParameterDescriptor, val variance: Variance)
@@ -167,12 +160,9 @@ class VarianceCheckerCore(
checkTypePosition(
position,
{ typeParameterDescriptor, typeBinding, errorPosition ->
diagnosticSink.report(
Errors.TYPE_VARIANCE_CONFLICT.on(
typeBinding.psiElement,
VarianceConflictDiagnosticData(containingType, typeParameterDescriptor, errorPosition)
)
)
val varianceConflictDiagnosticData = VarianceConflictDiagnosticData(containingType, typeParameterDescriptor, errorPosition)
val diagnostic = if (typeBinding.isInAbbreviation) Errors.TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE else Errors.TYPE_VARIANCE_CONFLICT
diagnosticSink.report(diagnostic.on(typeBinding.psiElement, varianceConflictDiagnosticData))
},
customVariance = { it.varianceWithManual() }
)
@@ -0,0 +1,17 @@
// !DIAGNOSTICS: -UNUSED_TYPEALIAS_PARAMETER
interface I1<S>
interface Out<out T>
interface InvOut<T1, out T2>
typealias A1<S> = I1<S>
typealias A2<T, S> = I1<S>
typealias AOut<T> = Out<T>
typealias AInvOut<T1, T2> = InvOut<T1, T2>
typealias AInvOutTT<T> = AInvOut<T, T>
class Test1<out S> : <!TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE!>A1<S><!>
class Test2<out S> : <!TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE!>A2<Any, S><!>
class Test3<out S> : AOut<S>
class Test4<out S> : <!TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE!>AInvOut<S, S><!>
class Test5<out S> : <!TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE!>AInvOutTT<S><!>
@@ -0,0 +1,59 @@
package
public interface I1</*0*/ S> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface InvOut</*0*/ T1, /*1*/ out T2> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Out</*0*/ out T> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Test1</*0*/ out S> : A1<S> /* = I1<S> */ {
public constructor Test1</*0*/ out S>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Test2</*0*/ out S> : A2<kotlin.Any, S> /* = I1<S> */ {
public constructor Test2</*0*/ out S>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Test3</*0*/ out S> : AOut<S> /* = Out<out S> */ {
public constructor Test3</*0*/ out S>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Test4</*0*/ out S> : AInvOut<S, S> /* = InvOut<S, out S> */ {
public constructor Test4</*0*/ out S>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Test5</*0*/ out S> : AInvOutTT<S> /* = InvOut<S, out S> */ {
public constructor Test5</*0*/ out S>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public typealias A1</*0*/ S> = I1<S>
public typealias A2</*0*/ T, /*1*/ S> = I1<S>
public typealias AInvOut</*0*/ T1, /*1*/ T2> = InvOut<T1, T2>
public typealias AInvOutTT</*0*/ T> = AInvOut<T, T>
public typealias AOut</*0*/ T> = Out<T>
@@ -0,0 +1,11 @@
interface Out<out R>
interface Inv<E>
typealias A1<E> = Out<Out<E>>
typealias A2<E> = Out<Out<E>>
typealias A3<E> = Inv<Out<E>>
typealias A4<E> = Out<Inv<E>>
interface Q1<out S> : Out<A1<S>>
interface Q2<out S> : Out<A2<S>>
interface Q3<out S> : Out<<!TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE!>A3<S><!>>
interface Q4<out S> : Out<<!TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE!>A4<S><!>>
@@ -0,0 +1,41 @@
package
public interface Inv</*0*/ E> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Out</*0*/ out R> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Q1</*0*/ out S> : Out<A1<S> /* = Out<Out<out S>> */> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Q2</*0*/ out S> : Out<A2<S> /* = Out<Out<out S>> */> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Q3</*0*/ out S> : Out<A3<S> /* = Inv<Out<out S>> */> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Q4</*0*/ out S> : Out<A4<S> /* = Out<Inv<S>> */> {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public typealias A1</*0*/ E> = Out<Out<E>>
public typealias A2</*0*/ E> = Out<Out<E>>
public typealias A3</*0*/ E> = Inv<Out<E>>
public typealias A4</*0*/ E> = Out<Inv<E>>
@@ -20736,6 +20736,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("kt14498.kt")
public void testKt14498() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/kt14498.kt");
doTest(fileName);
}
@TestMetadata("kt14498a.kt")
public void testKt14498a() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/kt14498a.kt");
doTest(fileName);
}
@TestMetadata("localTypeAlias.kt")
public void testLocalTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/typealias/localTypeAlias.kt");
@@ -38,7 +38,6 @@ fun <D : TypeHolder<D>> D.checkTypePosition(
reportError: (TypeParameterDescriptor, D, Variance) -> Unit = DO_NOTHING_3,
customVariance: (TypeParameterDescriptor) -> Variance? = { null }
): Boolean {
flexibleBounds?.let {
return it.first.checkTypePosition(position, reportError, customVariance) and
it.second.checkTypePosition(position, reportError, customVariance)
@@ -0,0 +1,10 @@
// !DIAGNOSTICS_NUMBER: 1
// !DIAGNOSTICS: TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE
// !MESSAGE_TYPE: TEXT
interface InvOut<T1, out T2>
typealias AInvOut<T1, T2> = InvOut<T1, T2>
typealias AInvOutTT<T> = AInvOut<T, T>
class Test<out S> : AInvOutTT<S>
@@ -0,0 +1,2 @@
<!-- typeVarianceConflictInTypeAliasExpansion1 -->
Type parameter S is declared as 'out' but occurs in 'invariant' position in abbreviated type AInvOutTT<S> /* = InvOut<S, out S> */
@@ -275,6 +275,12 @@ public class DiagnosticMessageTestGenerated extends AbstractDiagnosticMessageTes
doTest(fileName);
}
@TestMetadata("typeVarianceConflictInTypeAliasExpansion.kt")
public void testTypeVarianceConflictInTypeAliasExpansion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/diagnosticMessage/typeVarianceConflictInTypeAliasExpansion.kt");
doTest(fileName);
}
@TestMetadata("unsupportedFeature.kt")
public void testUnsupportedFeature() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/diagnosticMessage/unsupportedFeature.kt");