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
@@ -7,40 +7,49 @@ package org.jetbrains.kotlin.resolve.jvm.checkers
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.UpperBoundChecker import org.jetbrains.kotlin.resolve.UpperBoundChecker
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.resolve.UpperBoundViolatedReporter
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
class EnhancedUpperBoundChecker(override val languageVersionSettings: LanguageVersionSettings) : UpperBoundChecker { // TODO: remove this checker after removing support LV < 1.6
override fun checkBound( class EnhancedUpperBoundChecker(languageVersionSettings: LanguageVersionSettings) : UpperBoundChecker(languageVersionSettings) {
bound: KotlinType, val isTypeEnhancementImprovementsEnabled = languageVersionSettings.supportsFeature(LanguageFeature.ImprovementsAroundTypeEnhancement)
override fun checkBounds(
argumentReference: KtTypeReference?,
argumentType: KotlinType,
typeParameterDescriptor: TypeParameterDescriptor,
substitutor: TypeSubstitutor, substitutor: TypeSubstitutor,
trace: BindingTrace, trace: BindingTrace,
jetTypeArgument: KtTypeReference, typeAliasUsageElement: KtElement?
typeArgument: KotlinType ) {
): Boolean { if (typeParameterDescriptor.upperBounds.isEmpty()) return
val isCheckPassed = super.checkBound(bound, substitutor, trace, jetTypeArgument, typeArgument)
// The error is already reported, it's unnecessary to do more checks val diagnosticsReporter = UpperBoundViolatedReporter(trace, argumentType, typeParameterDescriptor)
if (!isCheckPassed) return false val diagnosticsReporterForWarnings = UpperBoundViolatedReporter(
trace, argumentType, typeParameterDescriptor,
baseDiagnostic = UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS,
diagnosticForTypeAliases = UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS
)
val enhancedBound = bound.getEnhancement() ?: return false for (bound in typeParameterDescriptor.upperBounds) {
val isCheckPassed = checkBound(bound, argumentType, argumentReference, substitutor, typeAliasUsageElement, diagnosticsReporter)
val isTypeEnhancementImprovementsEnabled = // The error is already reported, it's unnecessary to do more checks
languageVersionSettings.supportsFeature(LanguageFeature.ImprovementsAroundTypeEnhancement) if (!isCheckPassed) continue
val substitutedBound = substitutor.safeSubstitute(enhancedBound, Variance.INVARIANT)
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) { // If improvements are enabled, then type parameter's upper bounds will already enhanced, and the error will reported inside the first check
if (isTypeEnhancementImprovementsEnabled) { if (isTypeEnhancementImprovementsEnabled) continue
trace.report(Errors.UPPER_BOUND_VIOLATED.on(jetTypeArgument, substitutedBound, typeArgument))
} else { val enhancedBound = bound.getEnhancementDeeply() ?: continue
trace.report(ErrorsJvm.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS.on(jetTypeArgument, substitutedBound, typeArgument))
} checkBound(enhancedBound, argumentType, argumentReference, substitutor, typeAliasUsageElement, diagnosticsReporterForWarnings)
return false
} }
return true
} }
} }
@@ -215,9 +215,9 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
if (!doesExpectedTypeContainsEnhancement && !doesExpressionTypeContainsEnhancement) return if (!doesExpectedTypeContainsEnhancement && !doesExpressionTypeContainsEnhancement) return
val enhancedExpectedType = if (doesExpectedTypeContainsEnhancement) buildTypeWithEnhancement(expectedType) else expectedType val enhancedExpectedType = if (doesExpectedTypeContainsEnhancement) expectedType.unwrapEnhancementDeeply() else expectedType
val enhancedExpressionType = enhanceExpressionTypeByDataFlowNullability( val enhancedExpressionType = enhanceExpressionTypeByDataFlowNullability(
if (doesExpressionTypeContainsEnhancement) buildTypeWithEnhancement(expressionType) else expressionType, if (doesExpressionTypeContainsEnhancement) expressionType.unwrapEnhancementDeeply() else expressionType,
expressionTypeDataFlowValue, expressionTypeDataFlowValue,
dataFlowInfo dataFlowInfo
) )
@@ -253,33 +253,6 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
} else { } else {
null null
} }
private fun enhanceTypeArguments(arguments: List<TypeProjection>) =
arguments.map { argument ->
// TODO: think about star projections with enhancement (e.g. came from Java: Foo<@NotNull ?>)
if (argument.isStarProjection) {
return@map argument
}
val argumentType = argument.type
val enhancedArgumentType = if (argumentType is TypeWithEnhancement) argumentType.enhancement else argumentType
val enhancedDeeplyArgumentType = buildTypeWithEnhancement(enhancedArgumentType)
argument.replaceType(enhancedDeeplyArgumentType)
}
fun buildTypeWithEnhancement(type: KotlinType): KotlinType {
val newArguments = enhanceTypeArguments(type.arguments)
val newArgumentsForUpperBound =
if (type is FlexibleType) {
enhanceTypeArguments(type.upperBound.arguments)
} else newArguments
val enhancedType = if (type is TypeWithEnhancement) type.enhancement else type
return enhancedType.replace(
newArguments = newArguments,
newArgumentsForUpperBound = newArgumentsForUpperBound
)
}
} }
class EnhancedNullabilityInfo(val enhancedType: KotlinType, val isFromJava: Boolean) { class EnhancedNullabilityInfo(val enhancedType: KotlinType, val isFromJava: Boolean) {
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.utils.StringsKt;
import java.util.List; import java.util.List;
import static kotlin.collections.CollectionsKt.*; import static kotlin.collections.CollectionsKt.*;
import static org.jetbrains.kotlin.diagnostics.Errors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION;
import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.*; import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.*;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.*; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.*;
@@ -86,6 +87,10 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, "Type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE); MAP.put(NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, "Type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE);
MAP.put(UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS, "Type argument is not within its bounds: should be subtype of ''{0}''", RENDER_TYPE, RENDER_TYPE); MAP.put(UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS, "Type argument is not within its bounds: should be subtype of ''{0}''", RENDER_TYPE, RENDER_TYPE);
MAP.put(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS,
"Type argument resulting from type alias expansion is not within required bounds for ''{2}'': " +
"should be subtype of ''{0}'', substituted type is ''{1}''",
RENDER_TYPE, RENDER_TYPE, NAME);
MAP.put(NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER, MAP.put(NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER,
"Type mismatch: value of a nullable type {0} is used where non-nullable type is expected. " + "Type mismatch: value of a nullable type {0} is used where non-nullable type is expected. " +
"This warning will become an error soon. " + "This warning will become an error soon. " +
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.jvm.diagnostics;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.diagnostics.*; import org.jetbrains.kotlin.diagnostics.*;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
@@ -140,6 +141,9 @@ public interface ErrorsJvm {
DiagnosticFactory2<KtTypeReference, KotlinType, KotlinType> UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS DiagnosticFactory2<KtTypeReference, KotlinType, KotlinType> UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS
= DiagnosticFactory2.create(WARNING); = DiagnosticFactory2.create(WARNING);
DiagnosticFactory3<KtElement, KotlinType, KotlinType, ClassifierDescriptor> UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS
= DiagnosticFactory3.create(WARNING);
DiagnosticFactory1<KtElement, KotlinType> NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER DiagnosticFactory1<KtElement, KotlinType> NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER
= DiagnosticFactory1.create(WARNING); = DiagnosticFactory1.create(WARNING);
@@ -202,7 +202,8 @@ class DeclarationsChecker(
private class TypeAliasDeclarationCheckingReportStrategy( private class TypeAliasDeclarationCheckingReportStrategy(
private val trace: BindingTrace, private val trace: BindingTrace,
typeAliasDescriptor: TypeAliasDescriptor, typeAliasDescriptor: TypeAliasDescriptor,
declaration: KtTypeAlias declaration: KtTypeAlias,
val upperBoundChecker: UpperBoundChecker
) : TypeAliasExpansionReportStrategy { ) : TypeAliasExpansionReportStrategy {
private val typeReference = declaration.getTypeReference() private val typeReference = declaration.getTypeReference()
?: throw AssertionError("Incorrect type alias declaration for $typeAliasDescriptor") ?: throw AssertionError("Incorrect type alias declaration for $typeAliasDescriptor")
@@ -224,15 +225,12 @@ class DeclarationsChecker(
} }
override fun boundsViolationInSubstitution( override fun boundsViolationInSubstitution(
bound: KotlinType, substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType, unsubstitutedArgument: KotlinType,
argument: KotlinType, argument: KotlinType,
typeParameter: TypeParameterDescriptor typeParameter: TypeParameterDescriptor
) { ) {
// TODO more precise diagnostics upperBoundChecker.checkBounds(null, argument, typeParameter, substitutor, trace, typeReference)
if (!argument.containsTypeAliasParameters() && !bound.containsTypeAliasParameters()) {
trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(typeReference, bound, argument, typeParameter))
}
} }
override fun repeatedAnnotation(annotation: AnnotationDescriptor) { override fun repeatedAnnotation(annotation: AnnotationDescriptor) {
@@ -243,7 +241,7 @@ class DeclarationsChecker(
private fun checkTypeAliasExpansion(declaration: KtTypeAlias, typeAliasDescriptor: TypeAliasDescriptor) { private fun checkTypeAliasExpansion(declaration: KtTypeAlias, typeAliasDescriptor: TypeAliasDescriptor) {
val typeAliasExpansion = TypeAliasExpansion.createWithFormalArguments(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) TypeAliasExpander(reportStrategy, true).expandWithoutAbbreviation(typeAliasExpansion, Annotations.EMPTY)
} }
@@ -597,7 +597,8 @@ class TypeResolver(
c.trace, c.trace,
type, typeAliasQualifierPart.typeArguments ?: typeAliasQualifierPart.expression, type, typeAliasQualifierPart.typeArguments ?: typeAliasQualifierPart.expression,
descriptor, descriptor.declaredTypeParameters, descriptor, descriptor.declaredTypeParameters,
argumentElementsFromUserType // TODO arguments from inner scope argumentElementsFromUserType, // TODO arguments from inner scope
upperBoundChecker
) )
if (parameters.size != arguments.size) { if (parameters.size != arguments.size) {
@@ -659,7 +660,8 @@ class TypeResolver(
val typeArgumentsOrTypeName: KtElement?, val typeArgumentsOrTypeName: KtElement?,
val typeAliasDescriptor: TypeAliasDescriptor, val typeAliasDescriptor: TypeAliasDescriptor,
typeParameters: List<TypeParameterDescriptor>, typeParameters: List<TypeParameterDescriptor>,
typeArguments: List<KtTypeProjection> typeArguments: List<KtTypeProjection>,
val upperBoundChecker: UpperBoundChecker
) : TypeAliasExpansionReportStrategy { ) : TypeAliasExpansionReportStrategy {
private val mappedArguments = typeParameters.zip(typeArguments).toMap() private val mappedArguments = typeParameters.zip(typeArguments).toMap()
@@ -690,7 +692,7 @@ class TypeResolver(
} }
override fun boundsViolationInSubstitution( override fun boundsViolationInSubstitution(
bound: KotlinType, substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType, unsubstitutedArgument: KotlinType,
argument: KotlinType, argument: KotlinType,
typeParameter: TypeParameterDescriptor typeParameter: TypeParameterDescriptor
@@ -698,11 +700,7 @@ class TypeResolver(
val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor
val argumentElement = mappedArguments[descriptorForUnsubstitutedArgument] val argumentElement = mappedArguments[descriptorForUnsubstitutedArgument]
val argumentTypeReferenceElement = argumentElement?.typeReference val argumentTypeReferenceElement = argumentElement?.typeReference
if (argumentTypeReferenceElement != null) { upperBoundChecker.checkBounds(argumentTypeReferenceElement, argument, typeParameter, substitutor, trace, type)
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))
}
} }
override fun repeatedAnnotation(annotation: AnnotationDescriptor) { override fun repeatedAnnotation(annotation: AnnotationDescriptor) {
@@ -7,17 +7,21 @@ package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor 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.KtTypeReference
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.containsTypeAliasParameters
@DefaultImplementation(impl = UpperBoundChecker::class) @DefaultImplementation(impl = UpperBoundChecker::class)
interface UpperBoundChecker { open class UpperBoundChecker(val languageVersionSettings: LanguageVersionSettings) {
val languageVersionSettings: LanguageVersionSettings
fun checkBounds(typeReference: KtTypeReference, type: KotlinType, trace: BindingTrace) { fun checkBounds(typeReference: KtTypeReference, type: KotlinType, trace: BindingTrace) {
if (type.isError) return if (type.isError) return
@@ -54,30 +58,59 @@ interface UpperBoundChecker {
} }
} }
fun checkBounds( open fun checkBounds(
jetTypeArgument: KtTypeReference, argumentReference: KtTypeReference?,
typeArgument: KotlinType, argumentType: KotlinType,
typeParameterDescriptor: TypeParameterDescriptor, typeParameterDescriptor: TypeParameterDescriptor,
substitutor: TypeSubstitutor, 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) { 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, bound: KotlinType,
argumentType: KotlinType,
argumentReference: KtTypeReference?,
substitutor: TypeSubstitutor, substitutor: TypeSubstitutor,
trace: BindingTrace, typeAliasUsageElement: KtElement? = null,
jetTypeArgument: KtTypeReference, upperBoundViolatedReporter: UpperBoundViolatedReporter
typeArgument: KotlinType
): Boolean { ): Boolean {
val substitutedBound = substitutor.safeSubstitute(bound, Variance.INVARIANT) 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 false
} }
return true 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, private val callElement: KtElement,
typeAlias: TypeAliasDescriptor, typeAlias: TypeAliasDescriptor,
ktTypeArguments: List<KtTypeProjection>, ktTypeArguments: List<KtTypeProjection>,
private val trace: BindingTrace private val trace: BindingTrace,
private val upperBoundChecker: UpperBoundChecker
) : TypeAliasExpansionReportStrategy { ) : TypeAliasExpansionReportStrategy {
init { init {
assert(!typeAlias.expandedType.isError) { "Incorrect type alias: $typeAlias" } assert(!typeAlias.expandedType.isError) { "Incorrect type alias: $typeAlias" }
@@ -635,7 +636,7 @@ class CandidateResolver(
} }
override fun boundsViolationInSubstitution( override fun boundsViolationInSubstitution(
bound: KotlinType, substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType, unsubstitutedArgument: KotlinType,
argument: KotlinType, argument: KotlinType,
typeParameter: TypeParameterDescriptor typeParameter: TypeParameterDescriptor
@@ -643,11 +644,8 @@ class CandidateResolver(
val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor val descriptorForUnsubstitutedArgument = unsubstitutedArgument.constructor.declarationDescriptor
val argumentElement = argumentsMapping[descriptorForUnsubstitutedArgument] val argumentElement = argumentsMapping[descriptorForUnsubstitutedArgument]
val argumentTypeReferenceElement = argumentElement?.typeReference val argumentTypeReferenceElement = argumentElement?.typeReference
if (argumentTypeReferenceElement != null) {
trace.report(UPPER_BOUND_VIOLATED.on(argumentTypeReferenceElement, bound, argument)) upperBoundChecker.checkBounds(argumentTypeReferenceElement, argument, typeParameter, substitutor, trace, callElement)
} else {
trace.report(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION.on(callElement, bound, argument, typeParameter))
}
} }
} }
@@ -665,7 +663,8 @@ class CandidateResolver(
val unsubstitutedType = typeAliasDescriptor.expandedType val unsubstitutedType = typeAliasDescriptor.expandedType
if (unsubstitutedType.isError) return if (unsubstitutedType.isError) return
val reportStrategy = TypeAliasSingleStepExpansionReportStrategy(call.callElement, typeAliasDescriptor, ktTypeArguments, trace) val reportStrategy =
TypeAliasSingleStepExpansionReportStrategy(call.callElement, typeAliasDescriptor, ktTypeArguments, trace, upperBoundChecker)
// TODO refactor TypeResolver // TODO refactor TypeResolver
// - perform full type alias expansion // - perform full type alias expansion
@@ -694,12 +693,12 @@ class CandidateResolver(
val typeParameter = typeParameters[i] val typeParameter = typeParameters[i]
val substitutedTypeArgument = substitutedTypeProjection.type val substitutedTypeArgument = substitutedTypeProjection.type
val unsubstitutedTypeArgument = unsubstitutedType.arguments[i].type val unsubstitutedTypeArgument = unsubstitutedType.arguments[i].type
TypeAliasExpander.checkBoundsInTypeAlias(
reportStrategy, reportStrategy.boundsViolationInSubstitution(
boundsSubstitutor,
unsubstitutedTypeArgument, unsubstitutedTypeArgument,
substitutedTypeArgument, substitutedTypeArgument,
typeParameter, typeParameter
boundsSubstitutor
) )
checkTypeInTypeAliasSubstitutionRec( checkTypeInTypeAliasSubstitutionRec(
@@ -15,6 +15,8 @@ import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isNull import org.jetbrains.kotlin.psi.psiUtil.isNull
import org.jetbrains.kotlin.resolve.BindingContext 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.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.callUtil.reportTrailingLambdaErrorOr import org.jetbrains.kotlin.resolve.calls.callUtil.reportTrailingLambdaErrorOr
@@ -393,8 +395,8 @@ class DiagnosticReporterByTrackingStrategy(
} }
(position as? ExplicitTypeParameterConstraintPositionImpl)?.let { (position as? ExplicitTypeParameterConstraintPositionImpl)?.let {
val typeArgumentReference = (it.typeArgument as SimpleTypeArgumentImpl).typeReference UpperBoundViolatedReporter(trace, error.upperKotlinType)
trace.report(UPPER_BOUND_VIOLATED.on(typeArgumentReference, error.upperKotlinType, error.lowerKotlinType)) .report((it.typeArgument as SimpleTypeArgumentImpl).typeReference, error.lowerKotlinType)
} }
(position as? FixVariableConstraintPositionImpl)?.let { (position as? FixVariableConstraintPositionImpl)?.let {
@@ -84,6 +84,7 @@ class KotlinToResolvedCallTransformer(
private val smartCastManager: SmartCastManager, private val smartCastManager: SmartCastManager,
private val typeApproximator: TypeApproximator, private val typeApproximator: TypeApproximator,
private val missingSupertypesResolver: MissingSupertypesResolver, private val missingSupertypesResolver: MissingSupertypesResolver,
private val upperBoundChecker: UpperBoundChecker,
) { ) {
companion object { companion object {
private val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC private val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC
@@ -1,11 +1,36 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
// FULL_JDK // FULL_JDK
// FILE: MapLike.java // FILE: MapLike.java
import java.util.Map; import java.util.Map;
public interface MapLike<@org.jetbrains.annotations.NotNull K, V> { public class MapLike<@org.jetbrains.annotations.NotNull K, V> {
void putAll(Map<K, V> map); void putAll(Map<K, V> map);
} }
// FILE: ListLike.java
import java.util.Collection;
public class ListLike<K extends Collection<@org.jetbrains.annotations.NotNull Object>> {}
// FILE: main.kt // FILE: main.kt
fun test(map : MapLike<Int?, Int>) {} fun test0(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Int?<!>, Int>) {}
fun <K> test11(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>K<!>, K>) {}
fun <K> test12(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>K?<!>, K>) {}
fun <K : Any> test13(map : MapLike<K, K>) {}
fun <K : Any> test14(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>K?<!>, K>) {}
class Foo<K>
typealias A<A> = MapLike<A, Int>
typealias A2<B> = Foo<MapLike<B, Int>>
typealias A3<C> = ListLike<List<C>>
fun main1(x: A<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Int?<!>>) {}
fun main2(x: A2<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Int?<!>>) {}
fun main3(x: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS!>A3<Int?><!>) {}
fun main3() {
val x = A3<Int?>() // TODO: support reporting errors on typealias constructor calls
val x2 = A<Int?>() // TODO: support reporting errors on typealias constructor calls
val y: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS!>A3<Int?><!> = A3<Int?>()
}
@@ -1,16 +1,36 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
// FULL_JDK // FULL_JDK
// FILE: MapLike.java // FILE: MapLike.java
import java.util.Map; import java.util.Map;
public interface MapLike<@org.jetbrains.annotations.NotNull K, V> { public class MapLike<@org.jetbrains.annotations.NotNull K, V> {
void putAll(Map<K, V> map); void putAll(Map<K, V> map);
} }
// FILE: ListLike.java
import java.util.Collection;
public class ListLike<K extends Collection<@org.jetbrains.annotations.NotNull Object>> {}
// FILE: main.kt // FILE: main.kt
fun test0(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Int?<!>, Int>) {} fun test0(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Int?<!>, Int>) {}
fun <K> test11(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>K<!>, K>) {} fun <K> test11(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>K<!>, K>) {}
fun <K> test12(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>K?<!>, K>) {} fun <K> test12(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>K?<!>, K>) {}
fun <K : Any> test13(map : MapLike<K, K>) {} fun <K : Any> test13(map : MapLike<K, K>) {}
fun <K : Any> test14(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>K?<!>, K>) {} fun <K : Any> test14(map : MapLike<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>K?<!>, K>) {}
class Foo<K>
typealias A<A> = MapLike<A, Int>
typealias A2<B> = Foo<MapLike<B, Int>>
typealias A3<C> = ListLike<List<C>>
fun main1(x: A<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Int?<!>>) {}
fun main2(x: A2<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Int?<!>>) {}
fun main3(x: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS!>A3<Int?><!>) {}
fun main3() {
val x = A3<Int?>() // TODO: support reporting errors on typealias constructor calls
val x2 = A<Int?>() // TODO: support reporting errors on typealias constructor calls
val y: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS!>A3<Int?><!> = A3<Int?>()
}
@@ -1,15 +1,37 @@
package package
public fun main1(/*0*/ x: A<kotlin.Int?> /* = MapLike<kotlin.Int?, kotlin.Int> */): kotlin.Unit
public fun main2(/*0*/ x: A2<kotlin.Int?> /* = Foo<MapLike<kotlin.Int?, kotlin.Int>> */): kotlin.Unit
public fun main3(): kotlin.Unit
public fun main3(/*0*/ x: A3<kotlin.Int?> /* = ListLike<kotlin.collections.List<kotlin.Int?>> */): kotlin.Unit
public fun test0(/*0*/ map: MapLike<kotlin.Int?, kotlin.Int>): kotlin.Unit public fun test0(/*0*/ map: MapLike<kotlin.Int?, kotlin.Int>): kotlin.Unit
public fun </*0*/ K> test11(/*0*/ map: MapLike<K, K>): kotlin.Unit public fun </*0*/ K> test11(/*0*/ map: MapLike<K, K>): kotlin.Unit
public fun </*0*/ K> test12(/*0*/ map: MapLike<K?, K>): kotlin.Unit public fun </*0*/ K> test12(/*0*/ map: MapLike<K?, K>): kotlin.Unit
public fun </*0*/ K : kotlin.Any> test13(/*0*/ map: MapLike<K, K>): kotlin.Unit public fun </*0*/ K : kotlin.Any> test13(/*0*/ map: MapLike<K, K>): kotlin.Unit
public fun </*0*/ K : kotlin.Any> test14(/*0*/ map: MapLike<K?, K>): kotlin.Unit public fun </*0*/ K : kotlin.Any> test14(/*0*/ map: MapLike<K?, K>): kotlin.Unit
public fun test2(/*0*/ map: MapLike<kotlin.Int, kotlin.Int>): kotlin.Unit
public interface MapLike</*0*/ @org.jetbrains.annotations.NotNull K : kotlin.Any!, /*1*/ V : kotlin.Any!> { public final class Foo</*0*/ K> {
public constructor Foo</*0*/ K>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean 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 hashCode(): kotlin.Int
public abstract fun putAll(/*0*/ map: kotlin.collections.(Mutable)Map<K!, V!>!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
} }
public open class ListLike</*0*/ K : kotlin.collections.(Mutable)Collection<@org.jetbrains.annotations.NotNull kotlin.Any!>!> {
public constructor ListLike</*0*/ K : kotlin.collections.(Mutable)Collection<@org.jetbrains.annotations.NotNull kotlin.Any!>!>()
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 open class MapLike</*0*/ @org.jetbrains.annotations.NotNull K : kotlin.Any!, /*1*/ V : kotlin.Any!> {
public constructor MapLike</*0*/ @org.jetbrains.annotations.NotNull K : kotlin.Any!, /*1*/ V : kotlin.Any!>()
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/*package*/ open fun putAll(/*0*/ map: kotlin.collections.(Mutable)Map<K!, V!>!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public typealias A</*0*/ A> = MapLike<A, kotlin.Int>
public typealias A2</*0*/ B> = Foo<MapLike<B, kotlin.Int>>
public typealias A3</*0*/ C> = ListLike<kotlin.collections.List<C>>
@@ -1,17 +1,37 @@
// !LANGUAGE: +ImprovementsAroundTypeEnhancement // !LANGUAGE: +ImprovementsAroundTypeEnhancement
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
// FULL_JDK // FULL_JDK
// FILE: MapLike.java // FILE: MapLike.java
import java.util.Map; import java.util.Map;
public interface MapLike<@org.jetbrains.annotations.NotNull K, V> { public class MapLike<@org.jetbrains.annotations.NotNull K, V> {
void putAll(Map<K, V> map); void putAll(Map<K, V> map);
} }
// FILE: ListLike.java
import java.util.Collection;
public class ListLike<K extends Collection<@org.jetbrains.annotations.NotNull Object>> {}
// FILE: main.kt // FILE: main.kt
fun test0(map : MapLike<Int?, Int>) {} fun test0(map : MapLike<<!UPPER_BOUND_VIOLATED!>Int?<!>, Int>) {}
fun <K> test11(map : MapLike<K, K>) {} fun <K> test11(map : MapLike<<!UPPER_BOUND_VIOLATED!>K<!>, K>) {}
fun <K> test12(map : MapLike<K?, K>) {} fun <K> test12(map : MapLike<<!UPPER_BOUND_VIOLATED!>K?<!>, K>) {}
fun <K : Any> test13(map : MapLike<K, K>) {} fun <K : Any> test13(map : MapLike<K, K>) {}
fun <K : Any> test14(map : MapLike<K?, K>) {} fun <K : Any> test14(map : MapLike<<!UPPER_BOUND_VIOLATED!>K?<!>, K>) {}
class Foo<K>
typealias A<A> = MapLike<A, Int>
typealias A2<B> = Foo<MapLike<B, Int>>
typealias A3<C> = ListLike<List<C>>
fun main1(x: A<<!UPPER_BOUND_VIOLATED!>Int?<!>>) {}
fun main2(x: A2<<!UPPER_BOUND_VIOLATED!>Int?<!>>) {}
fun main3(x: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>A3<Int?><!>) {}
fun main3() {
val x = A3<Int?>() // TODO: support reporting errors on typealias constructor calls
val x2 = A<Int?>()
val y: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>A3<Int?><!> = A3<Int?>()
}
@@ -1,17 +1,37 @@
// !LANGUAGE: +ImprovementsAroundTypeEnhancement // !LANGUAGE: +ImprovementsAroundTypeEnhancement
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
// FULL_JDK // FULL_JDK
// FILE: MapLike.java // FILE: MapLike.java
import java.util.Map; import java.util.Map;
public interface MapLike<@org.jetbrains.annotations.NotNull K, V> { public class MapLike<@org.jetbrains.annotations.NotNull K, V> {
void putAll(Map<K, V> map); void putAll(Map<K, V> map);
} }
// FILE: ListLike.java
import java.util.Collection;
public class ListLike<K extends Collection<@org.jetbrains.annotations.NotNull Object>> {}
// FILE: main.kt // FILE: main.kt
fun test0(map : MapLike<<!UPPER_BOUND_VIOLATED!>Int?<!>, Int>) {} fun test0(map : MapLike<<!UPPER_BOUND_VIOLATED!>Int?<!>, Int>) {}
fun <K> test11(map : MapLike<<!UPPER_BOUND_VIOLATED!>K<!>, K>) {} fun <K> test11(map : MapLike<<!UPPER_BOUND_VIOLATED!>K<!>, K>) {}
fun <K> test12(map : MapLike<<!UPPER_BOUND_VIOLATED!>K?<!>, K>) {} fun <K> test12(map : MapLike<<!UPPER_BOUND_VIOLATED!>K?<!>, K>) {}
fun <K : Any> test13(map : MapLike<K, K>) {} fun <K : Any> test13(map : MapLike<K, K>) {}
fun <K : Any> test14(map : MapLike<<!UPPER_BOUND_VIOLATED!>K?<!>, K>) {} fun <K : Any> test14(map : MapLike<<!UPPER_BOUND_VIOLATED!>K?<!>, K>) {}
class Foo<K>
typealias A<A> = MapLike<A, Int>
typealias A2<B> = Foo<MapLike<B, Int>>
typealias A3<C> = ListLike<List<C>>
fun main1(x: A<<!UPPER_BOUND_VIOLATED!>Int?<!>>) {}
fun main2(x: A2<<!UPPER_BOUND_VIOLATED!>Int?<!>>) {}
fun main3(x: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>A3<Int?><!>) {}
fun main3() {
val x = A3<Int?>() // TODO: support reporting errors on typealias constructor calls
val x2 = A<<!UPPER_BOUND_VIOLATED!>Int?<!>>()
val y: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>A3<Int?><!> = A3<Int?>()
}
@@ -1,14 +1,37 @@
package package
public fun main1(/*0*/ x: A<kotlin.Int?> /* = MapLike<kotlin.Int?, kotlin.Int> */): kotlin.Unit
public fun main2(/*0*/ x: A2<kotlin.Int?> /* = Foo<MapLike<kotlin.Int?, kotlin.Int>> */): kotlin.Unit
public fun main3(): kotlin.Unit
public fun main3(/*0*/ x: A3<kotlin.Int?> /* = ListLike<kotlin.collections.List<kotlin.Int?>> */): kotlin.Unit
public fun test0(/*0*/ map: MapLike<kotlin.Int?, kotlin.Int>): kotlin.Unit public fun test0(/*0*/ map: MapLike<kotlin.Int?, kotlin.Int>): kotlin.Unit
public fun </*0*/ K> test11(/*0*/ map: MapLike<K, K>): kotlin.Unit public fun </*0*/ K> test11(/*0*/ map: MapLike<K, K>): kotlin.Unit
public fun </*0*/ K> test12(/*0*/ map: MapLike<K?, K>): kotlin.Unit public fun </*0*/ K> test12(/*0*/ map: MapLike<K?, K>): kotlin.Unit
public fun </*0*/ K : kotlin.Any> test13(/*0*/ map: MapLike<K, K>): kotlin.Unit public fun </*0*/ K : kotlin.Any> test13(/*0*/ map: MapLike<K, K>): kotlin.Unit
public fun </*0*/ K : kotlin.Any> test14(/*0*/ map: MapLike<K?, K>): kotlin.Unit public fun </*0*/ K : kotlin.Any> test14(/*0*/ map: MapLike<K?, K>): kotlin.Unit
public interface MapLike</*0*/ @org.jetbrains.annotations.NotNull K : kotlin.Any, /*1*/ V : kotlin.Any!> { public final class Foo</*0*/ K> {
public constructor Foo</*0*/ K>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean 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 hashCode(): kotlin.Int
public abstract fun putAll(/*0*/ map: kotlin.collections.(Mutable)Map<K, V!>!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
} }
public open class ListLike</*0*/ K : kotlin.collections.(Mutable)Collection<@org.jetbrains.annotations.NotNull kotlin.Any>!> {
public constructor ListLike</*0*/ K : kotlin.collections.(Mutable)Collection<@org.jetbrains.annotations.NotNull kotlin.Any>!>()
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 open class MapLike</*0*/ @org.jetbrains.annotations.NotNull K : kotlin.Any, /*1*/ V : kotlin.Any!> {
public constructor MapLike</*0*/ @org.jetbrains.annotations.NotNull K : kotlin.Any, /*1*/ V : kotlin.Any!>()
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/*package*/ open fun putAll(/*0*/ map: kotlin.collections.(Mutable)Map<K, V!>!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public typealias A</*0*/ A> = MapLike<A, kotlin.Int>
public typealias A2</*0*/ B> = Foo<MapLike<B, kotlin.Int>>
public typealias A3</*0*/ C> = ListLike<kotlin.collections.List<C>>
@@ -250,12 +250,11 @@ class TypeAliasExpander(
val unsubstitutedArgument = unsubstitutedType.arguments[i] val unsubstitutedArgument = unsubstitutedType.arguments[i]
val typeParameter = unsubstitutedType.constructor.parameters[i] val typeParameter = unsubstitutedType.constructor.parameters[i]
if (shouldCheckBounds) { if (shouldCheckBounds) {
checkBoundsInTypeAlias( reportStrategy.boundsViolationInSubstitution(
reportStrategy, typeSubstitutor,
unsubstitutedArgument.type, unsubstitutedArgument.type,
substitutedArgument.type, substitutedArgument.type,
typeParameter, typeParameter
typeSubstitutor
) )
} }
} }
@@ -265,26 +264,6 @@ class TypeAliasExpander(
companion object { companion object {
private const val MAX_RECURSION_DEPTH = 100 private const val MAX_RECURSION_DEPTH = 100
fun checkBoundsInTypeAlias(
reportStrategy: TypeAliasExpansionReportStrategy,
unsubstitutedArgument: KotlinType,
typeArgument: KotlinType,
typeParameterDescriptor: TypeParameterDescriptor,
substitutor: TypeSubstitutor
) {
for (bound in typeParameterDescriptor.upperBounds) {
val substitutedBound = substitutor.safeSubstitute(bound, Variance.INVARIANT)
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) {
reportStrategy.boundsViolationInSubstitution(
substitutedBound,
unsubstitutedArgument,
typeArgument,
typeParameterDescriptor
)
}
}
}
private fun assertRecursionDepth(recursionDepth: Int, typeAliasDescriptor: TypeAliasDescriptor) { private fun assertRecursionDepth(recursionDepth: Int, typeAliasDescriptor: TypeAliasDescriptor) {
if (recursionDepth > MAX_RECURSION_DEPTH) { if (recursionDepth > MAX_RECURSION_DEPTH) {
throw AssertionError("Too deep recursion while expanding type alias ${typeAliasDescriptor.name}") throw AssertionError("Too deep recursion while expanding type alias ${typeAliasDescriptor.name}")
@@ -14,7 +14,7 @@ interface TypeAliasExpansionReportStrategy {
fun conflictingProjection(typeAlias: TypeAliasDescriptor, typeParameter: TypeParameterDescriptor?, substitutedArgument: KotlinType) fun conflictingProjection(typeAlias: TypeAliasDescriptor, typeParameter: TypeParameterDescriptor?, substitutedArgument: KotlinType)
fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor) fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor)
fun boundsViolationInSubstitution( fun boundsViolationInSubstitution(
bound: KotlinType, substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType, unsubstitutedArgument: KotlinType,
argument: KotlinType, argument: KotlinType,
typeParameter: TypeParameterDescriptor typeParameter: TypeParameterDescriptor
@@ -33,7 +33,7 @@ interface TypeAliasExpansionReportStrategy {
override fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor) {} override fun recursiveTypeAlias(typeAlias: TypeAliasDescriptor) {}
override fun boundsViolationInSubstitution( override fun boundsViolationInSubstitution(
bound: KotlinType, substitutor: TypeSubstitutor,
unsubstitutedArgument: KotlinType, unsubstitutedArgument: KotlinType,
argument: KotlinType, argument: KotlinType,
typeParameter: TypeParameterDescriptor typeParameter: TypeParameterDescriptor
@@ -88,6 +88,40 @@ fun KotlinType.getEnhancement(): KotlinType? = when (this) {
else -> null else -> null
} }
private fun List<TypeProjection>.enhanceTypeArguments(depth: Int) =
map { argument ->
// TODO: think about star projections with enhancement (e.g. came from Java: Foo<@NotNull ?>)
if (argument.isStarProjection) {
return@map argument
}
val argumentType = argument.type
val enhancedArgumentType = if (argumentType is TypeWithEnhancement) argumentType.enhancement else argumentType
val enhancedDeeplyArgumentType = enhancedArgumentType.getEnhancementDeeply(depth + 1)
argument.replaceType(enhancedDeeplyArgumentType)
}
private fun KotlinType.getEnhancementDeeply(depth: Int): KotlinType {
val newArguments = arguments.enhanceTypeArguments(depth)
val newArgumentsForUpperBound = if (this is FlexibleType) upperBound.arguments.enhanceTypeArguments(depth) else newArguments
val enhancedType = if (this is TypeWithEnhancement) enhancement else this
return enhancedType.replace(
newArguments = newArguments,
newArgumentsForUpperBound = newArgumentsForUpperBound
)
}
fun KotlinType.getEnhancementDeeply(): KotlinType? {
val enhancedTypeWithArguments = getEnhancementDeeply(depth = 0)
if (enhancedTypeWithArguments === this) return null
return enhancedTypeWithArguments
}
fun KotlinType.unwrapEnhancementDeeply() = getEnhancementDeeply() ?: this
fun KotlinType.unwrapEnhancement(): KotlinType = getEnhancement() ?: this fun KotlinType.unwrapEnhancement(): KotlinType = getEnhancement() ?: this
fun UnwrappedType.inheritEnhancement(origin: KotlinType): UnwrappedType = wrapEnhancement(origin.getEnhancement()) fun UnwrappedType.inheritEnhancement(origin: KotlinType): UnwrappedType = wrapEnhancement(origin.getEnhancement())
@@ -266,5 +266,6 @@ class CompositePlatformConigurator(private val componentConfigurators: List<Plat
// Unfortunately, it is declared in base class, so repeating call to 'configureModuleDependentCheckers' will lead // Unfortunately, it is declared in base class, so repeating call to 'configureModuleDependentCheckers' will lead
// to multiple registrrations. // to multiple registrrations.
container.useImpl<ExperimentalMarkerDeclarationAnnotationChecker>() container.useImpl<ExperimentalMarkerDeclarationAnnotationChecker>()
container.useImpl<UpperBoundChecker>()
} }
} }