[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
This commit is contained in:
Roman Efremov
2023-05-24 09:24:51 +00:00
committed by intellij-monorepo-bot
parent 9129250a49
commit b9a9d826bf
4 changed files with 20 additions and 49 deletions
@@ -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? {
@@ -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")
}
/**
@@ -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() un<caret>til MyObj()
0 u<caret>ntil 10
}
@@ -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()..<MyObj()
}
0..<10
}