From 9b63dde1c3164def76449a88bf0797d98aeabf51 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 3 Oct 2022 13:59:11 +0300 Subject: [PATCH] Add error-level OptIn on `safeAs`, `cast` and `assertedCast` functions in addToStdlib Usage of this function is unsafe because it does not have native compiler support. This means that compiler won't report UNCHECKED_CAST, CAST_NEVER_SUCCEED or similar diagnostics in case of error cast (which can happen immediately or after some refactoring of class hierarchy) --- build.gradle.kts | 51 +++++++++++++++++++ .../kotlin/common-configuration.gradle.kts | 4 ++ .../org/jetbrains/kotlin/utils/addToStdlib.kt | 17 +++++++ libraries/reflect/api/build.gradle | 1 + .../tools/kotlin-maven-serialization/pom.xml | 1 + 5 files changed, 74 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index d33dfc86cc5..59eaea7ed53 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -373,6 +373,57 @@ val projectsWithEnabledContextReceivers by extra { ) } +val projectsWithOptInToUnsafeCastFunctionsFromAddToStdLib by extra { + listOf( + ":analysis:analysis-api-fe10", + ":analysis:analysis-api-fir", + ":analysis:decompiled:light-classes-for-decompiled", + ":analysis:symbol-light-classes", + ":compiler", + ":compiler:backend", + ":compiler:backend.js", + ":compiler:backend.jvm", + ":compiler:backend.jvm.codegen", + ":compiler:backend.jvm.entrypoint", + ":compiler:backend.jvm.lower", + ":compiler:cli", + ":compiler:frontend:cfg", + ":compiler:frontend.common.jvm", + ":compiler:frontend.java", + ":compiler:frontend", + ":compiler:ir.backend.common", + ":compiler:ir.psi2ir", + ":compiler:ir.serialization.jvm", + ":compiler:ir.tree", + ":compiler:light-classes", + ":compiler:psi", + ":compiler:resolution.common.jvm", + ":compiler:resolution.common", + ":compiler:resolution", + ":core:descriptors.jvm", + ":core:descriptors", + ":core:deserialization", + ":core:reflection.jvm", + ":kotlin-reflect-api", + ":jps:jps-common", + ":jps:jps-common", + ":js:js.tests", + ":kotlin-build-common", + ":kotlin-gradle-plugin", + ":kotlin-native:backend.native", + ":kotlin-reflect-api", + ":kotlin-scripting-jvm-host-test", + ":native:frontend.native", + ":native:kotlin-klib-commonizer", + ":native:native.tests", + ":plugins:android-extensions-compiler", + ":plugins:jvm-abi-gen", + ":plugins:parcelize:parcelize-compiler:parcelize.k1", + ":plugins:parcelize:parcelize-compiler:parcelize.backend", + ":kotlinx-serialization-compiler-plugin.backend", + ) +} + val gradlePluginProjects = listOf( ":kotlin-gradle-plugin", ":kotlin-gradle-plugin-api", diff --git a/buildSrc/src/main/kotlin/common-configuration.gradle.kts b/buildSrc/src/main/kotlin/common-configuration.gradle.kts index be5ecf84de0..cc14f80f3cb 100644 --- a/buildSrc/src/main/kotlin/common-configuration.gradle.kts +++ b/buildSrc/src/main/kotlin/common-configuration.gradle.kts @@ -171,6 +171,7 @@ fun Project.configureKotlinCompilationOptions() { ) val projectsWithEnabledContextReceivers: List by rootProject.extra + val projectsWithOptInToUnsafeCastFunctionsFromAddToStdLib: List by rootProject.extra @Suppress("SuspiciousCollectionReassignment", "DEPRECATION") tasks.withType().configureEach { @@ -196,6 +197,9 @@ fun Project.configureKotlinCompilationOptions() { if (project.path in projectsWithEnabledContextReceivers) { freeCompilerArgs += "-Xcontext-receivers" } + if (project.path in projectsWithOptInToUnsafeCastFunctionsFromAddToStdLib) { + freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction" + } if (project.path == ":kotlin-util-klib") { // This is a temporary workaround for a configuration problem in kotlin-native. Namely, module `:kotlin-native-shared` diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt index 80e407d3a06..cfe93e8b992 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt @@ -91,9 +91,26 @@ fun sequenceOfLazyValues(vararg elements: () -> T): Sequence = elements.a fun Pair.swap(): Pair = Pair(second, first) +@RequiresOptIn( + message =""" + Usage of this function is unsafe because it does not have native compiler support + This means that compiler won't report UNCHECKED_CAST, CAST_NEVER_SUCCEED or similar + diagnostics in case of error cast (which can happen immediately or after some + refactoring of class hierarchy) + Consider using regular `as` and `as?` + """, + level = RequiresOptIn.Level.ERROR +) +annotation class UnsafeCastFunction + @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@UnsafeCastFunction inline fun Any?.safeAs(): @kotlin.internal.NoInfer T? = this as? T + +@UnsafeCastFunction inline fun Any?.cast(): T = this as T + +@UnsafeCastFunction inline fun Any?.assertedCast(message: () -> String): T = this as? T ?: throw AssertionError(message()) fun constant(calculator: () -> T): T { diff --git a/libraries/reflect/api/build.gradle b/libraries/reflect/api/build.gradle index b93d377d300..032368f4b6e 100644 --- a/libraries/reflect/api/build.gradle +++ b/libraries/reflect/api/build.gradle @@ -52,6 +52,7 @@ compileKotlin { "-Xno-optimized-callable-references", "-Xno-kotlin-nothing-value-exception", "-opt-in=kotlin.RequiresOptIn", + "-opt-in=org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction", "-Xno-new-java-annotation-targets", ] moduleName = "kotlin-reflection" diff --git a/libraries/tools/kotlin-maven-serialization/pom.xml b/libraries/tools/kotlin-maven-serialization/pom.xml index 8a75bd6b3ca..e9721a47367 100755 --- a/libraries/tools/kotlin-maven-serialization/pom.xml +++ b/libraries/tools/kotlin-maven-serialization/pom.xml @@ -86,6 +86,7 @@ -Xjvm-default=enable -opt-in=org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI -opt-in=org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi + -opt-in=org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction -Xcontext-receivers