diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index e78606b86df..6e22cf0c0f5 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -35858,6 +35858,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment.kt"); } + @Test + @TestMetadata("unsafeAssignmentExtra.kt") + public void testUnsafeAssignmentExtra() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.kt"); + } + + @Test + @TestMetadata("unsafeAssignment_noReport.kt") + public void testUnsafeAssignment_noReport() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.kt"); + } + @Test @TestMetadata("useInferenceInformationFromExtension.kt") public void testUseInferenceInformationFromExtension() throws Exception { 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 0e9a85f9bae..7f18fc5311c 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 @@ -35858,6 +35858,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment.kt"); } + @Test + @TestMetadata("unsafeAssignmentExtra.kt") + public void testUnsafeAssignmentExtra() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.kt"); + } + + @Test + @TestMetadata("unsafeAssignment_noReport.kt") + public void testUnsafeAssignment_noReport() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.kt"); + } + @Test @TestMetadata("useInferenceInformationFromExtension.kt") public void testUseInferenceInformationFromExtension() 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 bf2daafe5a7..4f5eec04f6c 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 @@ -35858,6 +35858,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment.kt"); } + @Test + @TestMetadata("unsafeAssignmentExtra.kt") + public void testUnsafeAssignmentExtra() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.kt"); + } + + @Test + @TestMetadata("unsafeAssignment_noReport.kt") + public void testUnsafeAssignment_noReport() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.kt"); + } + @Test @TestMetadata("useInferenceInformationFromExtension.kt") public void testUseInferenceInformationFromExtension() throws Exception { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/BuilderInferenceAssignmentChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/BuilderInferenceAssignmentChecker.kt index 511b4d4641c..cf7f5824fe4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/BuilderInferenceAssignmentChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/BuilderInferenceAssignmentChecker.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.descriptors.PropertyDescriptor import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.psi.KtBinaryExpression @@ -28,6 +29,7 @@ object BuilderInferenceAssignmentChecker : CallChecker { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { val resultingDescriptor = resolvedCall.resultingDescriptor if (resultingDescriptor !is PropertyDescriptor) return + if (context.languageVersionSettings.supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction)) return if (resolvedCall.candidateDescriptor.returnType !is StubTypeForBuilderInference) return if (reportOn !is KtNameReferenceExpression) return val binaryExpression = reportOn.getParentOfType(strict = true) ?: return diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.fir.kt new file mode 100644 index 00000000000..055ae3e448a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.fir.kt @@ -0,0 +1,60 @@ +// WITH_REFLECT +import kotlin.reflect.* + +interface Foo { + var a: T + val b: Array + + fun accept(arg: T) +} + +class FooImpl : Foo + +fun bar(p: KMutableProperty0) { + p.set(100) +} + +fun myBuilder(block: Foo.() -> Unit): Foo = FooImpl().apply(block) + +fun Foo.change(block: Foo.() -> Unit): Foo { + this.block() + return this +} + +fun main(arg: Any, condition: Boolean) { + val value = myBuilder { + b[0] = 123 + a = 45 + a++ + bar(::a) + if (a is Int) { + a = 67 + a-- + bar(::a) + } + when (condition) { + true -> a = 87 + false -> a = 65 + } + val x by a + + change { + a = 99 + } + } + + val value2 = myBuilder { + accept("") + a = 45 + when (condition) { + true -> a = 87 + false -> a = 65 + } + change { + a = 99 + } + if (a is Int) { + a = 67 + } + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.kt new file mode 100644 index 00000000000..1bfd7c09b8b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.kt @@ -0,0 +1,60 @@ +// WITH_REFLECT +import kotlin.reflect.* + +interface Foo { + var a: T + val b: Array + + fun accept(arg: T) +} + +class FooImpl : Foo + +fun bar(p: KMutableProperty0) { + p.set(100) +} + +fun myBuilder(block: Foo.() -> Unit): Foo = FooImpl().apply(block) + +fun Foo.change(block: Foo.() -> Unit): Foo { + this.block() + return this +} + +fun main(arg: Any, condition: Boolean) { + val value = myBuilder { + b[0] = 123 + a = 45 + a++ + bar(::a) + if (a is Int) { + a = 67 + a-- + bar(::a) + } + when (condition) { + true -> a = 87 + false -> a = 65 + } + val x by a + + change { + a = 99 + } + } + + val value2 = myBuilder { + accept("") + a = 45 + when (condition) { + true -> a = 87 + false -> a = 65 + } + change { + a = 99 + } + if (a is Int) { + a = 67 + } + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.txt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.txt new file mode 100644 index 00000000000..0a8a8c11a41 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.txt @@ -0,0 +1,26 @@ +package + +public fun bar(/*0*/ p: kotlin.reflect.KMutableProperty0): kotlin.Unit +public fun main(/*0*/ arg: kotlin.Any, /*1*/ condition: kotlin.Boolean): kotlin.Unit +public fun myBuilder(/*0*/ block: Foo.() -> kotlin.Unit): Foo +public fun Foo.change(/*0*/ block: Foo.() -> kotlin.Unit): Foo + +public interface Foo { + public abstract var a: T + public abstract val b: kotlin.Array + public abstract fun accept(/*0*/ arg: T): kotlin.Unit + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class FooImpl : Foo { + public constructor FooImpl() + public abstract override /*1*/ /*fake_override*/ var a: T + public abstract override /*1*/ /*fake_override*/ val b: kotlin.Array + public abstract override /*1*/ /*fake_override*/ fun accept(/*0*/ arg: T): kotlin.Unit + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.fir.kt new file mode 100644 index 00000000000..8d3e441103a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.fir.kt @@ -0,0 +1,21 @@ +// !LANGUAGE: +NoBuilderInferenceWithoutAnnotationRestriction + +class Foo { + fun doSmthng(arg: T) {} + var a: T? = null +} + +fun myBuilder(block: Foo.() -> Unit) : Foo = Foo().apply(block) + +fun main(arg: Any) { + val x = 57 + val value = myBuilder { + doSmthng("one ") + a = 57 + a = x + if (arg is String) { + a = arg + } + } + println(value.a?.count { it in 'l' .. 'q' }) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.kt new file mode 100644 index 00000000000..78d6520f9b5 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.kt @@ -0,0 +1,21 @@ +// !LANGUAGE: +NoBuilderInferenceWithoutAnnotationRestriction + +class Foo { + fun doSmthng(arg: T) {} + var a: T? = null +} + +fun myBuilder(block: Foo.() -> Unit) : Foo = Foo().apply(block) + +fun main(arg: Any) { + val x = 57 + val value = myBuilder { + doSmthng("one ") + a = 57 + a = x + if (arg is String) { + a = arg + } + } + println(value.a?.count { it in 'l' .. 'q' }) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.txt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.txt new file mode 100644 index 00000000000..f920da6ac4c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.txt @@ -0,0 +1,13 @@ +package + +public fun main(/*0*/ arg: kotlin.Any): kotlin.Unit +public fun myBuilder(/*0*/ block: Foo.() -> kotlin.Unit): Foo + +public final class Foo { + public constructor Foo() + public final var a: T? + public final fun doSmthng(/*0*/ arg: T): kotlin.Unit + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} 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 1335d7e92f4..eedf11bd3c6 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 @@ -35948,6 +35948,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment.kt"); } + @Test + @TestMetadata("unsafeAssignmentExtra.kt") + public void testUnsafeAssignmentExtra() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignmentExtra.kt"); + } + + @Test + @TestMetadata("unsafeAssignment_noReport.kt") + public void testUnsafeAssignment_noReport() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/builderInference/unsafeAssignment_noReport.kt"); + } + @Test @TestMetadata("useInferenceInformationFromExtension.kt") public void testUseInferenceInformationFromExtension() 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 005af7a7098..c9ad9d13ec5 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -295,6 +295,10 @@ enum class LanguageFeature( ApproximateIntegerLiteralTypesInReceiverPosition(sinceVersion = null), // Disabled for indefinite time. Disables restrictions of builder inference without annotation + // Note: In 1.7.0, builder inference without annotation was introduced. + // However, later we encountered various situations when it works incorrectly, and decided to forbid them. + // When this feature is disabled, various errors are reported which are related to these incorrect situations. + // When this feature is enabled, no such errors are reported. NoBuilderInferenceWithoutAnnotationRestriction(sinceVersion = null, kind = OTHER, defaultState = State.DISABLED), // Experimental features