diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/delegationInInterface.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/delegationInInterface.kt index b9bcdd74d4c..e334de22256 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/delegationInInterface.kt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/delegationInInterface.kt @@ -1,9 +1,9 @@ open class A -interface B : A by a { +interface B : A by a { val a: A } val test = A() -interface C : A by test +interface C : A by test 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 763c7c86fe5..32bd4afbd2e 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 @@ -60,6 +60,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val NOT_A_LOOP_LABEL by error() val VARIABLE_EXPECTED by error(PositioningStrategy.ASSIGNMENT_LHS) val DELEGATION_IN_INTERFACE by error() + val DELEGATION_NOT_TO_INTERFACE by error() val NESTED_CLASS_NOT_ALLOWED by error(PositioningStrategy.DECLARATION_NAME) { parameter("declaration") } 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 4c733d81bfb..628048aee45 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 @@ -92,6 +92,7 @@ object FirErrors { val NOT_A_LOOP_LABEL by error0() val VARIABLE_EXPECTED by error0(SourceElementPositioningStrategies.ASSIGNMENT_LHS) val DELEGATION_IN_INTERFACE by error0() + val DELEGATION_NOT_TO_INTERFACE by error0() val NESTED_CLASS_NOT_ALLOWED by error1(SourceElementPositioningStrategies.DECLARATION_NAME) val INCORRECT_CHARACTER_LITERAL by error0() val EMPTY_CHARACTER_LITERAL by error0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index 566dc2ad835..d3e4e60ecd0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -521,6 +521,8 @@ private val FirSimpleFunction.matchesHashCodeSignature: Boolean private val FirSimpleFunction.matchesToStringSignature: Boolean get() = valueParameters.isEmpty() +val Name.isDelegated: Boolean get() = asString().startsWith("<\$\$delegate_") + val ConeTypeProjection.isConflictingOrNotInvariant: Boolean get() = kind != ProjectionKind.INVARIANT || this is ConeKotlinTypeConflictingProjection fun checkTypeMismatch( diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSupertypesChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSupertypesChecker.kt index 8a22186be96..48ba8ae4b4c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSupertypesChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSupertypesChecker.kt @@ -7,17 +7,14 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.fir.analysis.checkers.checkInconsistentTypeParameters +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fir.analysis.checkers.* import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.checkers.extractTypeRefAndSourceFromTypeArgument -import org.jetbrains.kotlin.fir.analysis.checkers.isConflictingOrNotInvariant import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics -import org.jetbrains.kotlin.fir.declarations.FirClass -import org.jetbrains.kotlin.fir.declarations.FirRegularClass -import org.jetbrains.kotlin.fir.declarations.modality +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol @@ -85,6 +82,9 @@ object FirSupertypesChecker : FirClassChecker() { val fullyExpandedType = coneType.fullyExpandedType(context.session) val symbol = fullyExpandedType.toSymbol(context.session) + if (symbol is FirRegularClassSymbol && symbol.classId == StandardClassIds.Enum) { + reporter.reportOn(superTypeRef.source, FirErrors.CLASS_CANNOT_BE_EXTENDED_DIRECTLY, symbol, context) + } if (coneType.typeArguments.isNotEmpty()) { for ((index, typeArgument) in coneType.typeArguments.withIndex()) { @@ -107,9 +107,19 @@ object FirSupertypesChecker : FirClassChecker() { } } } + } + } - if (symbol is FirRegularClassSymbol && symbol.classId == StandardClassIds.Enum) { - reporter.reportOn(superTypeRef.source, FirErrors.CLASS_CANNOT_BE_EXTENDED_DIRECTLY, symbol, context) + for (subDeclaration in declaration.declarations) { + if (subDeclaration is FirField) { + if (subDeclaration.visibility == Visibilities.Local && + subDeclaration.name.isSpecial && + subDeclaration.name.isDelegated + ) { + val subDeclFir = subDeclaration.returnTypeRef.toRegularClass(context.session) + if (subDeclFir is FirRegularClass && subDeclFir.classKind != ClassKind.INTERFACE) { + reporter.reportOn(subDeclaration.returnTypeRef.source, FirErrors.DELEGATION_NOT_TO_INTERFACE, context) + } } } } 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 838d0ed97bb..03cc7eb5e02 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 @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics +import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages import org.jetbrains.kotlin.diagnostics.rendering.LanguageFeatureMessageRenderer import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_POSITION_VARIANCE import org.jetbrains.kotlin.diagnostics.rendering.Renderers.STRING @@ -16,10 +17,10 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NAME import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NOT_RENDERED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NULLABLE_STRING import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_CLASS_OR_OBJECT +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_COLLECTION_OF_TYPES import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOLS -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_COLLECTION_OF_TYPES import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.TO_STRING import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VARIABLE_NAME import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VISIBILITY @@ -99,6 +100,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATION_IN_INTERFACE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATION_NOT_TO_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_MODIFIER_PAIR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_SINCE_KOTLIN_OUTSIDE_KOTLIN_SUBPACKAGE @@ -414,6 +416,7 @@ class FirDefaultErrorMessages { map.put(NOT_A_LOOP_LABEL, "The label does not denote a loop") // * map.put(VARIABLE_EXPECTED, "Variable expected") map.put(DELEGATION_IN_INTERFACE, "Interfaces cannot use delegation") + map.put(DELEGATION_NOT_TO_INTERFACE, "Only interfaces can be delegated to") map.put(NESTED_CLASS_NOT_ALLOWED, "{0} is not allowed here", TO_STRING) map.put(VAL_OR_VAR_ON_LOOP_PARAMETER, "''{0}'' on loop parameter is not allowed", TO_STRING) map.put(VAL_OR_VAR_ON_FUN_PARAMETER, "''{0}'' on function parameter is not allowed", TO_STRING) diff --git a/compiler/testData/diagnostics/tests/delegation/DelegationNotTotrait.fir.kt b/compiler/testData/diagnostics/tests/delegation/DelegationNotTotrait.fir.kt index 7d51fde3621..40baa8cc8f3 100644 --- a/compiler/testData/diagnostics/tests/delegation/DelegationNotTotrait.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/DelegationNotTotrait.fir.kt @@ -2,7 +2,7 @@ open class Foo() { } -class Barrr() : Foo by Foo() {} +class Barrr() : Foo by Foo() {} interface T {} @@ -12,4 +12,4 @@ open enum class EN() { A } -class Test2(e : EN) : EN by e {} +class Test2(e : EN) : EN by e {} diff --git a/compiler/testData/diagnostics/tests/delegation/DelegationToJavaIface.fir.kt b/compiler/testData/diagnostics/tests/delegation/DelegationToJavaIface.fir.kt index 1e62a0c87bb..6175e519d3b 100644 --- a/compiler/testData/diagnostics/tests/delegation/DelegationToJavaIface.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/DelegationToJavaIface.fir.kt @@ -1,4 +1,4 @@ // JAVAC_EXPECTED_FILE class TestIface(r : Runnable) : Runnable by r {} -class TestObject(o : Object) : Object by o {} +class TestObject(o : Object) : Object by o {} diff --git a/compiler/testData/diagnostics/tests/delegation/kt40510.fir.kt b/compiler/testData/diagnostics/tests/delegation/kt40510.fir.kt index 18d3d1257db..657ddc34f40 100644 --- a/compiler/testData/diagnostics/tests/delegation/kt40510.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/kt40510.fir.kt @@ -11,4 +11,4 @@ public abstract class A { // FILE: main.kt import foo.A -class DelegatedA(val a: A) : A by a +class DelegatedA(val a: A) : A by a 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 6901919797e..b7fa474d039 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 @@ -138,6 +138,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.DELEGATION_NOT_TO_INTERFACE) { firDiagnostic -> + DelegationNotToInterfaceImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.NESTED_CLASS_NOT_ALLOWED) { firDiagnostic -> NestedClassNotAllowedImpl( firDiagnostic.a, 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 049c4e0ca8d..9898c45f52e 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 @@ -121,6 +121,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = DelegationInInterface::class } + abstract class DelegationNotToInterface : KtFirDiagnostic() { + override val diagnosticClass get() = DelegationNotToInterface::class + } + abstract class NestedClassNotAllowed : KtFirDiagnostic() { override val diagnosticClass get() = NestedClassNotAllowed::class abstract val declaration: String 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 eb62f8cfd18..ac543717dd6 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 @@ -161,6 +161,13 @@ internal class DelegationInInterfaceImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class DelegationNotToInterfaceImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.DelegationNotToInterface(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class NestedClassNotAllowedImpl( override val declaration: String, firDiagnostic: FirPsiDiagnostic,