Use upper bound checker for typealias expansion
This commit is contained in:
+33
-24
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-29
@@ -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) {
|
||||||
|
|||||||
+5
@@ -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(
|
||||||
|
|||||||
+4
-2
@@ -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 {
|
||||||
|
|||||||
+1
@@ -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
|
||||||
|
|||||||
+27
-2
@@ -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?>()
|
||||||
|
}
|
||||||
|
|||||||
+22
-2
@@ -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?>()
|
||||||
|
}
|
||||||
|
|||||||
+25
-3
@@ -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>>
|
||||||
|
|
||||||
|
|||||||
+26
-6
@@ -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?>()
|
||||||
|
}
|
||||||
|
|||||||
+22
-2
@@ -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?>()
|
||||||
|
}
|
||||||
|
|||||||
+25
-2
@@ -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())
|
||||||
|
|||||||
+1
@@ -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>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user