Report warnings or errors for violated type parameter's upper bounds from Java annotated with nullability annotations
^KT-43262 Fixed
This commit is contained in:
@@ -56,11 +56,12 @@ internal class DeclarationsCheckerBuilder(
|
||||
private val identifierChecker: IdentifierChecker,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
private val typeSpecificityComparator: TypeSpecificityComparator,
|
||||
private val diagnosticSuppressor: PlatformDiagnosticSuppressor
|
||||
private val diagnosticSuppressor: PlatformDiagnosticSuppressor,
|
||||
private val upperBoundChecker: UpperBoundChecker
|
||||
) {
|
||||
fun withTrace(trace: BindingTrace) = DeclarationsChecker(
|
||||
descriptorResolver, originalModifiersChecker, annotationChecker, identifierChecker, trace, languageVersionSettings,
|
||||
typeSpecificityComparator, diagnosticSuppressor
|
||||
typeSpecificityComparator, diagnosticSuppressor, upperBoundChecker
|
||||
)
|
||||
}
|
||||
|
||||
@@ -72,7 +73,8 @@ class DeclarationsChecker(
|
||||
private val trace: BindingTrace,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
typeSpecificityComparator: TypeSpecificityComparator,
|
||||
private val diagnosticSuppressor: PlatformDiagnosticSuppressor
|
||||
private val diagnosticSuppressor: PlatformDiagnosticSuppressor,
|
||||
private val upperBoundChecker: UpperBoundChecker
|
||||
) {
|
||||
|
||||
private val modifiersChecker = modifiersChecker.withTrace(trace)
|
||||
@@ -360,7 +362,7 @@ class DeclarationsChecker(
|
||||
|
||||
for (delegationSpecifier in classOrObject.superTypeListEntries) {
|
||||
val typeReference = delegationSpecifier.typeReference ?: continue
|
||||
typeReference.type()?.let { DescriptorResolver.checkBounds(typeReference, it, trace) }
|
||||
typeReference.type()?.let { upperBoundChecker.checkBounds(typeReference, it, trace) }
|
||||
}
|
||||
|
||||
if (classOrObject !is KtClass) return
|
||||
@@ -383,7 +385,7 @@ class DeclarationsChecker(
|
||||
DescriptorResolver.checkUpperBoundTypes(trace, upperBoundCheckRequests, false)
|
||||
|
||||
for (request in upperBoundCheckRequests) {
|
||||
DescriptorResolver.checkBounds(request.upperBound, request.upperBoundType, trace)
|
||||
upperBoundChecker.checkBounds(request.upperBound, request.upperBoundType, trace)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1312,61 +1312,6 @@ public class DescriptorResolver {
|
||||
return propertyDescriptor;
|
||||
}
|
||||
|
||||
public static void checkBounds(@NotNull KtTypeReference typeReference, @NotNull KotlinType type, @NotNull BindingTrace trace) {
|
||||
if (KotlinTypeKt.isError(type)) return;
|
||||
|
||||
KtTypeElement typeElement = typeReference.getTypeElement();
|
||||
if (typeElement == null) return;
|
||||
|
||||
List<TypeParameterDescriptor> parameters = type.getConstructor().getParameters();
|
||||
List<TypeProjection> arguments = type.getArguments();
|
||||
assert parameters.size() == arguments.size();
|
||||
|
||||
List<KtTypeReference> ktTypeArguments = typeElement.getTypeArgumentsAsTypes();
|
||||
|
||||
// A type reference from Kotlin code can yield a flexible type only if it's `ft<T1, T2>`, whose bounds should not be checked
|
||||
if (FlexibleTypesKt.isFlexible(type) && !DynamicTypesKt.isDynamic(type)) {
|
||||
assert ktTypeArguments.size() == 2
|
||||
: "Flexible type cannot be denoted in Kotlin otherwise than as ft<T1, T2>, but was: "
|
||||
+ PsiUtilsKt.getElementTextWithContext(typeReference);
|
||||
// it's really ft<Foo, Bar>
|
||||
FlexibleType flexibleType = FlexibleTypesKt.asFlexibleType(type);
|
||||
checkBounds(ktTypeArguments.get(0), flexibleType.getLowerBound(), trace);
|
||||
checkBounds(ktTypeArguments.get(1), flexibleType.getUpperBound(), trace);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the numbers of type arguments do not match, the error has been already reported in TypeResolver
|
||||
if (ktTypeArguments.size() != arguments.size()) return;
|
||||
|
||||
TypeSubstitutor substitutor = TypeSubstitutor.create(type);
|
||||
for (int i = 0; i < ktTypeArguments.size(); i++) {
|
||||
KtTypeReference ktTypeArgument = ktTypeArguments.get(i);
|
||||
if (ktTypeArgument == null) continue;
|
||||
|
||||
KotlinType typeArgument = arguments.get(i).getType();
|
||||
checkBounds(ktTypeArgument, typeArgument, trace);
|
||||
|
||||
TypeParameterDescriptor typeParameterDescriptor = parameters.get(i);
|
||||
checkBounds(ktTypeArgument, typeArgument, typeParameterDescriptor, substitutor, trace);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkBounds(
|
||||
@NotNull KtTypeReference jetTypeArgument,
|
||||
@NotNull KotlinType typeArgument,
|
||||
@NotNull TypeParameterDescriptor typeParameterDescriptor,
|
||||
@NotNull TypeSubstitutor substitutor,
|
||||
@NotNull BindingTrace trace
|
||||
) {
|
||||
for (KotlinType bound : typeParameterDescriptor.getUpperBounds()) {
|
||||
KotlinType substitutedBound = substitutor.safeSubstitute(bound, Variance.INVARIANT);
|
||||
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) {
|
||||
trace.report(UPPER_BOUND_VIOLATED.on(jetTypeArgument, substitutedBound, typeArgument));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkHasOuterClassInstance(
|
||||
@NotNull LexicalScope scope,
|
||||
@NotNull BindingTrace trace,
|
||||
|
||||
@@ -70,7 +70,8 @@ class TypeResolver(
|
||||
private val dynamicCallableDescriptors: DynamicCallableDescriptors,
|
||||
private val identifierChecker: IdentifierChecker,
|
||||
private val platformToKotlinClassMapper: PlatformToKotlinClassMapper,
|
||||
private val languageVersionSettings: LanguageVersionSettings
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
private val upperBoundChecker: UpperBoundChecker
|
||||
) {
|
||||
private val isNonParenthesizedAnnotationsOnFunctionalTypesEnabled =
|
||||
languageVersionSettings.getFeatureSupport(LanguageFeature.NonParenthesizedAnnotationsOnFunctionalTypes) == LanguageFeature.State.ENABLED
|
||||
@@ -524,7 +525,7 @@ class TypeResolver(
|
||||
val typeReference = collectedArgumentAsTypeProjections.getOrNull(i)?.typeReference
|
||||
|
||||
if (typeReference != null) {
|
||||
DescriptorResolver.checkBounds(typeReference, argument, parameter, substitutor, c.trace)
|
||||
upperBoundChecker.checkBounds(typeReference, argument, parameter, substitutor, c.trace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.container.DefaultImplementation
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
|
||||
@DefaultImplementation(impl = UpperBoundChecker::class)
|
||||
interface UpperBoundChecker {
|
||||
val languageVersionSettings: LanguageVersionSettings
|
||||
|
||||
fun checkBounds(typeReference: KtTypeReference, type: KotlinType, trace: BindingTrace) {
|
||||
if (type.isError) return
|
||||
|
||||
val typeElement = typeReference.typeElement ?: return
|
||||
val parameters = type.constructor.parameters
|
||||
val arguments = type.arguments
|
||||
|
||||
assert(parameters.size == arguments.size)
|
||||
|
||||
val ktTypeArguments = typeElement.typeArgumentsAsTypes
|
||||
|
||||
// A type reference from Kotlin code can yield a flexible type only if it's `ft<T1, T2>`, whose bounds should not be checked
|
||||
if (type.isFlexible() && !type.isDynamic()) {
|
||||
assert(ktTypeArguments.size == 2) {
|
||||
("Flexible type cannot be denoted in Kotlin otherwise than as ft<T1, T2>, but was: "
|
||||
+ typeReference.getElementTextWithContext())
|
||||
}
|
||||
// it's really ft<Foo, Bar>
|
||||
val flexibleType = type.asFlexibleType()
|
||||
checkBounds(ktTypeArguments[0], flexibleType.lowerBound, trace)
|
||||
checkBounds(ktTypeArguments[1], flexibleType.upperBound, trace)
|
||||
return
|
||||
}
|
||||
|
||||
// If the numbers of type arguments do not match, the error has been already reported in TypeResolver
|
||||
if (ktTypeArguments.size != arguments.size) return
|
||||
|
||||
val substitutor = TypeSubstitutor.create(type)
|
||||
|
||||
for (i in ktTypeArguments.indices) {
|
||||
val ktTypeArgument = ktTypeArguments[i] ?: continue
|
||||
checkBounds(ktTypeArgument, arguments[i].type, trace)
|
||||
checkBounds(ktTypeArgument, arguments[i].type, parameters[i], substitutor, trace)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkBounds(
|
||||
jetTypeArgument: KtTypeReference,
|
||||
typeArgument: KotlinType,
|
||||
typeParameterDescriptor: TypeParameterDescriptor,
|
||||
substitutor: TypeSubstitutor,
|
||||
trace: BindingTrace
|
||||
) {
|
||||
for (bound in typeParameterDescriptor.upperBounds) {
|
||||
checkBound(bound, substitutor, trace, jetTypeArgument, typeArgument)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkBound(
|
||||
bound: KotlinType,
|
||||
substitutor: TypeSubstitutor,
|
||||
trace: BindingTrace,
|
||||
jetTypeArgument: KtTypeReference,
|
||||
typeArgument: KotlinType
|
||||
): Boolean {
|
||||
val substitutedBound = substitutor.safeSubstitute(bound, Variance.INVARIANT)
|
||||
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(typeArgument, substitutedBound)) {
|
||||
trace.report(Errors.UPPER_BOUND_VIOLATED.on(jetTypeArgument, substitutedBound, typeArgument))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,8 @@ class CandidateResolver(
|
||||
private val reflectionTypes: ReflectionTypes,
|
||||
private val additionalTypeCheckers: Iterable<AdditionalTypeChecker>,
|
||||
private val smartCastManager: SmartCastManager,
|
||||
private val dataFlowValueFactory: DataFlowValueFactory
|
||||
private val dataFlowValueFactory: DataFlowValueFactory,
|
||||
private val upperBoundChecker: UpperBoundChecker
|
||||
) {
|
||||
fun <D : CallableDescriptor> performResolutionForCandidateCall(
|
||||
context: CallCandidateResolutionContext<D>,
|
||||
@@ -596,7 +597,7 @@ class CandidateResolver(
|
||||
val typeArgument = typeArguments[i]
|
||||
val typeReference = ktTypeArguments[i].typeReference
|
||||
if (typeReference != null) {
|
||||
DescriptorResolver.checkBounds(typeReference, typeArgument, typeParameterDescriptor, substitutor, trace)
|
||||
upperBoundChecker.checkBounds(typeReference, typeArgument, typeParameterDescriptor, substitutor, trace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user