From fa33c5dc7983e919f8dc25a702fe182d85d8cc55 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 15 Dec 2023 17:08:53 +0200 Subject: [PATCH] [FIR] Implement deprecation warning for synthetic without java base getter Also implement new resolution behavior under language feature ^KT-63076 Fixed ^KT-64358 --- .../diagnostics/KtFirDataClassConverters.kt | 8 ++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 6 ++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 7 ++ ...CompilerTestFE10TestdataTestGenerated.java | 18 ++++ ...sticCompilerFE10TestDataTestGenerated.java | 18 ++++ ...eeOldFrontendDiagnosticsTestGenerated.java | 18 ++++ ...siOldFrontendDiagnosticsTestGenerated.java | 18 ++++ .../diagnostics/FirJvmDiagnosticsList.kt | 5 + .../analysis/diagnostics/jvm/FirJvmErrors.kt | 2 + .../jvm/FirJvmErrorsDefaultMessages.kt | 11 +++ .../jvm/checkers/JvmExpressionCheckers.kt | 5 + ...ntheticPropertyWithoutJavaOriginChecker.kt | 39 ++++++++ .../kotlin/fir/resolve/calls/Synthetics.kt | 97 +++++++++++++++---- ...opertyFromKotlinGetter_kotlinOrigin.fir.kt | 24 ++--- .../syntheticPropertyFromKotlin_k1Like.kt | 28 ++++++ ...syntheticPropertyFromKotlin_warning.fir.kt | 27 ++++++ .../syntheticPropertyFromKotlin_warning.kt | 27 ++++++ .../syntheticPropertyThroughJava.fir.kt | 19 ++++ .../syntheticPropertyThroughJava.kt | 19 ++++ .../javaProperties/Bases.fir.kt | 2 +- .../javaProperties/JavaOverridesKotlin.fir.kt | 2 +- .../test/runners/DiagnosticTestGenerated.java | 18 ++++ .../kotlin/config/LanguageVersionSettings.kt | 1 + 23 files changed, 384 insertions(+), 35 deletions(-) create mode 100644 compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirSyntheticPropertyWithoutJavaOriginChecker.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_k1Like.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.fir.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.fir.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.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 9c56e7738ab..843af89a56d 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 @@ -5518,6 +5518,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJvmErrors.SYNTHETIC_PROPERTY_WITHOUT_JAVA_ORIGIN) { firDiagnostic -> + SyntheticPropertyWithoutJavaOriginImpl( + firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol(firDiagnostic.a), + firDiagnostic.b, + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirJsErrors.IMPLEMENTING_FUNCTION_INTERFACE) { firDiagnostic -> ImplementingFunctionInterfaceImpl( firDiagnostic as KtPsiDiagnostic, 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 e9abb5979ad..64ae2eb3539 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 @@ -3839,6 +3839,12 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = NoReflectionInClassPath::class } + interface SyntheticPropertyWithoutJavaOrigin : KtFirDiagnostic { + override val diagnosticClass get() = SyntheticPropertyWithoutJavaOrigin::class + val originalSymbol: KtFunctionLikeSymbol + val functionName: Name + } + interface ImplementingFunctionInterface : KtFirDiagnostic { override val diagnosticClass get() = ImplementingFunctionInterface::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 1a5b9abbf0a..3105cdc809e 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 @@ -4635,6 +4635,13 @@ internal class NoReflectionInClassPathImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.NoReflectionInClassPath +internal class SyntheticPropertyWithoutJavaOriginImpl( + override val originalSymbol: KtFunctionLikeSymbol, + override val functionName: Name, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.SyntheticPropertyWithoutJavaOrigin + internal class ImplementingFunctionInterfaceImpl( firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index 9ebe41ffdd8..6d38e30e792 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -22443,6 +22443,24 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlinGetter_mixedOrigin_2.kt"); } + @Test + @TestMetadata("syntheticPropertyFromKotlin_k1Like.kt") + public void testSyntheticPropertyFromKotlin_k1Like() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_k1Like.kt"); + } + + @Test + @TestMetadata("syntheticPropertyFromKotlin_warning.kt") + public void testSyntheticPropertyFromKotlin_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.kt"); + } + + @Test + @TestMetadata("syntheticPropertyThroughJava.kt") + public void testSyntheticPropertyThroughJava() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.kt"); + } + @Test @TestMetadata("unitVsVoid.kt") public void testUnitVsVoid() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 14b7666cb48..56b891de0a2 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -22443,6 +22443,24 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlinGetter_mixedOrigin_2.kt"); } + @Test + @TestMetadata("syntheticPropertyFromKotlin_k1Like.kt") + public void testSyntheticPropertyFromKotlin_k1Like() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_k1Like.kt"); + } + + @Test + @TestMetadata("syntheticPropertyFromKotlin_warning.kt") + public void testSyntheticPropertyFromKotlin_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.kt"); + } + + @Test + @TestMetadata("syntheticPropertyThroughJava.kt") + public void testSyntheticPropertyThroughJava() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.kt"); + } + @Test @TestMetadata("unitVsVoid.kt") public void testUnitVsVoid() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 9f1c84d8cbc..51da3a61f4c 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -22437,6 +22437,24 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlinGetter_mixedOrigin_2.kt"); } + @Test + @TestMetadata("syntheticPropertyFromKotlin_k1Like.kt") + public void testSyntheticPropertyFromKotlin_k1Like() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_k1Like.kt"); + } + + @Test + @TestMetadata("syntheticPropertyFromKotlin_warning.kt") + public void testSyntheticPropertyFromKotlin_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.kt"); + } + + @Test + @TestMetadata("syntheticPropertyThroughJava.kt") + public void testSyntheticPropertyThroughJava() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.kt"); + } + @Test @TestMetadata("unitVsVoid.kt") public void testUnitVsVoid() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index 8594281e0d4..ac56d96cfee 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -22443,6 +22443,24 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlinGetter_mixedOrigin_2.kt"); } + @Test + @TestMetadata("syntheticPropertyFromKotlin_k1Like.kt") + public void testSyntheticPropertyFromKotlin_k1Like() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_k1Like.kt"); + } + + @Test + @TestMetadata("syntheticPropertyFromKotlin_warning.kt") + public void testSyntheticPropertyFromKotlin_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.kt"); + } + + @Test + @TestMetadata("syntheticPropertyThroughJava.kt") + public void testSyntheticPropertyThroughJava() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.kt"); + } + @Test @TestMetadata("unitVsVoid.kt") public void testUnitVsVoid() 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 cde95e25f7d..08798ec1a7c 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 @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticL import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -192,5 +193,9 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { ) val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error() val NO_REFLECTION_IN_CLASS_PATH by warning() + val SYNTHETIC_PROPERTY_WITHOUT_JAVA_ORIGIN by warning(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) { + parameter("originalSymbol") + parameter("functionName") + } } } 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 c5e775770cc..ac1fc825625 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 @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory import org.jetbrains.kotlin.fir.analysis.diagnostics.* import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -127,6 +128,7 @@ object FirJvmErrors { val SPREAD_ON_SIGNATURE_POLYMORPHIC_CALL: KtDiagnosticFactoryForDeprecation0 by deprecationError0(ProhibitSpreadOnSignaturePolymorphicCall, SourceElementPositioningStrategies.SPREAD_OPERATOR) val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE: KtDiagnosticFactory0 by error0() val NO_REFLECTION_IN_CLASS_PATH: KtDiagnosticFactory0 by warning0() + val SYNTHETIC_PROPERTY_WITHOUT_JAVA_ORIGIN: KtDiagnosticFactory2 by warning2(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) init { RootDiagnosticRendererFactory.registerFactory(FirJvmErrorsDefaultMessages) diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt index 10b5b441b78..44476358229 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderers.NOT_RENDERED import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderers.TO_STRING import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory +import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.NAME import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE @@ -79,6 +80,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_ON_ABSTRACT import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_ON_INLINE import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_ON_SUSPEND +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNTHETIC_PROPERTY_WITHOUT_JAVA_ORIGIN import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.UPPER_BOUND_CANNOT_BE_ARRAY import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION_BASED_ON_JAVA_ANNOTATIONS @@ -296,5 +298,14 @@ object FirJvmErrorsDefaultMessages : BaseDiagnosticRendererFactory() { "Call uses reflection API which is not found in compilation classpath. " + "Make sure you have kotlin-reflect.jar in the classpath." ) + + map.put( + SYNTHETIC_PROPERTY_WITHOUT_JAVA_ORIGIN, + "This synthetic property is based on the getter function ''{0}'' from Kotlin. " + + "In the future, synthetic properties will be available only if the base getter function came from Java. " + + "Consider replacing this property access with a ''{1}()'' function call.", + SYMBOL, + NAME + ) } } 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 facde70f6e4..a515ce800e6 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 @@ -22,6 +22,11 @@ object JvmExpressionCheckers : ExpressionCheckers() { FirQualifiedAccessJavaNullabilityWarningChecker, ) + override val propertyAccessExpressionCheckers: Set + get() = setOf( + FirSyntheticPropertyWithoutJavaOriginChecker, + ) + override val callableReferenceAccessCheckers: Set get() = setOf( FirUnsupportedSyntheticCallableReferenceChecker, diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirSyntheticPropertyWithoutJavaOriginChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirSyntheticPropertyWithoutJavaOriginChecker.kt new file mode 100644 index 00000000000..ba1085f1d67 --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirSyntheticPropertyWithoutJavaOriginChecker.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2023 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.FirPropertyAccessExpressionChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors +import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression +import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment +import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.resolve.calls.FirSimpleSyntheticPropertySymbol +import org.jetbrains.kotlin.fir.resolve.calls.noJavaOrigin + +object FirSyntheticPropertyWithoutJavaOriginChecker : FirPropertyAccessExpressionChecker() { + override fun check(expression: FirPropertyAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.ForbidSyntheticPropertiesWithoutBaseJavaGetter)) return + val syntheticProperty = expression.toResolvedCallableSymbol() as? FirSimpleSyntheticPropertySymbol ?: return + val containingAssignment = context.callsOrAssignments.getOrNull(context.callsOrAssignments.size - 2) as? FirVariableAssignment + val isAssignment = containingAssignment?.lValue === expression + val originalFunction = when (isAssignment) { + false -> syntheticProperty.getterSymbol?.delegateFunctionSymbol + true -> syntheticProperty.setterSymbol?.delegateFunctionSymbol + } ?: return + if (syntheticProperty.noJavaOrigin) { + reporter.reportOn( + expression.source, + FirJvmErrors.SYNTHETIC_PROPERTY_WITHOUT_JAVA_ORIGIN, + originalFunction, originalFunction.name, + context + ) + } + } +} diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt index 0c500ea0abe..1db4b71160b 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt @@ -5,21 +5,19 @@ package org.jetbrains.kotlin.fir.resolve.calls +import org.jetbrains.kotlin.config.LanguageFeature.ForbidSyntheticPropertiesWithoutBaseJavaGetter import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty import org.jetbrains.kotlin.fir.declarations.utils.isStatic +import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticPropertiesScope.SyntheticGetterCompatibility.* import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.Name @@ -126,7 +124,8 @@ class FirSyntheticPropertiesScope private constructor( if (getterReturnType?.isUnit == true && CompilerConeAttributes.EnhancedNullability !in getterReturnType.attributes) return // Should have Java among overridden _and_ don't have isHiddenEverywhereBesideSuperCalls among them - if (!getterSymbol.mayBeUsedAsGetterForSyntheticProperty()) return + val getterCompatibility = getterSymbol.computeGetterCompatibility() + if (getterCompatibility == Incompatible) return var matchingSetter: FirSimpleFunction? = null if (needCheckForSetter && getterReturnType != null) { @@ -144,12 +143,13 @@ class FirSyntheticPropertiesScope private constructor( }) } - val property = buildSyntheticProperty(propertyName, getter, matchingSetter) + val property = buildSyntheticProperty(propertyName, getter, matchingSetter, getterCompatibility) getter.originalForSubstitutionOverride?.let { property.originalForSubstitutionOverrideAttr = buildSyntheticProperty( propertyName, it, - matchingSetter?.originalForSubstitutionOverride ?: matchingSetter + matchingSetter?.originalForSubstitutionOverride ?: matchingSetter, + getterCompatibility ) } val syntheticSymbol = property.symbol @@ -161,7 +161,12 @@ class FirSyntheticPropertiesScope private constructor( processor(syntheticSymbol) } - private fun buildSyntheticProperty(propertyName: Name, getter: FirSimpleFunction, setter: FirSimpleFunction?): FirSyntheticProperty { + private fun buildSyntheticProperty( + propertyName: Name, + getter: FirSimpleFunction, + setter: FirSimpleFunction?, + getterCompatibility: SyntheticGetterCompatibility, + ): FirSyntheticProperty { val classLookupTag = getter.symbol.originalOrSelf().dispatchReceiverClassLookupTagOrNull() val packageName = classLookupTag?.classId?.packageFqName ?: getter.symbol.callableId.packageName val className = classLookupTag?.classId?.relativeClassName @@ -176,6 +181,10 @@ class FirSyntheticPropertiesScope private constructor( delegateGetter = getter delegateSetter = setter deprecationsProvider = getDeprecationsProviderFromAccessors(session, getter, setter) + }.apply { + if (getterCompatibility != HasJavaOrigin) { + noJavaOrigin = true + } } } @@ -235,23 +244,62 @@ class FirSyntheticPropertiesScope private constructor( || processOverrides(getterSymbol, setterSymbolToCompare = setterSymbol) } - private fun FirNamedFunctionSymbol.mayBeUsedAsGetterForSyntheticProperty(): Boolean { - var result = false - var isHiddenEverywhereBesideSuperCalls = false - baseScope.processOverriddenFunctionsAndSelf(this) { - val unwrapped = it.unwrapFakeOverrides().fir - if (unwrapped.origin == FirDeclarationOrigin.Enhancement) { - result = true - } + private enum class SyntheticGetterCompatibility { + Incompatible, + HasKotlinOrigin, + HasJavaOrigin + } - if (unwrapped.isHiddenEverywhereBesideSuperCalls == true) { + /** + * This method computes if getter method can be used as base for synthetic property based on overridden hierarchy + * There are three kinds of compatibility: + * - `Incompatible` (obvious) + * - `HasJavaOrigin` indicates that this getter is based on root java function (ok to create property) + * - `HasKotlinOrigin` shows that there is no base java getter overridden. Property will be created only with some LV (KT-64358) + */ + private fun FirNamedFunctionSymbol.computeGetterCompatibility(): SyntheticGetterCompatibility { + val kotlinBaseAllowed = !session.languageVersionSettings.supportsFeature(ForbidSyntheticPropertiesWithoutBaseJavaGetter) + + var isHiddenEverywhereBesideSuperCalls = false + var result = Incompatible + + val visited = mutableSetOf>() + fun checkJavaOrigin(symbol: FirNamedFunctionSymbol, scope: FirTypeScope) { + if (symbol.fir.isHiddenEverywhereBesideSuperCalls == true) { isHiddenEverywhereBesideSuperCalls = true } - ProcessorAction.NEXT + val overriddenWithScope = scope.getDirectOverriddenFunctionsWithBaseScope(symbol) + + if (symbol.origin == FirDeclarationOrigin.Enhancement) { + /** + * If there is no overridden then we found java root and want to stick with it + * Otherwise we are in the middle of the hierarchy, so leaf potentially can be from Kotlin + */ + val potentialResult = when { + overriddenWithScope.isEmpty() -> HasJavaOrigin + kotlinBaseAllowed -> HasKotlinOrigin + else -> Incompatible + } + result = maxOf(result, potentialResult) + } + + overriddenWithScope.forEach { + if (!visited.add(it)) return@forEach + checkJavaOrigin(it.member, it.baseScope) + } + } + + checkJavaOrigin(this, baseScope) + + return when { + isHiddenEverywhereBesideSuperCalls -> Incompatible + result != Incompatible -> result + !kotlinBaseAllowed -> Incompatible + // This branch is needed for compatibility reasons, see KT-64358 + isJavaTypeOnThePath(this.dispatchReceiverType) -> HasKotlinOrigin + else -> Incompatible } - result = result || isJavaTypeOnThePath(this.dispatchReceiverType) - return result && !isHiddenEverywhereBesideSuperCalls } /* @@ -310,3 +358,10 @@ class FirSyntheticPropertiesScope private constructor( } } } + +private object NoJavaOriginKey : FirDeclarationDataKey() +private var FirSyntheticProperty.noJavaOrigin: Boolean? by FirDeclarationDataRegistry.data(NoJavaOriginKey) + +val FirSimpleSyntheticPropertySymbol.noJavaOrigin: Boolean + get() = (fir as FirSyntheticProperty).noJavaOrigin == true + diff --git a/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlinGetter_kotlinOrigin.fir.kt b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlinGetter_kotlinOrigin.fir.kt index 60fdabaaee5..fd7f998a860 100644 --- a/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlinGetter_kotlinOrigin.fir.kt +++ b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlinGetter_kotlinOrigin.fir.kt @@ -36,22 +36,22 @@ fun test_1(a: A) { } fun test_2(b: B) { - b.x1 - b.x2 - b.x3 - b.x4 + b.x1 + b.x2 + b.x3 + b.x4 } fun test_3(c: C) { - c.x1 - c.x2 - c.x3 - c.x4 + c.x1 + c.x2 + c.x3 + c.x4 } fun test_4(d: D) { - d.x1 - d.x2 - d.x3 - d.x4 + d.x1 + d.x2 + d.x3 + d.x4 } diff --git a/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_k1Like.kt b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_k1Like.kt new file mode 100644 index 00000000000..341b2f59d65 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_k1Like.kt @@ -0,0 +1,28 @@ +// FIR_IDENTICAL +// LANGUAGE: +ForbidSyntheticPropertiesWithoutBaseJavaGetter + +// FILE: Base.kt +open class Base { + fun getFoo(): String = "" + fun setFoo(value: String) {} + + fun getBar(): String = "" +} + +// FILE: Derived.java +public class Derived extends Base { + public void setBar(String value) {} +} + +// FILE: main.kt +class Impl : Derived() + +fun test_1(x: Impl) { + x.foo + x.foo = "a" +} + +fun test_2(x: Impl) { + x.bar + x.bar = "a" +} diff --git a/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.fir.kt b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.fir.kt new file mode 100644 index 00000000000..dd1ca1bb448 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.fir.kt @@ -0,0 +1,27 @@ +// LANGUAGE: -ForbidSyntheticPropertiesWithoutBaseJavaGetter + +// FILE: Base.kt +open class Base { + fun getFoo(): String = "" + fun setFoo(value: String) {} + + fun getBar(): String = "" +} + +// FILE: Derived.java +public class Derived extends Base { + public void setBar(String value) {} +} + +// FILE: main.kt +class Impl : Derived() + +fun test_1(x: Impl) { + x.foo + x.foo = "a" +} + +fun test_2(x: Impl) { + x.bar + x.bar = "a" +} diff --git a/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.kt b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.kt new file mode 100644 index 00000000000..08a36b376d0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.kt @@ -0,0 +1,27 @@ +// LANGUAGE: -ForbidSyntheticPropertiesWithoutBaseJavaGetter + +// FILE: Base.kt +open class Base { + fun getFoo(): String = "" + fun setFoo(value: String) {} + + fun getBar(): String = "" +} + +// FILE: Derived.java +public class Derived extends Base { + public void setBar(String value) {} +} + +// FILE: main.kt +class Impl : Derived() + +fun test_1(x: Impl) { + x.foo + x.foo = "a" +} + +fun test_2(x: Impl) { + x.bar + x.bar = "a" +} diff --git a/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.fir.kt b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.fir.kt new file mode 100644 index 00000000000..3f643cf052a --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.fir.kt @@ -0,0 +1,19 @@ +// ISSUE: KT-59550 +// FILE: Base.kt +abstract class Base(private val foo: String) { + fun getFoo(): String = foo +} + +// FILE: Intermediate.java +public class Intermediate extends Base { + public Intermediate(String foo) { + super(foo); + } +} + +// FILE: main.kt +class Final(val i: Intermediate) : Intermediate(i.foo) + +fun test(x: Final) { + x.foo +} diff --git a/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.kt b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.kt new file mode 100644 index 00000000000..69879d16b8b --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.kt @@ -0,0 +1,19 @@ +// ISSUE: KT-59550 +// FILE: Base.kt +abstract class Base(private val foo: String) { + fun getFoo(): String = foo +} + +// FILE: Intermediate.java +public class Intermediate extends Base { + public Intermediate(String foo) { + super(foo); + } +} + +// FILE: main.kt +class Final(val i: Intermediate) : Intermediate(i.foo) + +fun test(x: Final) { + x.foo +} diff --git a/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/Bases.fir.kt b/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/Bases.fir.kt index d486307633e..0e4c3f29aa8 100644 --- a/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/Bases.fir.kt +++ b/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/Bases.fir.kt @@ -14,7 +14,7 @@ fun foo(k: KotlinClass2) { useInt(k.something2) useInt(k.getSomethingKotlin1()) useInt(k.getSomethingKotlin2()) - k.somethingKotlin1 + k.somethingKotlin1 k.somethingKotlin2 } diff --git a/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/JavaOverridesKotlin.fir.kt b/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/JavaOverridesKotlin.fir.kt index f0bf1c647f0..51773f5aee1 100644 --- a/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/JavaOverridesKotlin.fir.kt +++ b/compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/JavaOverridesKotlin.fir.kt @@ -8,7 +8,7 @@ open class KotlinClass { fun foo(javaClass: JavaClass) { useInt(javaClass.getSomething1()) - useInt(javaClass.something1) + useInt(javaClass.something1) javaClass.setSomething2(javaClass.getSomething2() + 1) javaClass.something2 = javaClass.something2 + 1 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 b7d7453aafb..7cb798480b2 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 @@ -22443,6 +22443,24 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlinGetter_mixedOrigin_2.kt"); } + @Test + @TestMetadata("syntheticPropertyFromKotlin_k1Like.kt") + public void testSyntheticPropertyFromKotlin_k1Like() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_k1Like.kt"); + } + + @Test + @TestMetadata("syntheticPropertyFromKotlin_warning.kt") + public void testSyntheticPropertyFromKotlin_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyFromKotlin_warning.kt"); + } + + @Test + @TestMetadata("syntheticPropertyThroughJava.kt") + public void testSyntheticPropertyThroughJava() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/properties/syntheticPropertyThroughJava.kt"); + } + @Test @TestMetadata("unitVsVoid.kt") public void testUnitVsVoid() 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 71970350262..0e13781b902 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -376,6 +376,7 @@ enum class LanguageFeature( IntrinsicConstEvaluation(sinceVersion = null, kind = UNSTABLE_FEATURE), // KT-49303 DisableCheckingChangedProgressionsResolve(sinceVersion = null, kind = OTHER), // KT-49276 ContextSensitiveEnumResolutionInWhen(sinceVersion = null, kind = UNSTABLE_FEATURE), // KT-52774 + ForbidSyntheticPropertiesWithoutBaseJavaGetter(sinceVersion = null, kind = OTHER), // KT-64358 ; init {