From b9a9d826bf6cc350b6fe271123515ab474e1eb93 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Fri, 12 May 2023 17:57:54 +0200 Subject: [PATCH] [inspection] Support opt-in from @WasExperimental in RangeUntil inspection The logic is similar to Enum.values() inspection. No new tests added because current tests were broken and are now fixed. Broken tests are tests in ReplaceRangeToWithRangeUntil and "noOptIn" in ReplaceUntilWithRangeUntil. ^KTIJ-25447 GitOrigin-RevId: c51e1095379b2fed4908327fdb62251dfdd45f09 --- .../ReplaceUntilWithRangeUntilInspection.kt | 10 ++++++++- .../UnnecessaryOptInAnnotationInspection.kt | 19 ++++++----------- .../optInIsNotRequired.kt | 19 ++--------------- .../optInIsNotRequired.kt.after | 21 +++---------------- 4 files changed, 20 insertions(+), 49 deletions(-) diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceUntilWithRangeUntilInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceUntilWithRangeUntilInspection.kt index 872ed12ced53..0177d2532787 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceUntilWithRangeUntilInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceUntilWithRangeUntilInspection.kt @@ -10,6 +10,7 @@ import org.jetbrains.annotations.ApiStatus import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings import org.jetbrains.kotlin.idea.base.resources.KotlinBundle +import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor import org.jetbrains.kotlin.idea.intentions.getArguments import org.jetbrains.kotlin.idea.intentions.receiverType import org.jetbrains.kotlin.idea.statistics.KotlinLanguageFeaturesFUSCollector @@ -17,6 +18,7 @@ import org.jetbrains.kotlin.idea.statistics.NewAndDeprecatedFeaturesInspectionDa import org.jetbrains.kotlin.idea.util.RangeKtExpressionType import org.jetbrains.kotlin.idea.util.RangeKtExpressionType.RANGE_UNTIL import org.jetbrains.kotlin.idea.util.RangeKtExpressionType.UNTIL +import org.jetbrains.kotlin.idea.util.WasExperimentalOptInsNecessityCheckerFe10 import org.jetbrains.kotlin.idea.util.projectStructure.module import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.FqName @@ -91,7 +93,13 @@ class ReplaceUntilWithRangeUntilInspection : AbstractRangeInspection( private fun KtElement.isOtpInRequiredForRangeUntil(annotationFqName: FqName, context: BindingContext): Boolean { val rangeUntilFunctionDescriptor = findRangeUntilFunctionDescriptor(context) ?: return false - return rangeUntilFunctionDescriptor.annotations.hasAnnotation(annotationFqName) + if (rangeUntilFunctionDescriptor.annotations.hasAnnotation(annotationFqName)) { + return true + } + val necessaryOptIns = WasExperimentalOptInsNecessityCheckerFe10.getNecessaryOptInsFromWasExperimental( + rangeUntilFunctionDescriptor.annotations, findModuleDescriptor(), languageVersionSettings.apiVersion + ) + return annotationFqName in necessaryOptIns } private fun KtElement.findRangeUntilFunctionDescriptor(context: BindingContext): CallableDescriptor? { diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/UnnecessaryOptInAnnotationInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/UnnecessaryOptInAnnotationInspection.kt index 875b23287c4b..58b5ad179d25 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/UnnecessaryOptInAnnotationInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/UnnecessaryOptInAnnotationInspection.kt @@ -11,32 +11,33 @@ import com.intellij.psi.util.parentOfType import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings import org.jetbrains.kotlin.idea.base.psi.KotlinPsiHeuristics +import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.base.util.names.FqNames import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection import org.jetbrains.kotlin.idea.core.OPT_IN_FQ_NAMES import org.jetbrains.kotlin.idea.core.getDirectlyOverriddenDeclarations +import org.jetbrains.kotlin.idea.inspections.CanSealedSubClassBeObjectInspection.Companion.asKtClass import org.jetbrains.kotlin.idea.refactoring.fqName.fqName import org.jetbrains.kotlin.idea.references.ReadWriteAccessChecker import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor +import org.jetbrains.kotlin.idea.util.WasExperimentalOptInsNecessityCheckerFe10 import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.BindingContext -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.resolve.descriptorUtil.* import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -54,12 +55,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs * any names. For these redundant markers, the inspection proposes a quick fix to remove the marker * or the entire unnecessary `@OptIn` annotation if it contains a single marker. */ -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() { /** @@ -314,8 +309,6 @@ private class MarkerCollector(private val resolutionFacade: ResolutionFacade) { */ private fun KtReferenceExpression.isSetterCall(): Boolean = readWriteAccessChecker.readWriteAccessWithFullExpression(this, true).first.isWrite - - private val VERSION_ARGUMENT = Name.identifier("version") } /** diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceUntilWithRangeUntil/optInIsNotRequired.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceUntilWithRangeUntil/optInIsNotRequired.kt index 5108c33710e4..3506e20ef129 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceUntilWithRangeUntil/optInIsNotRequired.kt +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceUntilWithRangeUntil/optInIsNotRequired.kt @@ -1,20 +1,5 @@ -// COMPILER_ARGUMENTS: -XXLanguage:+RangeUntilOperator // WITH_STDLIB -// LANGUAGE_VERSION: 1.9 -package kotlin - -class MyObj { - @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - @SinceKotlin("1.9") - @WasExperimental(ExperimentalStdlibApi::class) - @kotlin.internal.InlineOnly - operator inline fun rangeUntil(o: MyObj) = Unit -} - -// Since `until` is only defined for primitives, we create this fake function in kotlin package -// to activate the inspection and make it look at MyObj.rangeUntil, which is without opt-in. -infix fun MyObj.until(o: MyObj) = Unit - +// API_VERSION: 1.9 fun main() { - MyObj() until MyObj() + 0 until 10 } \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceUntilWithRangeUntil/optInIsNotRequired.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceUntilWithRangeUntil/optInIsNotRequired.kt.after index fa23164158a3..e9eb53ea577c 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceUntilWithRangeUntil/optInIsNotRequired.kt.after +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceUntilWithRangeUntil/optInIsNotRequired.kt.after @@ -1,20 +1,5 @@ -// COMPILER_ARGUMENTS: -XXLanguage:+RangeUntilOperator -// WITH_STDLIB // LANGUAGE_VERSION: 1.9 -package kotlin - -class MyObj { - @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - @SinceKotlin("1.9") - @WasExperimental(ExperimentalStdlibApi::class) - @kotlin.internal.InlineOnly - operator inline fun rangeUntil(o: MyObj) = Unit -} - -// Since `until` is only defined for primitives, we create this fake function in kotlin package -// to activate the inspection and make it look at MyObj.rangeUntil, which is without opt-in. -infix fun MyObj.until(o: MyObj) = Unit - +// WITH_STDLIB fun main() { - MyObj()..