Use upper bound checker for typealias expansion

This commit is contained in:
Victor Petukhov
2021-02-03 12:59:49 +03:00
parent edb8007d52
commit d783d99443
20 changed files with 306 additions and 140 deletions
@@ -202,7 +202,8 @@ class DeclarationsChecker(
private class TypeAliasDeclarationCheckingReportStrategy(
private val trace: BindingTrace,
typeAliasDescriptor: TypeAliasDescriptor,
declaration: KtTypeAlias
declaration: KtTypeAlias,
val upperBoundChecker: UpperBoundChecker
) : TypeAliasExpansionReportStrategy {
private val typeReference = declaration.getTypeReference()
?: throw AssertionError("Incorrect type alias declaration for $typeAliasDescriptor")
@@ -224,15 +225,12 @@ class DeclarationsChecker(
}
override fun boundsViolationInSubstitution(
bound: KotlinType,
substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType,
argument: KotlinType,
typeParameter: TypeParameterDescriptor
) {
// TODO more precise diagnostics
if (!argument.containsTypeAliasParameters() && !bound.containsTypeAliasParameters()) {
trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(typeReference, bound, argument, typeParameter))
}
upperBoundChecker.checkBounds(null, argument, typeParameter, substitutor, trace, typeReference)
}
override fun repeatedAnnotation(annotation: AnnotationDescriptor) {
@@ -243,7 +241,7 @@ class DeclarationsChecker(
private fun checkTypeAliasExpansion(declaration: KtTypeAlias, typeAliasDescriptor: TypeAliasDescriptor) {
val typeAliasExpansion = TypeAliasExpansion.createWithFormalArguments(typeAliasDescriptor)
val reportStrategy = TypeAliasDeclarationCheckingReportStrategy(trace, typeAliasDescriptor, declaration)
val reportStrategy = TypeAliasDeclarationCheckingReportStrategy(trace, typeAliasDescriptor, declaration, upperBoundChecker)
TypeAliasExpander(reportStrategy, true).expandWithoutAbbreviation(typeAliasExpansion, Annotations.EMPTY)
}
@@ -597,7 +597,8 @@ class TypeResolver(
c.trace,
type, typeAliasQualifierPart.typeArguments ?: typeAliasQualifierPart.expression,
descriptor, descriptor.declaredTypeParameters,
argumentElementsFromUserType // TODO arguments from inner scope
argumentElementsFromUserType, // TODO arguments from inner scope
upperBoundChecker
)
if (parameters.size != arguments.size) {
@@ -659,7 +660,8 @@ class TypeResolver(
val typeArgumentsOrTypeName: KtElement?,
val typeAliasDescriptor: TypeAliasDescriptor,
typeParameters: List<TypeParameterDescriptor>,
typeArguments: List<KtTypeProjection>
typeArguments: List<KtTypeProjection>,
val upperBoundChecker: UpperBoundChecker
) : TypeAliasExpansionReportStrategy {
private val mappedArguments = typeParameters.zip(typeArguments).toMap()
@@ -690,7 +692,7 @@ class TypeResolver(
}
override fun boundsViolationInSubstitution(
bound: KotlinType,
substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType,
argument: KotlinType,
typeParameter: TypeParameterDescriptor
@@ -698,11 +700,7 @@ class TypeResolver(
val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor
val argumentElement = mappedArguments[descriptorForUnsubstitutedArgument]
val argumentTypeReferenceElement = argumentElement?.typeReference
if (argumentTypeReferenceElement != null) {
trace.report(UPPER_BOUND_VIOLATED.on(argumentTypeReferenceElement, bound, argument))
} else if (type != null) {
trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(type, bound, argument, typeParameter))
}
upperBoundChecker.checkBounds(argumentTypeReferenceElement, argument, typeParameter, substitutor, trace, type)
}
override fun repeatedAnnotation(annotation: AnnotationDescriptor) {
@@ -7,17 +7,21 @@ package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory3
import org.jetbrains.kotlin.diagnostics.Errors.UPPER_BOUND_VIOLATED
import org.jetbrains.kotlin.diagnostics.Errors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.containsTypeAliasParameters
@DefaultImplementation(impl = UpperBoundChecker::class)
interface UpperBoundChecker {
val languageVersionSettings: LanguageVersionSettings
open class UpperBoundChecker(val languageVersionSettings: LanguageVersionSettings) {
fun checkBounds(typeReference: KtTypeReference, type: KotlinType, trace: BindingTrace) {
if (type.isError) return
@@ -54,30 +58,59 @@ interface UpperBoundChecker {
}
}
fun checkBounds(
jetTypeArgument: KtTypeReference,
typeArgument: KotlinType,
open fun checkBounds(
argumentReference: KtTypeReference?,
argumentType: KotlinType,
typeParameterDescriptor: TypeParameterDescriptor,
substitutor: TypeSubstitutor,
trace: BindingTrace
trace: BindingTrace,
typeAliasUsageElement: KtElement? = null,
) {
if (typeParameterDescriptor.upperBounds.isEmpty()) return
val diagnosticsReporter = UpperBoundViolatedReporter(trace, argumentType, typeParameterDescriptor)
for (bound in typeParameterDescriptor.upperBounds) {
checkBound(bound, substitutor, trace, jetTypeArgument, typeArgument)
checkBound(bound, argumentType, argumentReference, substitutor, typeAliasUsageElement, diagnosticsReporter)
}
}
fun checkBound(
protected fun checkBound(
bound: KotlinType,
argumentType: KotlinType,
argumentReference: KtTypeReference?,
substitutor: TypeSubstitutor,
trace: BindingTrace,
jetTypeArgument: KtTypeReference,
typeArgument: KotlinType
typeAliasUsageElement: KtElement? = null,
upperBoundViolatedReporter: UpperBoundViolatedReporter
): Boolean {
val substitutedBound = substitutor.safeSubstitute(bound, Variance.INVARIANT)
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) {
trace.report(Errors.UPPER_BOUND_VIOLATED.on(jetTypeArgument, substitutedBound, typeArgument))
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(argumentType, substitutedBound)) {
if (argumentReference != null) {
upperBoundViolatedReporter.report(argumentReference, substitutedBound)
} else if (typeAliasUsageElement != null && !substitutedBound.containsTypeAliasParameters() && !argumentType.containsTypeAliasParameters()) {
upperBoundViolatedReporter.reportForTypeAliasExpansion(typeAliasUsageElement, substitutedBound)
}
return false
}
return true
}
}
class UpperBoundViolatedReporter(
val trace: BindingTrace,
val argumentType: KotlinType,
val typeParameterDescriptor: TypeParameterDescriptor? = null,
val baseDiagnostic: DiagnosticFactory2<KtTypeReference, KotlinType, KotlinType> = UPPER_BOUND_VIOLATED,
val diagnosticForTypeAliases: DiagnosticFactory3<KtElement, KotlinType, KotlinType, ClassifierDescriptor> = UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION
) {
fun report(typeArgumentReference: KtTypeReference, substitutedBound: KotlinType) {
trace.report(baseDiagnostic.on(typeArgumentReference, substitutedBound, argumentType))
}
fun reportForTypeAliasExpansion(callElement: KtElement, substitutedBound: KotlinType) {
if (typeParameterDescriptor == null) return
trace.report(diagnosticForTypeAliases.on(callElement, substitutedBound, argumentType, typeParameterDescriptor))
}
}
@@ -606,7 +606,8 @@ class CandidateResolver(
private val callElement: KtElement,
typeAlias: TypeAliasDescriptor,
ktTypeArguments: List<KtTypeProjection>,
private val trace: BindingTrace
private val trace: BindingTrace,
private val upperBoundChecker: UpperBoundChecker
) : TypeAliasExpansionReportStrategy {
init {
assert(!typeAlias.expandedType.isError) { "Incorrect type alias: $typeAlias" }
@@ -635,7 +636,7 @@ class CandidateResolver(
}
override fun boundsViolationInSubstitution(
bound: KotlinType,
substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType,
argument: KotlinType,
typeParameter: TypeParameterDescriptor
@@ -643,11 +644,8 @@ class CandidateResolver(
val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor
val argumentElement = argumentsMapping[descriptorForUnsubstitutedArgument]
val argumentTypeReferenceElement = argumentElement?.typeReference
if (argumentTypeReferenceElement != null) {
trace.report(UPPER_BOUND_VIOLATED.on(argumentTypeReferenceElement, bound, argument))
} else {
trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(callElement, bound, argument, typeParameter))
}
upperBoundChecker.checkBounds(argumentTypeReferenceElement, argument, typeParameter, substitutor, trace, callElement)
}
}
@@ -665,7 +663,8 @@ class CandidateResolver(
val unsubstitutedType = typeAliasDescriptor.expandedType
if (unsubstitutedType.isError) return
val reportStrategy = TypeAliasSingleStepExpansionReportStrategy(call.callElement, typeAliasDescriptor, ktTypeArguments, trace)
val reportStrategy =
TypeAliasSingleStepExpansionReportStrategy(call.callElement, typeAliasDescriptor, ktTypeArguments, trace, upperBoundChecker)
// TODO refactor TypeResolver
// - perform full type alias expansion
@@ -694,12 +693,12 @@ class CandidateResolver(
val typeParameter = typeParameters[i]
val substitutedTypeArgument = substitutedTypeProjection.type
val unsubstitutedTypeArgument = unsubstitutedType.arguments[i].type
TypeAliasExpander.checkBoundsInTypeAlias(
reportStrategy,
reportStrategy.boundsViolationInSubstitution(
boundsSubstitutor,
unsubstitutedTypeArgument,
substitutedTypeArgument,
typeParameter,
boundsSubstitutor
typeParameter
)
checkTypeInTypeAliasSubstitutionRec(
@@ -15,6 +15,8 @@ import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isNull
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.UpperBoundChecker
import org.jetbrains.kotlin.resolve.UpperBoundViolatedReporter
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.reportTrailingLambdaErrorOr
@@ -393,8 +395,8 @@ class DiagnosticReporterByTrackingStrategy(
}
(position as? ExplicitTypeParameterConstraintPositionImpl)?.let {
val typeArgumentReference = (it.typeArgument as SimpleTypeArgumentImpl).typeReference
trace.report(UPPER_BOUND_VIOLATED.on(typeArgumentReference, error.upperKotlinType, error.lowerKotlinType))
UpperBoundViolatedReporter(trace, error.upperKotlinType)
.report((it.typeArgument as SimpleTypeArgumentImpl).typeReference, error.lowerKotlinType)
}
(position as? FixVariableConstraintPositionImpl)?.let {
@@ -84,6 +84,7 @@ class KotlinToResolvedCallTransformer(
private val smartCastManager: SmartCastManager,
private val typeApproximator: TypeApproximator,
private val missingSupertypesResolver: MissingSupertypesResolver,
private val upperBoundChecker: UpperBoundChecker,
) {
companion object {
private val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC