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.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.resolve.BindingTrace
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.checker.KotlinTypeChecker
class EnhancedUpperBoundChecker(override val languageVersionSettings: LanguageVersionSettings) : UpperBoundChecker {
override fun checkBound(
bound: KotlinType,
// TODO: remove this checker after removing support LV < 1.6
class EnhancedUpperBoundChecker(languageVersionSettings: LanguageVersionSettings) : UpperBoundChecker(languageVersionSettings) {
val isTypeEnhancementImprovementsEnabled = languageVersionSettings.supportsFeature(LanguageFeature.ImprovementsAroundTypeEnhancement)
override fun checkBounds(
argumentReference: KtTypeReference?,
argumentType: KotlinType,
typeParameterDescriptor: TypeParameterDescriptor,
substitutor: TypeSubstitutor,
trace: BindingTrace,
jetTypeArgument: KtTypeReference,
typeArgument: KotlinType
): Boolean {
val isCheckPassed = super.checkBound(bound, substitutor, trace, jetTypeArgument, typeArgument)
typeAliasUsageElement: KtElement?
) {
if (typeParameterDescriptor.upperBounds.isEmpty()) return
// The error is already reported, it's unnecessary to do more checks
if (!isCheckPassed) return false
val diagnosticsReporter = UpperBoundViolatedReporter(trace, argumentType, typeParameterDescriptor)
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 =
languageVersionSettings.supportsFeature(LanguageFeature.ImprovementsAroundTypeEnhancement)
val substitutedBound = substitutor.safeSubstitute(enhancedBound, Variance.INVARIANT)
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) {
if (isTypeEnhancementImprovementsEnabled) {
trace.report(Errors.UPPER_BOUND_VIOLATED.on(jetTypeArgument, substitutedBound, typeArgument))
} else {
trace.report(ErrorsJvm.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS.on(jetTypeArgument, substitutedBound, typeArgument))
}
return false
// The error is already reported, it's unnecessary to do more checks
if (!isCheckPassed) continue
// If improvements are enabled, then type parameter's upper bounds will already enhanced, and the error will reported inside the first check
if (isTypeEnhancementImprovementsEnabled) continue
val enhancedBound = bound.getEnhancementDeeply() ?: continue
checkBound(enhancedBound, argumentType, argumentReference, substitutor, typeAliasUsageElement, diagnosticsReporterForWarnings)
}
return true
}
}
@@ -215,9 +215,9 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
if (!doesExpectedTypeContainsEnhancement && !doesExpressionTypeContainsEnhancement) return
val enhancedExpectedType = if (doesExpectedTypeContainsEnhancement) buildTypeWithEnhancement(expectedType) else expectedType
val enhancedExpectedType = if (doesExpectedTypeContainsEnhancement) expectedType.unwrapEnhancementDeeply() else expectedType
val enhancedExpressionType = enhanceExpressionTypeByDataFlowNullability(
if (doesExpressionTypeContainsEnhancement) buildTypeWithEnhancement(expressionType) else expressionType,
if (doesExpressionTypeContainsEnhancement) expressionType.unwrapEnhancementDeeply() else expressionType,
expressionTypeDataFlowValue,
dataFlowInfo
)
@@ -253,33 +253,6 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
} else {
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) {
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.utils.StringsKt;
import java.util.List;
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.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(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,
"Type mismatch: value of a nullable type {0} is used where non-nullable type is expected. " +
"This warning will become an error soon. " +
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.jvm.diagnostics;
import com.intellij.psi.PsiElement;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.diagnostics.*;
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.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.create(WARNING);