[Jspecify] Report warnings on violated bounds of method type parameters

This commit is contained in:
Victor Petukhov
2021-05-12 19:06:57 +03:00
parent 0d6764efc5
commit 937e82db46
4 changed files with 47 additions and 21 deletions
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.UpperBoundChecker
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
@@ -48,9 +49,7 @@ class JavaNullabilityChecker(val upperBoundChecker: UpperBoundChecker) : Additio
expressionTypeWithSmartCast: KotlinType,
c: ResolutionContext<*>
) {
if (expressionType is AbbreviatedType) {
upperBoundChecker.checkBoundsOfExpandedTypeAlias(expressionType.expandedType, expression, c.trace)
}
checkTypeParameterBounds(expression, expressionType, c)
val dataFlowValue by lazy(LazyThreadSafetyMode.NONE) {
c.dataFlowValueFactory.createDataFlowValue(expression, expressionType, c)
@@ -122,6 +121,29 @@ class JavaNullabilityChecker(val upperBoundChecker: UpperBoundChecker) : Additio
}
}
private fun checkTypeParameterBounds(
expression: KtExpression,
expressionType: KotlinType,
c: ResolutionContext<*>
) {
if (expressionType is AbbreviatedType) {
upperBoundChecker.checkBoundsOfExpandedTypeAlias(expressionType.expandedType, expression, c.trace)
}
if (c !is BasicCallResolutionContext || upperBoundChecker !is WarningAwareUpperBoundChecker) return
val resolvedCall = c.trace.bindingContext[BindingContext.RESOLVED_CALL, c.call] ?: return
for ((typeParameter, typeArgument) in resolvedCall.typeArguments) {
// continue if we don't have explicit type arguments
val typeReference = c.call.typeArguments.getOrNull(typeParameter.index)?.typeReference ?: continue
upperBoundChecker.checkBounds(
typeReference, typeArgument, typeParameter, TypeSubstitutor.create(typeArgument), c.trace, withOnlyCheckForWarning = true
)
}
}
private fun isWrongTypeParameterNullabilityForSubtyping(
expressionType: KotlinType,
c: ResolutionContext<*>,
@@ -49,7 +49,7 @@ class WarningAwareUpperBoundChecker : UpperBoundChecker() {
typeParameterDescriptor: TypeParameterDescriptor,
substitutor: TypeSubstitutor,
trace: BindingTrace,
typeAliasUsageElement: KtElement?,
typeAliasUsageElement: KtElement? = null,
withOnlyCheckForWarning: Boolean = false
) {
if (typeParameterDescriptor.upperBounds.isEmpty()) return
@@ -71,8 +71,12 @@ class WarningAwareUpperBoundChecker : UpperBoundChecker() {
}
val enhancedBound = bound.getEnhancementDeeply() ?: continue
val argumentTypeEnhancement = argumentType.getEnhancementDeeply() ?: argumentType
checkBound(enhancedBound, argumentType, argumentReference, substitutor, typeAliasUsageElement, diagnosticsReporterForWarnings)
checkBound(
enhancedBound, argumentTypeEnhancement, argumentReference,
substitutor, typeAliasUsageElement, diagnosticsReporterForWarnings
)
}
}
}
@@ -22,24 +22,24 @@ public class Test {}
// jspecify_nullness_mismatch, jspecify_nullness_mismatch
fun <T : Test> main(a1: A<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Any?<!>>, a2: A<Test>, b1: B<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Any?<!>>, b2: B<Test>, x: T): Unit {
a1.foo(null)
// jspecify_nullness_mismatch{mute}
a1.bar<T?>(null)
// jspecify_nullness_mismatch
a1.bar<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>T?<!>>(null)
a1.bar<T>(x)
a2.foo(null)
// jspecify_nullness_mismatch{mute}
a2.bar<T?>(null)
// jspecify_nullness_mismatch
a2.bar<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>T?<!>>(null)
a2.bar<T>(x)
// jspecify_nullness_mismatch{mute}
b1.foo(null)
// jspecify_nullness_mismatch{mute}
b1.bar<T?>(null)
// jspecify_nullness_mismatch
b1.bar<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>T?<!>>(null)
b1.bar<T>(x)
// jspecify_nullness_mismatch{mute}
b2.foo(null)
// jspecify_nullness_mismatch{mute}
b2.bar<T?>(null)
// jspecify_nullness_mismatch
b2.bar<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>T?<!>>(null)
b2.bar<T>(x)
}
@@ -22,24 +22,24 @@ public class Test {}
// jspecify_nullness_mismatch, jspecify_nullness_mismatch
fun <T : Test> main(a1: A<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Any?<!>>, a2: A<Test>, b1: B<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>Any?<!>>, b2: B<Test>, x: T): Unit {
a1.foo(null)
// jspecify_nullness_mismatch{mute}
a1.bar<T?>(null)
// jspecify_nullness_mismatch
a1.bar<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>T?<!>>(null)
a1.bar<T>(x)
a2.foo(null)
// jspecify_nullness_mismatch{mute}
a2.bar<T?>(null)
// jspecify_nullness_mismatch
a2.bar<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>T?<!>>(null)
a2.bar<T>(x)
// jspecify_nullness_mismatch{mute}
b1.foo(null)
// jspecify_nullness_mismatch{mute}
b1.bar<T?>(null)
// jspecify_nullness_mismatch
b1.bar<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>T?<!>>(null)
b1.bar<T>(x)
// jspecify_nullness_mismatch{mute}
b2.foo(null)
// jspecify_nullness_mismatch{mute}
b2.bar<T?>(null)
// jspecify_nullness_mismatch
b2.bar<<!UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS!>T?<!>>(null)
b2.bar<T>(x)
}