diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt index 48e507e95a0..5b6eeb471e3 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt @@ -10,6 +10,8 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.utils.IDEAPlatforms +import org.jetbrains.kotlin.utils.IDEAPluginsCompatibilityAPI @SuppressWarnings("WeakerAccess") abstract class CommonCompilerArguments : CommonToolArguments() { @@ -149,6 +151,14 @@ abstract class CommonCompilerArguments : CommonToolArguments() { ) var readDeserializedContracts: Boolean by FreezableVar(false) + @IDEAPluginsCompatibilityAPI( + IDEAPlatforms._212, // maybe 211 AS used it too + IDEAPlatforms._213, + message = "Please migrate to -opt-in", + plugins = "Android" + ) + var experimental: Array? = null + @Argument( value = "-Xuse-experimental", valueDescription = "", diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/callUtil/callUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/callUtil/callUtil.kt index 49820f252d2..7023d6f3538 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/callUtil/callUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/callUtil/callUtil.kt @@ -17,59 +17,58 @@ import org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny import org.jetbrains.kotlin.resolve.calls.util.createLookupLocation import org.jetbrains.kotlin.resolve.calls.util.getValueArgumentForExpression import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.IDEAPlatforms +import org.jetbrains.kotlin.utils.IDEAPluginsCompatibilityAPI -@Deprecated( - "Use org.jetbrains.kotlin.resolve.calls.util.getResolvedCall instead.", - ReplaceWith("getResolvedCall", "org.jetbrains.kotlin.resolve.calls.util.getResolvedCall"), - level = DeprecationLevel.ERROR +@IDEAPluginsCompatibilityAPI( + IDEAPlatforms._213, // I'm not sure about 212 and 211 AS -- didn't check them + message = "Use org.jetbrains.kotlin.resolve.calls.util.getResolvedCall instead.", + plugins = "Android in IDEA" ) fun Call?.getResolvedCall(context: BindingContext): ResolvedCall? = getResolvedCall(context) -@Deprecated( - "Use org.jetbrains.kotlin.resolve.calls.util.getResolvedCall instead.", - ReplaceWith("getResolvedCall", "org.jetbrains.kotlin.resolve.calls.util.getResolvedCall"), - level = DeprecationLevel.ERROR +@IDEAPluginsCompatibilityAPI( + IDEAPlatforms._213, // I'm not sure about 212 and 211 AS -- didn't check them + message = "Use org.jetbrains.kotlin.resolve.calls.util.getResolvedCall instead.", + plugins = "Android in IDEA" ) fun KtElement?.getResolvedCall(context: BindingContext): ResolvedCall? = getResolvedCall(context) -@Deprecated( - "Use org.jetbrains.kotlin.resolve.calls.util.getType instead.", - ReplaceWith("getType", "org.jetbrains.kotlin.resolve.calls.util.getType"), - level = DeprecationLevel.ERROR + +// TODO: find what IDEA's used it +@IDEAPluginsCompatibilityAPI( + message = "Use org.jetbrains.kotlin.resolve.calls.util.getType instead." ) fun KtExpression.getType(context: BindingContext): KotlinType? = getType(context) -@Deprecated( - "Use org.jetbrains.kotlin.resolve.calls.util.getCall instead.", - ReplaceWith("getType", "org.jetbrains.kotlin.resolve.calls.util.getCall"), - level = DeprecationLevel.ERROR +@IDEAPluginsCompatibilityAPI( + IDEAPlatforms._213, // I'm not sure about 212 and 211 AS -- didn't check them + message = "Use org.jetbrains.kotlin.resolve.calls.util.getCall instead.", + plugins = "Android in IDEA" ) fun KtElement.getCall(context: BindingContext): Call? = getCall(context) -@Deprecated( - "Use org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny instead.", - ReplaceWith("getCalleeExpressionIfAny", "org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny"), - level = DeprecationLevel.ERROR +// TODO: find what IDEA's used it +@IDEAPluginsCompatibilityAPI( + message = "Use org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny instead." ) fun KtElement?.getCalleeExpressionIfAny(): KtExpression? = getCalleeExpressionIfAny() -@Deprecated( - "Use org.jetbrains.kotlin.resolve.calls.util.createLookupLocation instead.", - ReplaceWith("createLookupLocation", "org.jetbrains.kotlin.resolve.calls.util.createLookupLocation"), - level = DeprecationLevel.ERROR +// TODO: find what IDEA's used it +@IDEAPluginsCompatibilityAPI( + message = "Use org.jetbrains.kotlin.resolve.calls.util.createLookupLocation instead." ) fun Call.createLookupLocation(): KotlinLookupLocation = createLookupLocation() -@Deprecated( - "Use org.jetbrains.kotlin.resolve.calls.util.createLookupLocation instead.", - ReplaceWith("createLookupLocation", "org.jetbrains.kotlin.resolve.calls.util.createLookupLocation"), - level = DeprecationLevel.ERROR +// TODO: find what IDEA's used it +@IDEAPluginsCompatibilityAPI( + message = "Use org.jetbrains.kotlin.resolve.calls.util.createLookupLocation instead." ) fun KtExpression.createLookupLocation(): KotlinLookupLocation? = createLookupLocation() -@Deprecated( - "Use org.jetbrains.kotlin.resolve.calls.util.getValueArgumentForExpression instead.", - ReplaceWith("getValueArgumentForExpression", "org.jetbrains.kotlin.resolve.calls.util.getValueArgumentForExpression"), - level = DeprecationLevel.ERROR +@IDEAPluginsCompatibilityAPI( + IDEAPlatforms._213, // I'm not sure about 212 and 211 AS -- didn't check them + message = "Use org.jetbrains.kotlin.resolve.calls.util.getValueArgumentForExpression instead.", + plugins = "Android in IDEA" ) fun Call.getValueArgumentForExpression(expression: KtExpression): ValueArgument? = getValueArgumentForExpression(expression) diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/IDEAPluginsCompatibilityAPI.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/IDEAPluginsCompatibilityAPI.kt new file mode 100644 index 00000000000..6da28c1e074 --- /dev/null +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/IDEAPluginsCompatibilityAPI.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2022 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.utils + +import kotlin.annotation.AnnotationTarget.* + +/** + * AS 211-based, IU211 or IC211 is the same _211 key. + * + * Usage in 213 platform means, that there was a **released** IDEA build or plugin on plugin marketplace compatible with + * `213.*` platform that has used this API. If you found a usage in the IDEA and removed it usage before the first official release of the + * corresponding platform it is considered as "not used" in this platform. In other words, if API was used in 221 EAP's + * or even 221 RC but not in the 221 release -- then API don't have the 221 usage. + */ +enum class IDEAPlatforms { + _211, + _212, + _213, + _221, + _222, + _223 +} + +/** + * This annotation is created to handle the following situation: + * Sometimes IDE plugins are using some API from Kotlin plugin. But it is known that Kotlin plugin includes some parts of + * the kotlin compiler. It means, that sometimes the IDE plugins are using API from kotlin compiler itself. + * Also, we are providing updates of the Kotlin plugin with the new kotlin front-end inside. + * But it also means, that because of such update some third-party plugins could be broken, because they have been using the API + * from kotlin compiler that was removed or changed in the new version of the compiler. + * + * To prevent that we have some teamcity configuration that are checking such compatibility with all the plugins published into plugin side. + * So, once we detected, that some API was broken, we should return corresponding API in the compiler and mark it. + * Exactly for these usages this annotation should be used. + * + * List of IDEA platforms is needed to be able to remove the API completely at some point, once we'll stop support of the corresponding + * IDEA platform. + * + * Instead of that we could use just Deprecation annotation with level Error, because it also preserves the binary compatibility and + * enforce third-party plugin migrate to the new API, but unfortunately it isn't always possible. With OptIn Annotation that is more + * flexible. And yes, please use Opt-in mechanic only as a last reserve. + * + * One of the legit use-cases for OptIn usage is the following: + * Sometimes third-party plugins sources located inside IDEA monorepo. And in kt-*- branches we don't want to change the sources outside + * the kotlin plugin, because it is incorrect. So instead of that we could add the corresponding OptIn in the corresponding module where + * old API is used and **remove** such usages in the IDEA master. + * + * P.S. usedIn is declared as first parameter to force its usage -- without it is easy enough forget about it and declare only message + * + * [usedIn] see description for [IDEAPlatforms] + * [message] -- you could add details what API should be used instead + * [plugins] -- if you want, you could mention what IDEA plugins used these APIs. It is not mandatory, but sometimes could be helpful. + */ +@Target(CLASS, ANNOTATION_CLASS, PROPERTY, FIELD, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, TYPEALIAS) +@RequiresOptIn("This API will be removed from the Kotlin compiler, you shouldn't use it", level = RequiresOptIn.Level.ERROR) +annotation class IDEAPluginsCompatibilityAPI(vararg val usedIn: IDEAPlatforms, val message: String, val plugins: String = "") 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 e2ebe53ca43..7c7542ca87e 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.utils.addToStdlib +import org.jetbrains.kotlin.utils.IDEAPlatforms +import org.jetbrains.kotlin.utils.IDEAPluginsCompatibilityAPI import java.lang.reflect.Modifier import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -99,10 +101,12 @@ fun String.indexOfOrNull(char: Char, startIndex: Int = 0, ignoreCase: Boolean = fun String.lastIndexOfOrNull(char: Char, startIndex: Int = lastIndex, ignoreCase: Boolean = false): Int? = lastIndexOf(char, startIndex, ignoreCase).takeIf { it >= 0 } -@Deprecated( +@IDEAPluginsCompatibilityAPI( + IDEAPlatforms._211, + IDEAPlatforms._212, + IDEAPlatforms._213, message = "Use firstNotNullOfOrNull from stdlib instead", - replaceWith = ReplaceWith("firstNotNullOfOrNull(transform)"), - level = DeprecationLevel.ERROR + plugins = "Android plugin in the IDEA, kotlin-ultimate.kotlin-ocswift" ) inline fun Iterable.firstNotNullResult(transform: (T) -> R?): R? { for (element in this) { @@ -112,6 +116,21 @@ inline fun Iterable.firstNotNullResult(transform: (T) -> R?): R? return null } +@IDEAPluginsCompatibilityAPI( + IDEAPlatforms._211, + IDEAPlatforms._212, + IDEAPlatforms._213, + message = "Use firstNotNullOfOrNull from stdlib instead", + plugins = "Android plugin in the IDEA" +) +inline fun Array.firstNotNullResult(transform: (T) -> R?): R? { + for (element in this) { + val result = transform(element) + if (result != null) return result + } + return null +} + inline fun Iterable.sumByLong(selector: (T) -> Long): Long { var sum: Long = 0 for (element in this) {