diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java index 21b61937088..5a2543620c5 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java @@ -2855,6 +2855,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/samConstructors"), Pattern.compile("^([^.]+)\\.kt$"), null, true); } + @TestMetadata("funInterfaceConstructorReference.kt") + public void testFunInterfaceConstructorReference() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/samConstructors/funInterfaceConstructorReference.kt"); + } + @TestMetadata("genericSam.kt") public void testGenericSam() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/samConstructors/genericSam.kt"); diff --git a/compiler/fir/analysis-tests/testData/resolve/samConstructors/funInterfaceConstructorReference.fir.txt b/compiler/fir/analysis-tests/testData/resolve/samConstructors/funInterfaceConstructorReference.fir.txt new file mode 100644 index 00000000000..85d8f45629c --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/samConstructors/funInterfaceConstructorReference.fir.txt @@ -0,0 +1,7 @@ +FILE: funInterfaceConstructorReference.kt + public abstract interface Test : R|kotlin/Any| { + public abstract fun foo(): R|kotlin/Unit| + + } + public final val f: R|kotlin/reflect/KFunction1, Test>| = ::R|/Test| + public get(): R|kotlin/reflect/KFunction1, Test>| diff --git a/compiler/fir/analysis-tests/testData/resolve/samConstructors/funInterfaceConstructorReference.kt b/compiler/fir/analysis-tests/testData/resolve/samConstructors/funInterfaceConstructorReference.kt new file mode 100644 index 00000000000..d4305b000dc --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/samConstructors/funInterfaceConstructorReference.kt @@ -0,0 +1,5 @@ +fun interface Test { + fun foo() +} + +val f = ::Test diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index 98af733d5a3..91600c79a39 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -3227,6 +3227,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/samConstructors"), Pattern.compile("^([^.]+)\\.kt$"), null, true); } + @Test + @TestMetadata("funInterfaceConstructorReference.kt") + public void testFunInterfaceConstructorReference() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/samConstructors/funInterfaceConstructorReference.kt"); + } + @Test @TestMetadata("genericSam.kt") public void testGenericSam() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index cadf68deae1..7f1ff18e0ea 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -3264,6 +3264,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/samConstructors"), Pattern.compile("^([^.]+)\\.kt$"), null, true); } + @Test + @TestMetadata("funInterfaceConstructorReference.kt") + public void testFunInterfaceConstructorReference() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/samConstructors/funInterfaceConstructorReference.kt"); + } + @Test @TestMetadata("genericSam.kt") public void testGenericSam() throws Exception { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index da86f3e6a21..84598acf2a0 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -479,6 +479,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION by error() } + val FUN_INTERFACES by object : DiagnosticGroup("Fun interfaces") { + val FUN_INTERFACE_CONSTRUCTOR_REFERENCE by error(PositioningStrategy.REFERENCE_BY_QUALIFIED) + } + val PROPERTIES_AND_ACCESSORS by object : DiagnosticGroup("Properties & accessors") { val ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS by error(PositioningStrategy.MODALITY_MODIFIER) { parameter("property") diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 79aa53f0ead..de3d6770fe3 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -296,6 +296,9 @@ object FirErrors { val FORBIDDEN_VARARG_PARAMETER_TYPE by error1(SourceElementPositioningStrategies.PARAMETER_VARARG_MODIFIER) val VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION by error0() + // Fun interfaces + val FUN_INTERFACE_CONSTRUCTOR_REFERENCE by error0(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) + // Properties & accessors val ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS by error2(SourceElementPositioningStrategies.MODALITY_MODIFIER) val PRIVATE_PROPERTY_IN_INTERFACE by error0(SourceElementPositioningStrategies.VISIBILITY_MODIFIER) 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 new file mode 100644 index 00000000000..de130fb32ae --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunInterfaceConstructorReferenceChecker.kt @@ -0,0 +1,36 @@ +/* + * 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.checkers.expression + +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.hasModifier +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClass +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CONSTRUCTOR_REFERENCE +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.declarations.isInterface +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.lexer.KtTokens + +object FirFunInterfaceConstructorReferenceChecker : FirQualifiedAccessChecker() { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression !is FirCallableReferenceAccess || expression is FirGetClassCall) return + + val reference = expression.calleeReference as? FirResolvedNamedReference ?: return + val referredDeclaration = reference.resolvedSymbol.fir + + if (referredDeclaration is FirSimpleFunction && + referredDeclaration.returnTypeRef.toRegularClass(context.session).isFunInterface() + ) { + reporter.reportOn(reference.source, FUN_INTERFACE_CONSTRUCTOR_REFERENCE, context) + } + } + + private fun FirRegularClass?.isFunInterface() = this != null && isInterface && hasModifier(KtTokens.FUN_KEYWORD) +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index c4678d2e108..e990e6555e8 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -107,6 +107,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_PROPERT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FINAL_UPPER_BOUND import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_DECLARATION_WITH_NO_NAME +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CONSTRUCTOR_REFERENCE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GENERIC_THROWABLE_SUBCLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HIDDEN import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_CONST_EXPRESSION @@ -685,6 +686,9 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { map.put(FORBIDDEN_VARARG_PARAMETER_TYPE, "Forbidden vararg parameter type: {0}", RENDER_TYPE) map.put(VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION, "A type annotation is required on a value parameter") + // Fun interfaces + map.put(FUN_INTERFACE_CONSTRUCTOR_REFERENCE, "Functional interface constructor references are prohibited") + // Properties & accessors map.put( ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt index f6b5fbff93e..4c9b6a1388c 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt @@ -28,6 +28,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirTypeParameterInQualifiedAccessChecker, FirSealedClassConstructorCallChecker, FirUninitializedEnumChecker, + FirFunInterfaceConstructorReferenceChecker, ) override val functionCallCheckers: Set = setOf( 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 c8fdd583f33..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 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt b/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt index aa4297708f0..d515dd88574 100644 --- a/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt +++ b/compiler/testData/diagnostics/tests/funInterface/prohibitFunInterfaceConstructorReferences.kt @@ -1,3 +1,5 @@ +// FIR_IDENTICAL + fun interface Foo { fun run() } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 32a54c12bc9..7c59608da7e 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -1338,6 +1338,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.FUN_INTERFACE_CONSTRUCTOR_REFERENCE) { firDiagnostic -> + FunInterfaceConstructorReferenceImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS) { firDiagnostic -> AbstractPropertyInNonAbstractClassImpl( firSymbolBuilder.buildSymbol(firDiagnostic.a as FirDeclaration), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index c3951071c4f..747ba79e83c 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -949,6 +949,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = ValueParameterWithNoTypeAnnotation::class } + abstract class FunInterfaceConstructorReference : KtFirDiagnostic() { + override val diagnosticClass get() = FunInterfaceConstructorReference::class + } + abstract class AbstractPropertyInNonAbstractClass : KtFirDiagnostic() { override val diagnosticClass get() = AbstractPropertyInNonAbstractClass::class abstract val property: KtSymbol diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 04ca5ab16ad..00ac575bc0a 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1529,6 +1529,13 @@ internal class ValueParameterWithNoTypeAnnotationImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class FunInterfaceConstructorReferenceImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.FunInterfaceConstructorReference(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class AbstractPropertyInNonAbstractClassImpl( override val property: KtSymbol, override val containingClass: KtSymbol,