From 1fd0dec5e7508b76c1547e865340aae2d183e26f Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 29 Nov 2021 15:10:14 +0300 Subject: [PATCH] FE KT-47939 callable references to functional interface constructors Allow callable references to Kotlin 'fun interface' constructors. Prohibit callable references to Java SAM interface constructors. --- .../diagnostics/KtFirDataClassConverters.kt | 6 +++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 ++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 5 +++ ...irOldFrontendDiagnosticsTestGenerated.java | 18 ++++++--- ...DiagnosticsWithLightTreeTestGenerated.java | 18 ++++++--- .../diagnostics/FirJvmDiagnosticsList.kt | 1 + .../analysis/diagnostics/jvm/FirJvmErrors.kt | 1 + ...JvmOverridesBackwardCompatibilityHelper.kt | 4 +- .../jvm/checkers/JvmExpressionCheckers.kt | 3 +- ...JavaGenericVarianceViolationTypeChecker.kt | 9 ++--- ...SamInterfaceConstructorReferenceChecker.kt | 40 +++++++++++++++++++ ...FunInterfaceConstructorReferenceChecker.kt | 11 +++-- .../fir/declarations/deprecationUtils.kt | 5 +-- .../declarations/utils/FirDeclarationUtil.kt | 10 ++++- ...nterfaceConstructorReferenceCallChecker.kt | 30 ++++++++++++++ .../diagnostics/DefaultErrorMessagesJvm.java | 2 + .../resolve/jvm/diagnostics/ErrorsJvm.java | 2 + .../jvm/platform/JvmPlatformConfigurator.kt | 8 ++-- ...FunInterfaceConstructorReferenceChecker.kt | 3 ++ ...funInterfaceConstructorReferences_after.kt | 10 +++++ ...nInterfaceConstructorReferences_after.txt} | 0 ...nInterfaceConstructorReferences_before.kt} | 3 ++ ...nInterfaceConstructorReferences_before.txt | 12 ++++++ ...itFunInterfaceConstructorReferences.fir.kt | 7 ---- .../test/runners/DiagnosticTestGenerated.java | 18 ++++++--- .../kotlin/config/LanguageVersionSettings.kt | 1 + 26 files changed, 188 insertions(+), 43 deletions(-) create mode 100644 compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaSamInterfaceConstructorReferenceChecker.kt create mode 100644 compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/SamInterfaceConstructorReferenceCallChecker.kt create mode 100644 compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_after.kt rename compiler/testData/diagnostics/tests/funInterface/{prohibitFunInterfaceConstructorReferences.txt => funInterfaceConstructorReferences_after.txt} (100%) rename compiler/testData/diagnostics/tests/funInterface/{prohibitFunInterfaceConstructorReferences.kt => funInterfaceConstructorReferences_before.kt} (50%) create mode 100644 compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.txt delete mode 100644 compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.fir.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 94984ff1927..db5d4eff1be 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -4333,4 +4333,10 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJvmErrors.JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE) { firDiagnostic -> + JavaSamInterfaceConstructorReferenceImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 9b97496f54c..31ab0d5fdd7 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -3016,4 +3016,8 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = SpreadOnSignaturePolymorphicCallWarning::class } + abstract class JavaSamInterfaceConstructorReference : KtFirDiagnostic() { + override val diagnosticClass get() = JavaSamInterfaceConstructorReference::class + } + } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 5a305ced2b3..8f87afb756d 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3646,3 +3646,8 @@ internal class SpreadOnSignaturePolymorphicCallWarningImpl( override val token: ValidityToken, ) : KtFirDiagnostic.SpreadOnSignaturePolymorphicCallWarning(), KtAbstractFirDiagnostic +internal class JavaSamInterfaceConstructorReferenceImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.JavaSamInterfaceConstructorReference(), KtAbstractFirDiagnostic + diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index a0ee560c1ef..4ad93d17a2d 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -10919,6 +10919,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/funInterface/basicFunInterfaceDisabled.kt"); } + @Test + @TestMetadata("funInterfaceConstructorReferences_after.kt") + public void testFunInterfaceConstructorReferences_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_after.kt"); + } + + @Test + @TestMetadata("funInterfaceConstructorReferences_before.kt") + public void testFunInterfaceConstructorReferences_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.kt"); + } + @Test @TestMetadata("funInterfaceConversionOnReceiver.kt") public void testFunInterfaceConversionOnReceiver() throws Exception { @@ -10961,12 +10973,6 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/funInterface/noCompatibilityResolveForFunInterfaces.kt"); } - @Test - @TestMetadata("prohibitFunInterfaceConstructorReferences.kt") - public void testProhibitFunInterfaceConstructorReferences() throws Exception { - runTest("compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt"); - } - @Test @TestMetadata("resolveFunInterfaceWithoutMainMethod.kt") public void testResolveFunInterfaceWithoutMainMethod() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 99a64300bc9..97268c13819 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -10919,6 +10919,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/funInterface/basicFunInterfaceDisabled.kt"); } + @Test + @TestMetadata("funInterfaceConstructorReferences_after.kt") + public void testFunInterfaceConstructorReferences_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_after.kt"); + } + + @Test + @TestMetadata("funInterfaceConstructorReferences_before.kt") + public void testFunInterfaceConstructorReferences_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.kt"); + } + @Test @TestMetadata("funInterfaceConversionOnReceiver.kt") public void testFunInterfaceConversionOnReceiver() throws Exception { @@ -10961,12 +10973,6 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/funInterface/noCompatibilityResolveForFunInterfaces.kt"); } - @Test - @TestMetadata("prohibitFunInterfaceConstructorReferences.kt") - public void testProhibitFunInterfaceConstructorReferences() throws Exception { - runTest("compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt"); - } - @Test @TestMetadata("resolveFunInterfaceWithoutMainMethod.kt") public void testResolveFunInterfaceWithoutMainMethod() throws Exception { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt index 74c1734f162..21b602fdd9b 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt @@ -183,5 +183,6 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { ProhibitSpreadOnSignaturePolymorphicCall, PositioningStrategy.SPREAD_OPERATOR ) + val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error() } } diff --git a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt index 80b286eb127..032d4215f02 100644 --- a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt +++ b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt @@ -125,6 +125,7 @@ object FirJvmErrors { val SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val CONCURRENT_HASH_MAP_CONTAINS_OPERATOR by deprecationError0(ProhibitConcurrentHashMapContains) val SPREAD_ON_SIGNATURE_POLYMORPHIC_CALL by deprecationError0(ProhibitSpreadOnSignaturePolymorphicCall, SourceElementPositioningStrategies.SPREAD_OPERATOR) + val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error0() init { RootDiagnosticRendererFactory.registerFactory(FirJvmErrorsDefaultMessages) diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/FirJvmOverridesBackwardCompatibilityHelper.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/FirJvmOverridesBackwardCompatibilityHelper.kt index 82a3dcc5a36..c4134bdb968 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/FirJvmOverridesBackwardCompatibilityHelper.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/FirJvmOverridesBackwardCompatibilityHelper.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.utils.isAbstract import org.jetbrains.kotlin.fir.declarations.utils.isFinal import org.jetbrains.kotlin.fir.declarations.utils.isInterface +import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement import org.jetbrains.kotlin.fir.dispatchReceiverTypeOrNull import org.jetbrains.kotlin.fir.originalOrSelf import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol @@ -41,7 +42,6 @@ import org.jetbrains.kotlin.name.ClassId * Note that, in case of multi-override, if any super member requires `override`, then the `override` keyword cannot be omitted. */ object FirJvmOverridesBackwardCompatibilityHelper : FirOverridesBackwardCompatibilityHelper { - private val javaOrigin = setOf(FirDeclarationOrigin.Java, FirDeclarationOrigin.Enhancement) private val platformDependentAnnotation = ClassId.fromString("kotlin/internal/PlatformDependent") override fun overrideCanBeOmitted( @@ -71,7 +71,7 @@ object FirJvmOverridesBackwardCompatibilityHelper : FirOverridesBackwardCompatib return true } - if (originalMember.origin !in javaOrigin) return false + if (!originalMember.isJavaOrEnhancement) return false val containingClassName = originalMember.containingClass()?.classId?.asSingleFqName()?.toUnsafe() ?: return false // If the super class is mapped to a Kotlin built-in class, then we don't require `override` keyword. if (JavaToKotlinClassMap.mapKotlinToJava(containingClassName) != null) { diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt index 67cb5a898ce..2b339eb2399 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt @@ -16,7 +16,8 @@ object JvmExpressionCheckers : ExpressionCheckers() { override val qualifiedAccessExpressionCheckers: Set get() = setOf( - FirInterfaceDefaultMethodCallChecker + FirInterfaceDefaultMethodCallChecker, + FirJavaSamInterfaceConstructorReferenceChecker ) override val functionCallCheckers: Set diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaGenericVarianceViolationTypeChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaGenericVarianceViolationTypeChecker.kt index 38abc6ae544..6f0bed8426f 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaGenericVarianceViolationTypeChecker.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaGenericVarianceViolationTypeChecker.kt @@ -5,13 +5,13 @@ package org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.StandardTypes import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker -import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors -import org.jetbrains.kotlin.diagnostics.reportOn -import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.argumentMapping import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol @@ -46,11 +46,10 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs * doesn't do anything to prevent this from happening. */ object FirJavaGenericVarianceViolationTypeChecker : FirFunctionCallChecker() { - private val javaOrigin = setOf(FirDeclarationOrigin.Java, FirDeclarationOrigin.Enhancement) override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) { val calleeFunction = expression.calleeReference.toResolvedCallableSymbol() as? FirFunctionSymbol<*> ?: return - if (calleeFunction.originalOrSelf().origin !in javaOrigin) { + if (!calleeFunction.originalOrSelf().isJavaOrEnhancement) { return } val argumentMapping = expression.argumentMapping ?: return diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaSamInterfaceConstructorReferenceChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaSamInterfaceConstructorReferenceChecker.kt new file mode 100644 index 00000000000..6455a89b5fb --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaSamInterfaceConstructorReferenceChecker.kt @@ -0,0 +1,40 @@ +/* + * 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.fir.analysis.jvm.checkers.expression + +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.utils.isFun +import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement +import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol + +object FirJavaSamInterfaceConstructorReferenceChecker : FirQualifiedAccessExpressionChecker() { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression !is FirCallableReferenceAccess) return + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitJavaSamInterfaceConstructorReference)) return + + val reference = expression.calleeReference as? FirResolvedNamedReference ?: return + val referredSymbol = reference.resolvedSymbol + + if (referredSymbol is FirNamedFunctionSymbol && + referredSymbol.origin == FirDeclarationOrigin.SamConstructor + ) { + val samClassSymbol = referredSymbol.resolvedReturnTypeRef.toRegularClassSymbol(context.session) ?: return + if (samClassSymbol.isFun && samClassSymbol.isJavaOrEnhancement) { + reporter.reportOn(reference.source, FirJvmErrors.JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE, context) + } + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunInterfaceConstructorReferenceChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunInterfaceConstructorReferenceChecker.kt index b2d2aeda35a..75ebc9d5c2e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunInterfaceConstructorReferenceChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunInterfaceConstructorReferenceChecker.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol import org.jetbrains.kotlin.diagnostics.DiagnosticReporter @@ -12,6 +13,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CON import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.utils.isFun +import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.resolved @@ -20,15 +22,18 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol object FirFunInterfaceConstructorReferenceChecker : FirQualifiedAccessExpressionChecker() { override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { if (expression !is FirCallableReferenceAccess) return + if (context.languageVersionSettings.supportsFeature(LanguageFeature.AllowKotlinFunInterfaceConstructorReference)) return val reference = expression.calleeReference.resolved ?: return val referredSymbol = reference.resolvedSymbol if (referredSymbol is FirNamedFunctionSymbol && - referredSymbol.origin == FirDeclarationOrigin.SamConstructor && - referredSymbol.resolvedReturnTypeRef.toRegularClassSymbol(context.session)?.isFun == true + referredSymbol.origin == FirDeclarationOrigin.SamConstructor ) { - reporter.reportOn(reference.source, FUN_INTERFACE_CONSTRUCTOR_REFERENCE, context) + val samClassSymbol = referredSymbol.resolvedReturnTypeRef.toRegularClassSymbol(context.session) ?: return + if (samClassSymbol.isFun && !samClassSymbol.isJavaOrEnhancement) { + reporter.reportOn(reference.source, FUN_INTERFACE_CONSTRUCTOR_REFERENCE, context) + } } } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt index fabbfbd4f2e..2bcf12b2b2f 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.declarations.utils.isJavaOrEnhancement import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirNamedReference import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol @@ -26,8 +27,6 @@ import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue import org.jetbrains.kotlin.resolve.deprecation.SimpleDeprecationInfo import org.jetbrains.kotlin.utils.addToStdlib.safeAs -private val JAVA_ORIGINS = setOf(FirDeclarationOrigin.Java, FirDeclarationOrigin.Enhancement) - fun FirBasedSymbol<*>.getDeprecation(callSite: FirElement?): DeprecationInfo? { return when (this) { is FirPropertySymbol -> @@ -46,7 +45,7 @@ fun FirBasedSymbol<*>.getDeprecation(callSite: FirElement?): DeprecationInfo? { fun FirAnnotationContainer.getDeprecationInfos(currentVersion: ApiVersion): DeprecationsPerUseSite { val deprecationByUseSite = mutableMapOf() - val fromJava = JAVA_ORIGINS.contains(this.safeAs()?.origin) + val fromJava = this.safeAs()?.isJavaOrEnhancement == true annotations.extractDeprecationInfoPerUseSite(currentVersion, fromJava).toMap(deprecationByUseSite) if (this is FirProperty) { diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/FirDeclarationUtil.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/FirDeclarationUtil.kt index 48ac81ab63e..ad4503da99b 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/FirDeclarationUtil.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/FirDeclarationUtil.kt @@ -8,7 +8,11 @@ package org.jetbrains.kotlin.fir.declarations.utils import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.resolvedSymbol -import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.coneTypeSafe import org.jetbrains.kotlin.name.ClassId @@ -49,4 +53,8 @@ inline val FirDeclaration.isFromLibrary: Boolean inline val FirDeclaration.isSynthetic: Boolean get() = origin == FirDeclarationOrigin.Synthetic +inline val FirDeclaration.isJavaOrEnhancement: Boolean + get() = origin == FirDeclarationOrigin.Java || origin == FirDeclarationOrigin.Enhancement +inline val FirBasedSymbol<*>.isJavaOrEnhancement: Boolean + get() = origin == FirDeclarationOrigin.Java || origin == FirDeclarationOrigin.Enhancement diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/SamInterfaceConstructorReferenceCallChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/SamInterfaceConstructorReferenceCallChecker.kt new file mode 100644 index 00000000000..1bb6243f097 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/SamInterfaceConstructorReferenceCallChecker.kt @@ -0,0 +1,30 @@ +/* + * 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.jvm.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker +import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.util.isCallableReference +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm +import org.jetbrains.kotlin.resolve.sam.SamConstructorDescriptor + +object SamInterfaceConstructorReferenceCallChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + if (!context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitJavaSamInterfaceConstructorReference)) return + + val resultingDescriptor = resolvedCall.resultingDescriptor + if (resultingDescriptor !is SamConstructorDescriptor || !resolvedCall.call.isCallableReference()) return + + if (!resultingDescriptor.baseDescriptorForSynthetic.isFun) { + context.trace.report( + ErrorsJvm.JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE.on(reportOn) + ) + } + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index d6edfd392e9..b302881be9a 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -223,6 +223,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(TYPEOF_SUSPEND_TYPE, "Suspend functional types are not supported in typeOf"); MAP.put(TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND, "Non-reified type parameters with recursive bounds are not supported yet: {0}", STRING); + + MAP.put(JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE, "Java SAM interface constructor references are prohibited"); } @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 5ebdbb859b8..ced908cc768 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -202,6 +202,8 @@ public interface ErrorsJvm { DiagnosticFactory0 TYPEOF_SUSPEND_TYPE = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE = DiagnosticFactory0.create(ERROR); + @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index f52c6c89d12..67083e155f6 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -6,9 +6,10 @@ package org.jetbrains.kotlin.resolve.jvm.platform import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMapper -import org.jetbrains.kotlin.config.LanguageFeature -import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.container.* +import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver +import org.jetbrains.kotlin.container.StorageComponentContainer +import org.jetbrains.kotlin.container.useImpl +import org.jetbrains.kotlin.container.useInstance import org.jetbrains.kotlin.load.java.sam.JvmSamConversionOracle import org.jetbrains.kotlin.resolve.PlatformConfiguratorBase import org.jetbrains.kotlin.resolve.checkers.BigFunctionTypeAvailabilityChecker @@ -58,6 +59,7 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase( ApiVersionIsAtLeastArgumentsChecker, InconsistentOperatorFromJavaCallChecker, PolymorphicSignatureCallChecker, + SamInterfaceConstructorReferenceCallChecker, ), additionalTypeCheckers = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/FunInterfaceConstructorReferenceChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/FunInterfaceConstructorReferenceChecker.kt index 6f58fe50611..202bf5e200f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/FunInterfaceConstructorReferenceChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/FunInterfaceConstructorReferenceChecker.kt @@ -6,6 +6,7 @@ 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.util.isCallableReference import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall @@ -13,6 +14,8 @@ import org.jetbrains.kotlin.resolve.sam.SamConstructorDescriptor object FunInterfaceConstructorReferenceChecker : CallChecker { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.AllowKotlinFunInterfaceConstructorReference)) return + val resultingDescriptor = resolvedCall.resultingDescriptor if (resultingDescriptor !is SamConstructorDescriptor || !resolvedCall.call.isCallableReference()) return diff --git a/compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_after.kt b/compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_after.kt new file mode 100644 index 00000000000..067f2dddae7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_after.kt @@ -0,0 +1,10 @@ +// FIR_IDENTICAL +// !LANGUAGE: +AllowKotlinFunInterfaceConstructorReference +ProhibitJavaSamInterfaceConstructorReference + +fun interface Foo { + fun run() +} + +val x = ::Foo +val y = Foo { } +val z = ::Runnable diff --git a/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.txt b/compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_after.txt similarity index 100% rename from compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.txt rename to compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_after.txt diff --git a/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt b/compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.kt similarity index 50% rename from compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt rename to compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.kt index aa4297708f0..53ba1ae6c78 100644 --- a/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt +++ b/compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.kt @@ -1,3 +1,6 @@ +// FIR_IDENTICAL +// !LANGUAGE: -AllowKotlinFunInterfaceConstructorReference -ProhibitJavaSamInterfaceConstructorReference + fun interface Foo { fun run() } diff --git a/compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.txt b/compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.txt new file mode 100644 index 00000000000..4dc96ea4dd9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.txt @@ -0,0 +1,12 @@ +package + +public val x: kotlin.reflect.KFunction1<() -> kotlin.Unit, Foo> +public val y: Foo +public val z: kotlin.reflect.KFunction1<() -> kotlin.Unit, java.lang.Runnable> + +public fun interface Foo { + 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 +} diff --git a/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.fir.kt b/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.fir.kt deleted file mode 100644 index e34a72ac745..00000000000 --- a/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.fir.kt +++ /dev/null @@ -1,7 +0,0 @@ -fun interface Foo { - fun run() -} - -val x = ::Foo -val y = Foo { } -val z = ::Runnable diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 59719f9e08a..9a1cb321cc0 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -10925,6 +10925,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/funInterface/basicFunInterfaceDisabled.kt"); } + @Test + @TestMetadata("funInterfaceConstructorReferences_after.kt") + public void testFunInterfaceConstructorReferences_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_after.kt"); + } + + @Test + @TestMetadata("funInterfaceConstructorReferences_before.kt") + public void testFunInterfaceConstructorReferences_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/funInterface/funInterfaceConstructorReferences_before.kt"); + } + @Test @TestMetadata("funInterfaceConversionOnReceiver.kt") public void testFunInterfaceConversionOnReceiver() throws Exception { @@ -10967,12 +10979,6 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/funInterface/noCompatibilityResolveForFunInterfaces.kt"); } - @Test - @TestMetadata("prohibitFunInterfaceConstructorReferences.kt") - public void testProhibitFunInterfaceConstructorReferences() throws Exception { - runTest("compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt"); - } - @Test @TestMetadata("resolveFunInterfaceWithoutMainMethod.kt") public void testResolveFunInterfaceWithoutMainMethod() throws Exception { diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index ba4739c143f..dd5a8153823 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -244,6 +244,7 @@ enum class LanguageFeature( ConsiderExtensionReceiverFromConstrainsInLambda(KOTLIN_1_7, kind = BUG_FIX), // KT-49832 ProperInternalVisibilityCheckInImportingScope(KOTLIN_1_7, kind = BUG_FIX), InlineClassImplementationByDelegation(KOTLIN_1_7), + AllowKotlinFunInterfaceConstructorReference(KOTLIN_1_7), // 1.8