From 139497bafb4def5b84f291a1639c179eb2da4efb Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Wed, 29 May 2019 10:53:07 +0300 Subject: [PATCH] [NI] Add checking @NotNull parameters for candidates --- .../fir/resolve/calls/ConeInferenceContext.kt | 4 ++ .../DiagnosticReporterByTrackingStrategy.kt | 2 +- .../calls/components/SimpleArgumentsChecks.kt | 7 ++++ .../starNullabilityRecursive.kt | 3 +- .../tests/j+k/sam/enhancedSamConstructor.kt | 4 +- .../methodTypeParameter.kt | 12 +++--- .../notNullAnnotation.kt | 26 ++++++++++++ .../notNullAnnotation.txt | 40 +++++++++++++++++++ .../DiagnosticsTestWithStdLibGenerated.java | 5 +++ ...ticsTestWithStdLibUsingJavacGenerated.java | 5 +++ .../java/typeEnhancement/typeEnhancement.kt | 2 +- .../kotlin/types/TypeCapabilities.kt | 4 ++ .../types/checker/ClassicTypeSystemContext.kt | 4 ++ .../kotlin/types/model/TypeSystemContext.kt | 1 + 14 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.txt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt index 6790fd4748f..8855c11fecf 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeInferenceContext.kt @@ -40,6 +40,10 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, return StandardClassIds.Nothing(symbolProvider).constructType(emptyArray(), false) } + override fun anyType(): SimpleTypeMarker { + return StandardClassIds.Any(symbolProvider).constructType(emptyArray(), false) + } + override fun createFlexibleType(lowerBound: SimpleTypeMarker, upperBound: SimpleTypeMarker): KotlinTypeMarker { require(lowerBound is ConeKotlinType) require(upperBound is ConeKotlinType) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index a630395347a..2d6a3880fce 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -135,7 +135,7 @@ class DiagnosticReporterByTrackingStrategy( require(diagnostic is ArgumentTypeMismatchDiagnostic) reportIfNonNull(callArgument.safeAs()?.valueArgument?.getArgumentExpression()) { if (it.isNull()) { - trace.report(NULL_FOR_NONNULL_TYPE.on(it, diagnostic.expectedType)) + trace.reportDiagnosticOnce(NULL_FOR_NONNULL_TYPE.on(it, diagnostic.expectedType)) } else { trace.report(TYPE_MISMATCH.on(it, diagnostic.expectedType, diagnostic.actualType)) } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt index 6b7f00d384a..cda98072f65 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosi import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.ReceiverConstraintPosition import org.jetbrains.kotlin.resolve.calls.model.* +import org.jetbrains.kotlin.types.NotNullTypeVariable import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.checker.captureFromExpression import org.jetbrains.kotlin.types.checker.hasSupertypeWithGivenTypeConstructor @@ -81,6 +82,12 @@ private fun checkExpressionArgument( val expectedNullableType = expectedType.makeNullableAsSpecified(true) val position = if (isReceiver) ReceiverConstraintPosition(expressionArgument) else ArgumentConstraintPosition(expressionArgument) + + // Used only for arguments with @NotNull annotation + if (expectedType is NotNullTypeVariable && argumentType.isMarkedNullable) { + diagnosticsHolder.addDiagnostic(ArgumentTypeMismatchDiagnostic(expectedType, argumentType, expressionArgument)) + } + if (expressionArgument.isSafeCall) { if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedNullableType, position)) { diagnosticsHolder.addDiagnosticIfNotNull( diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/starNullabilityRecursive.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/starNullabilityRecursive.kt index 7cc339fe377..7a5f0dc7fdd 100644 --- a/compiler/testData/diagnostics/tests/generics/projectionsScope/starNullabilityRecursive.kt +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/starNullabilityRecursive.kt @@ -5,6 +5,5 @@ interface A?> { fun foo(): T? } fun testA(a: A<*>) { - // in new inference here we have A>> - a.foo() checkType { _?>() } + a.foo() checkType { _?>() } } diff --git a/compiler/testData/diagnostics/tests/j+k/sam/enhancedSamConstructor.kt b/compiler/testData/diagnostics/tests/j+k/sam/enhancedSamConstructor.kt index 940d1900a42..b08bed6f734 100644 --- a/compiler/testData/diagnostics/tests/j+k/sam/enhancedSamConstructor.kt +++ b/compiler/testData/diagnostics/tests/j+k/sam/enhancedSamConstructor.kt @@ -18,11 +18,11 @@ public interface J2 extends J { fun main() { J { s: String -> s} // should be prohibited, because SAM value parameter has nullable type J { "" + it.length } - J { null } + J { null } J { it?.length?.toString() } J2 { s: String -> s} J2 { "" + it.length } - J2 { null } + J2 { null } J2 { it?.length?.toString() } } diff --git a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/methodTypeParameter.kt b/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/methodTypeParameter.kt index e1baf17bc99..b3cfaecde22 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/methodTypeParameter.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/notNullTypeParameter/methodTypeParameter.kt @@ -11,13 +11,15 @@ public class A { // FILE: k.kt fun test() { - A.bar(null, "") + A.bar(null, "") A.bar(null, "") - A.bar(null, "") - A.bar(null, A.platformString()) + A.bar(null, "") + A.bar("", "") + + A.bar(null, A.platformString()) val x: String? = null - A.bar(x, "") - A.bar(null, "") + A.bar(x, "") + A.bar(null, "") } diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt new file mode 100644 index 00000000000..8d588891567 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt @@ -0,0 +1,26 @@ +// !LANGUAGE: +NewInference + +// FILE: SmartFMap.java + +public class SmartFMap implements java.util.Map { + public static < K, V> SmartFMap emptyMap() { + return null; + } + + public SmartFMap < K, V> plus(@org.jetbrains.annotations.NotNull K key, V value) { + return null; + } +} + +// FILE: main.kt + +class KotlinType + +interface TypePredicate : (KotlinType) -> Boolean { + override fun invoke(typeToCheck: KotlinType): Boolean +} + +fun TypePredicate.expectedTypeFor(keys: Iterable): Map = + keys.fold(SmartFMap.emptyMap()) { map, key -> + map.plus(key, this) + } diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.txt new file mode 100644 index 00000000000..dc8ec2ca491 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.txt @@ -0,0 +1,40 @@ +package + +public fun TypePredicate.expectedTypeFor(/*0*/ keys: kotlin.collections.Iterable): kotlin.collections.Map + +public final class KotlinType { + public constructor KotlinType() + 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 SmartFMap : kotlin.collections.MutableMap { + public constructor SmartFMap() + public abstract override /*1*/ /*fake_override*/ val entries: kotlin.collections.MutableSet> + public abstract override /*1*/ /*fake_override*/ val keys: kotlin.collections.MutableSet + public abstract override /*1*/ /*fake_override*/ val size: kotlin.Int + public abstract override /*1*/ /*fake_override*/ val values: kotlin.collections.MutableCollection + public abstract override /*1*/ /*fake_override*/ fun clear(): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun containsKey(/*0*/ key: K!): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun containsValue(/*0*/ value: V!): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ key: K!): V? + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean + public open fun plus(/*0*/ @org.jetbrains.annotations.NotNull key: K, /*1*/ value: V!): SmartFMap! + public abstract override /*1*/ /*fake_override*/ fun put(/*0*/ key: K!, /*1*/ value: V!): V? + public abstract override /*1*/ /*fake_override*/ fun putAll(/*0*/ from: kotlin.collections.Map): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun remove(/*0*/ key: K!): V? + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public open fun emptyMap(): SmartFMap! +} + +public interface TypePredicate : (KotlinType) -> 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 abstract override /*1*/ fun invoke(/*0*/ typeToCheck: KotlinType): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 4af5c2eb860..456810bd0ed 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -2753,6 +2753,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/noInferAnnotation.kt"); } + @TestMetadata("notNullAnnotation.kt") + public void testNotNullAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt"); + } + @TestMetadata("onlyInputTypesAndLowPriority.kt") public void testOnlyInputTypesAndLowPriority() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesAndLowPriority.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index ceb9bb87ca7..772e8977dcf 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -2753,6 +2753,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/noInferAnnotation.kt"); } + @TestMetadata("notNullAnnotation.kt") + public void testNotNullAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/notNullAnnotation.kt"); + } + @TestMetadata("onlyInputTypesAndLowPriority.kt") public void testOnlyInputTypesAndLowPriority() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesAndLowPriority.kt"); diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt index 197f2461af3..f73f8c9de52 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/typeEnhancement.kt @@ -215,7 +215,7 @@ private object EnhancedTypeAnnotationDescriptor : AnnotationDescriptor { override fun toString() = "[EnhancedType]" } -internal class NotNullTypeParameter(override val delegate: SimpleType) : CustomTypeVariable, DelegatingSimpleType() { +internal class NotNullTypeParameter(override val delegate: SimpleType) : NotNullTypeVariable, DelegatingSimpleType() { override val isTypeVariable: Boolean get() = true diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeCapabilities.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeCapabilities.kt index 33924046ca8..428df8a0d3f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeCapabilities.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeCapabilities.kt @@ -27,6 +27,10 @@ interface CustomTypeVariable { fun substitutionResult(replacement: KotlinType): KotlinType } +// That interface is needed to provide information about definitely not null +// type parameters (e.g. from @NotNull annotation) to type system +interface NotNullTypeVariable : CustomTypeVariable + fun KotlinType.isCustomTypeVariable(): Boolean = (unwrap() as? CustomTypeVariable)?.isTypeVariable ?: false fun KotlinType.getCustomTypeVariable(): CustomTypeVariable? = (unwrap() as? CustomTypeVariable)?.let { diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt index c7755993f0d..058f1296209 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/ClassicTypeSystemContext.kt @@ -333,6 +333,10 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext { return builtIns.nothingType } + override fun anyType(): SimpleTypeMarker { + return builtIns.anyType + } + val builtIns: KotlinBuiltIns get() = throw UnsupportedOperationException("Not supported") override fun KotlinTypeMarker.makeDefinitelyNotNullOrNotNull(): KotlinTypeMarker { diff --git a/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt b/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt index 062d58c8a36..39ff35c9caf 100644 --- a/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt +++ b/core/type-system/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt @@ -49,6 +49,7 @@ interface TypeSystemBuiltInsContext { fun nullableNothingType(): SimpleTypeMarker fun nullableAnyType(): SimpleTypeMarker fun nothingType(): SimpleTypeMarker + fun anyType(): SimpleTypeMarker } interface TypeSystemTypeFactoryContext {