mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[inspection] Extract logic of checking @WasExperimental from the opt-in inspection
It will be used in subsequent commit. ^KTIJ-25447 GitOrigin-RevId: 49a8c978d870e3d772cf374484ced898cd4587c6
This commit is contained in:
committed by
intellij-monorepo-bot
parent
38dd87e8ac
commit
9129250a49
+4
-30
@@ -57,6 +57,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.CanSealedSubClassBeObjectInspection.Companion.asKtClass
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.util.WasExperimentalOptInsNecessityCheckerFe10
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
|
||||
class UnnecessaryOptInAnnotationInspection : AbstractKotlinInspection() {
|
||||
@@ -300,36 +301,9 @@ private class MarkerCollector(private val resolutionFacade: ResolutionFacade) {
|
||||
foundMarkers += annotationFqName
|
||||
}
|
||||
|
||||
val wasExperimental = annotations.findAnnotation(OptInNames.WAS_EXPERIMENTAL_FQ_NAME) ?: continue
|
||||
val sinceKotlin = annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME) ?: continue
|
||||
|
||||
// If there are both `@SinceKotlin` and `@WasExperimental` annotations,
|
||||
// and Kotlin API version of the module is less than the version specified by `@SinceKotlin`,
|
||||
// then the `@OptIn` for `@WasExperimental` marker is necessary and should be added
|
||||
// to the set of found markers.
|
||||
//
|
||||
// For example, consider a function
|
||||
// ```
|
||||
// @SinceKotlin("1.6")
|
||||
// @WasExperimental(Marker::class)
|
||||
// fun foo() { ... }
|
||||
// ```
|
||||
// This combination of annotations means that `foo` was experimental before Kotlin 1.6
|
||||
// and required `@OptIn(Marker::class) or `@Marker` annotation. When the client code
|
||||
// is compiled as Kotlin 1.6 code, there are no problems, and the `@OptIn(Marker::class)`
|
||||
// annotation would not be necessary. At the same time, when the code is compiled with
|
||||
// `apiVersion = 1.5`, the non-experimental declaration of `foo` will be hidden
|
||||
// from the resolver, so `@OptIn` is necessary for the code to compile.
|
||||
val sinceKotlinApiVersion = sinceKotlin.allValueArguments[VERSION_ARGUMENT]
|
||||
?.safeAs<StringValue>()?.value?.let {
|
||||
ApiVersion.parse(it)
|
||||
}
|
||||
|
||||
if (sinceKotlinApiVersion != null && moduleApiVersion < sinceKotlinApiVersion) {
|
||||
wasExperimental.allValueArguments[OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS]?.safeAs<ArrayValue>()?.value
|
||||
?.mapNotNull { it.safeAs<KClassValue>()?.getArgumentType(module)?.fqName }
|
||||
?.forEach { foundMarkers.add(it) }
|
||||
}
|
||||
WasExperimentalOptInsNecessityCheckerFe10
|
||||
.getNecessaryOptInsFromWasExperimental(annotations, module, moduleApiVersion)
|
||||
.forEach { foundMarkers.add(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.util
|
||||
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.SINCE_KOTLIN_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||
import org.jetbrains.kotlin.resolve.constants.KClassValue
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
|
||||
/**
|
||||
* See [org.jetbrains.kotlin.idea.codeinsights.impl.base.inspection.WasExperimentalOptInsNecessityChecker]
|
||||
*/
|
||||
internal object WasExperimentalOptInsNecessityCheckerFe10 {
|
||||
private val VERSION_ARGUMENT = Name.identifier("version")
|
||||
|
||||
|
||||
// If there are both `@SinceKotlin` and `@WasExperimental` annotations,
|
||||
// and Kotlin API version of the module is less than the version specified by `@SinceKotlin`,
|
||||
// then the `@OptIn` for `@WasExperimental` marker is necessary.
|
||||
//
|
||||
// For example, consider a function
|
||||
// ```
|
||||
// @SinceKotlin("1.6")
|
||||
// @WasExperimental(Marker::class)
|
||||
// fun foo() { ... }
|
||||
// ```
|
||||
// This combination of annotations means that `foo` was experimental before Kotlin 1.6
|
||||
// and required `@OptIn(Marker::class) or `@Marker` annotation. When the client code
|
||||
// is compiled as Kotlin 1.6 code, there are no problems, and the `@OptIn(Marker::class)`
|
||||
// annotation would not be necessary. At the same time, when the code is compiled with
|
||||
// `apiVersion = 1.5`, the non-experimental declaration of `foo` will be hidden
|
||||
// from the resolver, so `@OptIn` is necessary for the code to compile.
|
||||
fun getNecessaryOptInsFromWasExperimental(
|
||||
annotations: Annotations,
|
||||
module: ModuleDescriptor,
|
||||
moduleApiVersion: ApiVersion,
|
||||
): Collection<FqName> {
|
||||
val wasExperimental = annotations.findAnnotation(OptInNames.WAS_EXPERIMENTAL_FQ_NAME)
|
||||
val sinceApiVersion = getSinceKotlinAnnotationApiVersionArgumentIfPresent(annotations)
|
||||
|
||||
if (wasExperimental == null || sinceApiVersion == null || moduleApiVersion >= sinceApiVersion) {
|
||||
return emptyList()
|
||||
}
|
||||
return getWasExperimentalAnnotationMarkerClassArgument(wasExperimental, module)
|
||||
}
|
||||
|
||||
private fun getSinceKotlinAnnotationApiVersionArgumentIfPresent(annotations: Annotations): ApiVersion? {
|
||||
val sinceKotlin = annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME) ?: return null
|
||||
return sinceKotlin.allValueArguments[VERSION_ARGUMENT]
|
||||
?.safeAs<StringValue>()
|
||||
?.value
|
||||
?.let { ApiVersion.parse(it) }
|
||||
}
|
||||
|
||||
private fun getWasExperimentalAnnotationMarkerClassArgument(
|
||||
annotation: AnnotationDescriptor,
|
||||
module: ModuleDescriptor,
|
||||
): Collection<FqName> {
|
||||
return annotation.allValueArguments[OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS]
|
||||
?.safeAs<ArrayValue>()
|
||||
?.value
|
||||
?.mapNotNull { (it as? KClassValue)?.getArgumentType(module)?.fqName }
|
||||
?: emptyList()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user