Fix false negative UPPER_BOUND_VIOLATED with typealiases in supertypes

^KT-50797 Fixed
^KT-50798 Open
This commit is contained in:
Denis.Zharkov
2022-01-14 19:29:39 +03:00
parent 8d0b511e95
commit b2543b7a26
16 changed files with 244 additions and 30 deletions
@@ -220,6 +220,8 @@ public interface Errors {
DiagnosticFactory1<KtElement, ClassifierDescriptor> RECURSIVE_TYPEALIAS_EXPANSION = DiagnosticFactory1.create(ERROR);
DiagnosticFactory3<KtElement, KotlinType, KotlinType, ClassifierDescriptor> UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION =
DiagnosticFactory3.create(ERROR);
DiagnosticFactory3<KtElement, KotlinType, KotlinType, ClassifierDescriptor> UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_WARNING =
DiagnosticFactory3.create(WARNING);
DiagnosticFactory1<KtElement, KotlinType> CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<KtTypeReference, KotlinType> TYPEALIAS_SHOULD_EXPAND_TO_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<KtTypeReference, KotlinType, String> TYPEALIAS_EXPANDED_TO_MALFORMED_TYPE = DiagnosticFactory2.create(ERROR);
@@ -10,7 +10,6 @@ import kotlin.Pair;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.config.LanguageVersion;
import org.jetbrains.kotlin.descriptors.MemberDescriptor;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
import org.jetbrains.kotlin.diagnostics.Errors;
@@ -607,6 +606,10 @@ public class DefaultErrorMessages {
"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(UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_WARNING,
"Type argument resulting from type alias expansion is not within required bounds for ''{2}'': " +
"should be subtype of ''{0}'', substituted type is ''{1}''. This warning will become an error since 1.8",
RENDER_TYPE, RENDER_TYPE, NAME);
MAP.put(CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION, "Conflicting projection in type alias expansion in intermediate type ''{0}''", RENDER_TYPE);
MAP.put(TYPEALIAS_SHOULD_EXPAND_TO_CLASS, "Type alias expands to {0}, which is not a class, an interface, or an object", RENDER_TYPE);
MAP.put(TYPEALIAS_EXPANDED_TO_MALFORMED_TYPE, "Type alias expanded to malformed type {0}: {1}", RENDER_TYPE, STRING);
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.*
@@ -45,9 +44,11 @@ import org.jetbrains.kotlin.resolve.inline.isInlineOnly
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlin.types.typeUtil.constituentTypes
import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.types.typeUtil.isArrayOfNothing
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
internal class DeclarationsCheckerBuilder(
private val descriptorResolver: DescriptorResolver,
@@ -357,7 +358,7 @@ class DeclarationsChecker(
for (delegationSpecifier in classOrObject.superTypeListEntries) {
val typeReference = delegationSpecifier.typeReference ?: continue
typeReference.type()?.let { upperBoundChecker.checkBounds(typeReference, it, trace) }
typeReference.type()?.let { upperBoundChecker.checkBoundsInSupertype(typeReference, it, trace, languageVersionSettings) }
}
if (classOrObject !is KtClass) return
@@ -380,7 +381,7 @@ class DeclarationsChecker(
DescriptorResolver.checkUpperBoundTypes(trace, upperBoundCheckRequests, false)
for (request in upperBoundCheckRequests) {
upperBoundChecker.checkBounds(request.upperBound, request.upperBoundType, trace)
upperBoundChecker.checkBoundsInSupertype(request.upperBound, request.upperBoundType, trace, languageVersionSettings)
}
}
@@ -5,13 +5,14 @@
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.diagnostics.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.diagnostics.Errors.*
import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
@@ -34,17 +35,24 @@ open class UpperBoundChecker {
substitutor: TypeSubstitutor,
trace: BindingTrace,
typeAliasUsageElement: KtElement? = null,
diagnosticForTypeAliases: DiagnosticFactory3<KtElement, KotlinType, KotlinType, ClassifierDescriptor> = UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION
) {
if (typeParameterDescriptor.upperBounds.isEmpty()) return
val diagnosticsReporter = UpperBoundViolatedReporter(trace, argumentType, typeParameterDescriptor)
val diagnosticsReporter =
UpperBoundViolatedReporter(trace, argumentType, typeParameterDescriptor, diagnosticForTypeAliases = diagnosticForTypeAliases)
for (bound in typeParameterDescriptor.upperBounds) {
checkBound(bound, argumentType, argumentReference, substitutor, typeAliasUsageElement, diagnosticsReporter)
}
}
fun checkBounds(typeReference: KtTypeReference, type: KotlinType, trace: BindingTrace) {
fun checkBoundsInSupertype(
typeReference: KtTypeReference,
type: KotlinType,
trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings,
) {
if (type.isError) return
val typeElement = typeReference.typeElement ?: return
@@ -63,8 +71,21 @@ open class UpperBoundChecker {
}
// it's really ft<Foo, Bar>
val flexibleType = type.asFlexibleType()
checkBounds(ktTypeArguments[0], flexibleType.lowerBound, trace)
checkBounds(ktTypeArguments[1], flexibleType.upperBound, trace)
checkBoundsInSupertype(ktTypeArguments[0], flexibleType.lowerBound, trace, languageVersionSettings)
checkBoundsInSupertype(ktTypeArguments[1], flexibleType.upperBound, trace, languageVersionSettings)
return
}
if (type is AbbreviatedType) {
checkBoundsForAbbreviatedSupertype(
type, trace, typeReference,
// The errors have been reported previously if ktTypeArguments.size accidentally was equal to the amount of arguments
// in the expanded type
reportWarning = ktTypeArguments.size != arguments.size &&
!languageVersionSettings.supportsFeature(
LanguageFeature.ReportMissingUpperBoundsViolatedErrorOnAbbreviationAtSupertypes
)
)
return
}
@@ -75,11 +96,41 @@ open class UpperBoundChecker {
for (i in ktTypeArguments.indices) {
val ktTypeArgument = ktTypeArguments[i] ?: continue
checkBounds(ktTypeArgument, arguments[i].type, trace)
checkBoundsInSupertype(ktTypeArgument, arguments[i].type, trace, languageVersionSettings)
checkBounds(ktTypeArgument, arguments[i].type, parameters[i], substitutor, trace)
}
}
private fun checkBoundsForAbbreviatedSupertype(
type: KotlinType,
trace: BindingTrace,
typeReference: KtTypeReference,
reportWarning: Boolean
) {
val parameters = type.constructor.parameters
val arguments = type.arguments
val substitutor = TypeSubstitutor.create(type)
val diagnostic =
if (reportWarning)
UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_WARNING
else
UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION
for (i in arguments.indices) {
if (arguments[i].isStarProjection) continue
val argumentType = arguments[i].type
checkBoundsForAbbreviatedSupertype(argumentType, trace, typeReference, reportWarning)
checkBounds(
argumentReference = null,
argumentType, parameters[i], substitutor, trace,
typeAliasUsageElement = typeReference, diagnosticForTypeAliases = diagnostic,
)
}
}
protected fun checkBound(
bound: KotlinType,
argumentType: KotlinType,