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() }
)