diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index 5f0d932387c..414c2e699ff 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -24476,6 +24476,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt"); } + @TestMetadata("unitConversionDisabledForSimpleArguments.kt") + public void testUnitConversionDisabledForSimpleArguments() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.kt"); + } + @TestMetadata("unitConversionForAllKinds.kt") public void testUnitConversionForAllKinds() throws Exception { runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 79bb7f1bdd9..3f342be2f74 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -49,7 +49,8 @@ private val DEFAULT_CALL_CHECKERS = listOf( PrimitiveNumericComparisonCallChecker, LambdaWithSuspendModifierCallChecker, UselessElvisCallChecker(), ResultTypeWithNullableOperatorsChecker(), NullableVarargArgumentCallChecker, NamedFunAsExpressionChecker, ContractNotAllowedCallChecker, ReifiedTypeParameterSubstitutionChecker(), - MissingDependencySupertypeChecker.ForCalls, AbstractClassInstantiationChecker, SuspendConversionCallChecker + MissingDependencySupertypeChecker.ForCalls, AbstractClassInstantiationChecker, SuspendConversionCallChecker, + UnitConversionCallChecker ) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/UnitConversionCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/UnitConversionCallChecker.kt new file mode 100644 index 00000000000..b541644a433 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/UnitConversionCallChecker.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2020 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.calls.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.tower.NewResolvedCallImpl +import org.jetbrains.kotlin.resolve.calls.tower.psiCallArgument + +object UnitConversionCallChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + if (resolvedCall !is NewResolvedCallImpl<*>) return + + if (context.languageVersionSettings.supportsFeature(LanguageFeature.UnitConversion)) return + + // lambdas are working since 1.0, callable references are handled as part of reference adaptation + // => here we're checking only simple argument + val argumentsWithUnitConversion = resolvedCall.resolvedCallAtom.argumentsWithUnitConversion + + for (argumentWithUnitConversion in argumentsWithUnitConversion.keys) { + context.trace.report( + Errors.UNSUPPORTED_FEATURE.on( + argumentWithUnitConversion.psiCallArgument.valueArgument.asElement(), + LanguageFeature.UnitConversion to context.languageVersionSettings + ) + ) + } + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/UnitTypeConversions.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/UnitTypeConversions.kt index 62580abda64..5f0eb6ed0f4 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/UnitTypeConversions.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/UnitTypeConversions.kt @@ -75,6 +75,8 @@ object UnitTypeConversions : ParameterTypeConversion { suspendFunction = expectedParameterType.isSuspendFunctionType ) + candidate.resolvedCall.registerArgumentWithUnitConversion(argument, nonUnitReturnedParameterType) + candidate.addDiagnostic(LowerPriorityToPreserveCompatibility) return nonUnitReturnedParameterType diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt index 778061a9a6e..8032a21e9bd 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionAtoms.kt @@ -69,6 +69,7 @@ abstract class ResolvedCallAtom : ResolvedAtom() { abstract val knownParametersSubstitutor: TypeSubstitutor abstract val argumentsWithConversion: Map abstract val argumentsWithSuspendConversion: Map + abstract val argumentsWithUnitConversion: Map abstract val argumentsWithConstantConversion: Map } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt index 31ee44b360a..0957bf240ca 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt @@ -193,6 +193,7 @@ class MutableResolvedCallAtom( lateinit var argumentToCandidateParameter: Map private var samAdapterMap: HashMap? = null private var suspendAdapterMap: HashMap? = null + private var unitAdapterMap: HashMap? = null private var signedUnsignedConstantConversions: HashMap? = null val hasSamConversion: Boolean @@ -207,6 +208,9 @@ class MutableResolvedCallAtom( override val argumentsWithSuspendConversion: Map get() = suspendAdapterMap ?: emptyMap() + override val argumentsWithUnitConversion: Map + get() = unitAdapterMap ?: emptyMap() + override val argumentsWithConstantConversion: Map get() = signedUnsignedConstantConversions ?: emptyMap() @@ -224,6 +228,13 @@ class MutableResolvedCallAtom( suspendAdapterMap!![argument] = convertedType } + fun registerArgumentWithUnitConversion(argument: KotlinCallArgument, convertedType: UnwrappedType) { + if (unitAdapterMap == null) + unitAdapterMap = hashMapOf() + + unitAdapterMap!![argument] = convertedType + } + fun registerArgumentWithConstantConversion(argument: KotlinCallArgument, convertedConstant: IntegerValueTypeConstant) { if (signedUnsignedConstantConversions == null) signedUnsignedConstantConversions = hashMapOf() diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.fir.kt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.fir.kt new file mode 100644 index 00000000000..f8db62e5ae8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.fir.kt @@ -0,0 +1,29 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +object Test1 { + fun foo(f: () -> Int) {} + + object Scope { + fun foo(f: () -> Unit) {} + + fun test(f: () -> Int) { + foo(f) + } + } +} + +object Test2 { + fun interface KRunnable { + fun run() + } + + fun foo(f: () -> Unit) {} + + object Scope1 { + fun foo(f: KRunnable) {} + + fun test(f: () -> Int) { + foo(f) + } + } +} diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt index e69de29bb2d..a10eec81b5f 100644 --- a/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt @@ -0,0 +1,29 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +object Test1 { + fun foo(f: () -> Int) {} + + object Scope { + fun foo(f: () -> Unit) {} + + fun test(f: () -> Int) { + foo(f) + } + } +} + +object Test2 { + fun interface KRunnable { + fun run() + } + + fun foo(f: () -> Unit) {} + + object Scope1 { + fun foo(f: KRunnable) {} + + fun test(f: () -> Int) { + foo(f) + } + } +} diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.txt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.txt new file mode 100644 index 00000000000..486102509b2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.txt @@ -0,0 +1,42 @@ +package + +public object Test1 { + private constructor Test1() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ f: () -> kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public object Scope { + private constructor Scope() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ f: () -> kotlin.Unit): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun test(/*0*/ f: () -> kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public object Test2 { + private constructor Test2() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ f: () -> kotlin.Unit): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public fun interface KRunnable { + 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 fun run(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public object Scope1 { + private constructor Scope1() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(/*0*/ f: Test2.KRunnable): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun test(/*0*/ f: () -> kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.fir.kt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.fir.kt new file mode 100644 index 00000000000..d04e50feb9b --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.fir.kt @@ -0,0 +1,16 @@ +// !LANGUAGE: -SuspendConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun foo(f: () -> Unit) {} + +fun bar(): Int = 0 + +abstract class SubInt : () -> Int + +fun test(f: () -> String, s: SubInt) { + foo { "lambda" } + foo(::bar) + + foo(f) + foo(s) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.kt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.kt new file mode 100644 index 00000000000..b8bf60ebd7f --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.kt @@ -0,0 +1,16 @@ +// !LANGUAGE: -SuspendConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION + +fun foo(f: () -> Unit) {} + +fun bar(): Int = 0 + +abstract class SubInt : () -> Int + +fun test(f: () -> String, s: SubInt) { + foo { "lambda" } + foo(::bar) + + foo(f) + foo(s) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.txt b/compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.txt new file mode 100644 index 00000000000..1c6a0199f7a --- /dev/null +++ b/compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.txt @@ -0,0 +1,13 @@ +package + +public fun bar(): kotlin.Int +public fun foo(/*0*/ f: () -> kotlin.Unit): kotlin.Unit +public fun test(/*0*/ f: () -> kotlin.String, /*1*/ s: SubInt): kotlin.Unit + +public abstract class SubInt : () -> kotlin.Int { + public constructor SubInt() + 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*/ /*fake_override*/ fun invoke(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index e020359378f..f189d1f5d96 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -24558,6 +24558,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt"); } + @TestMetadata("unitConversionDisabledForSimpleArguments.kt") + public void testUnitConversionDisabledForSimpleArguments() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.kt"); + } + @TestMetadata("unitConversionForAllKinds.kt") public void testUnitConversionForAllKinds() throws Exception { runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index a76fe38530c..820e5496444 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -24478,6 +24478,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionCompatibility.kt"); } + @TestMetadata("unitConversionDisabledForSimpleArguments.kt") + public void testUnitConversionDisabledForSimpleArguments() throws Exception { + runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionDisabledForSimpleArguments.kt"); + } + @TestMetadata("unitConversionForAllKinds.kt") public void testUnitConversionForAllKinds() throws Exception { runTest("compiler/testData/diagnostics/tests/unitConversion/unitConversionForAllKinds.kt");