mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
EmptyRangeInspection: support floats and doubles
^KTIJ-22379 Fixed GitOrigin-RevId: 72ed379b506bcca2c163c85fe7a452b74aaf7bac
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cb85fcc628
commit
4e1335fb04
@@ -1310,6 +1310,7 @@ add.replacewith.argument.to.specify.replacement.pattern=Add 'replaceWith' argume
|
||||
deprecated.annotation.without.replacewith.argument='@Deprecated' annotation without a 'replaceWith' argument
|
||||
variable.name.0.matches.the.name.of.a.different.component=Variable name ''{0}'' matches the name of a different component
|
||||
this.range.is.empty.did.you.mean.to.use.0=This range is empty. Did you mean to use ''{0}''?
|
||||
this.range.is.empty=This range is empty.
|
||||
equals.hashcode.in.object.declaration='equals()'/'hashCode()' in object declaration
|
||||
hash.code.text=Generate 'hashCode()'
|
||||
equals.text=Generate 'equals()'
|
||||
|
||||
+53
-59
@@ -7,13 +7,15 @@ import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.inspections.collections.isIterable
|
||||
import org.jetbrains.kotlin.idea.intentions.getArguments
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getType
|
||||
|
||||
/**
|
||||
* Tests:
|
||||
@@ -21,46 +23,61 @@ import org.jetbrains.kotlin.resolve.constants.*
|
||||
* [org.jetbrains.kotlin.idea.inspections.LocalInspectionTestGenerated.EmptyRange]
|
||||
*/
|
||||
class EmptyRangeInspection : AbstractRangeInspection() {
|
||||
override fun visitRangeTo(expression: KtExpression, context: BindingContext, holder: ProblemsHolder) {
|
||||
expression.startAndEndValueSignedOrNull(context)?.let { (startValue, endValue) ->
|
||||
if (startValue > endValue) holder.registerProblem(expression, downTo = true)
|
||||
}
|
||||
expression.startAndEndValueUnSignedOrNull(context)?.let { (startValue, endValue) ->
|
||||
if (startValue > endValue) holder.registerProblem(expression, downTo = true)
|
||||
override fun visitRangeTo(expression: KtExpression, context: BindingContext, holder: ProblemsHolder) =
|
||||
visitRangeToImpl<Nothing>(expression, context, holder)
|
||||
|
||||
override fun visitUntilOrRangeUntil(expression: KtExpression, context: BindingContext, holder: ProblemsHolder) =
|
||||
visitUntilOrRangeUntilImpl<Nothing>(expression, context, holder)
|
||||
|
||||
override fun visitDownTo(expression: KtExpression, context: BindingContext, holder: ProblemsHolder) =
|
||||
visitDownToImpl<Nothing>(expression, context, holder)
|
||||
|
||||
private fun <T> visitRangeToImpl(
|
||||
expression: KtExpression,
|
||||
context: BindingContext,
|
||||
holder: ProblemsHolder
|
||||
) where T : Comparable<T> {
|
||||
expression.getComparableArguments<T>(context)?.let { (startValue, endValue) ->
|
||||
if (startValue > endValue) holder.registerProblem(expression, context, downTo = true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitUntilOrRangeUntil(expression: KtExpression, context: BindingContext, holder: ProblemsHolder) {
|
||||
expression.startAndEndValueSignedOrNull(context)?.let { (startValue, endValue) ->
|
||||
private fun <T> visitUntilOrRangeUntilImpl(
|
||||
expression: KtExpression,
|
||||
context: BindingContext,
|
||||
holder: ProblemsHolder
|
||||
) where T : Comparable<T> {
|
||||
expression.getComparableArguments<T>(context)?.let { (startValue, endValue) ->
|
||||
when {
|
||||
startValue > endValue -> holder.registerProblem(expression, downTo = true)
|
||||
startValue == endValue -> holder.registerProblem(expression, downTo = false)
|
||||
}
|
||||
}
|
||||
expression.startAndEndValueUnSignedOrNull(context)?.let { (startValue, endValue) ->
|
||||
when {
|
||||
startValue > endValue -> holder.registerProblem(expression, downTo = true)
|
||||
startValue == endValue -> holder.registerProblem(expression, downTo = false)
|
||||
startValue > endValue -> holder.registerProblem(expression, context, downTo = true)
|
||||
startValue == endValue -> holder.registerProblem(expression, context, downTo = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitDownTo(expression: KtExpression, context: BindingContext, holder: ProblemsHolder) {
|
||||
expression.startAndEndValueSignedOrNull(context)?.let { (startValue, endValue) ->
|
||||
if (startValue < endValue) holder.registerProblem(expression, downTo = false)
|
||||
}
|
||||
expression.startAndEndValueUnSignedOrNull(context)?.let { (startValue, endValue) ->
|
||||
if (startValue < endValue) holder.registerProblem(expression, downTo = false)
|
||||
private fun <T> visitDownToImpl(
|
||||
expression: KtExpression,
|
||||
context: BindingContext,
|
||||
holder: ProblemsHolder
|
||||
) where T : Number, T : Comparable<T> {
|
||||
expression.getComparableArguments<T>(context)?.let { (startValue, endValue) ->
|
||||
if (startValue < endValue) holder.registerProblem(expression, context, downTo = false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ProblemsHolder.registerProblem(expression: KtExpression, downTo: Boolean) {
|
||||
val (functionName, operator) = if (downTo) "downTo" to "downTo" else "rangeTo" to ".."
|
||||
private fun ProblemsHolder.registerProblem(expression: KtExpression, context: BindingContext, downTo: Boolean) {
|
||||
val (msg, fixes) =
|
||||
if (!downTo || expression.getType(context)?.isIterable(DefaultBuiltIns.Instance) == true) {
|
||||
val (functionName, operator) = if (downTo) "downTo" to "downTo" else "rangeTo" to ".."
|
||||
KotlinBundle.message("this.range.is.empty.did.you.mean.to.use.0", functionName) to arrayOf(ReplaceFix(operator))
|
||||
} else {
|
||||
KotlinBundle.message("this.range.is.empty") to emptyArray<LocalQuickFix>()
|
||||
}
|
||||
registerProblem(
|
||||
expression,
|
||||
KotlinBundle.message("this.range.is.empty.did.you.mean.to.use.0", functionName),
|
||||
msg,
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
ReplaceFix(operator)
|
||||
*fixes
|
||||
)
|
||||
}
|
||||
|
||||
@@ -78,39 +95,16 @@ class EmptyRangeInspection : AbstractRangeInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtExpression.startAndEndValueSignedOrNull(context: BindingContext): Pair<Long, Long>? {
|
||||
private fun <T> KtExpression.getComparableArguments(context: BindingContext): Pair<T, T>? where T : Comparable<T> {
|
||||
val (start, end) = getArguments() ?: return null
|
||||
if (start?.isSignedValueConstant(context) == false) return null
|
||||
val startValue = start?.longValueOrNull(context) ?: return null
|
||||
val endValue = end?.longValueOrNull(context) ?: return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun KtExpression.value() = constantValueOrNull(context)?.boxedValue()
|
||||
// Because it's possible to write such things `2L..0`
|
||||
?.let { if (it is Number && it !is Double && it !is Float) it.toLong() else it } as? T
|
||||
|
||||
val startValue = start?.value() ?: return null
|
||||
val endValue = end?.value() ?: return null
|
||||
if (startValue::class != endValue::class) return null
|
||||
return startValue to endValue
|
||||
}
|
||||
|
||||
private fun KtExpression.startAndEndValueUnSignedOrNull(context: BindingContext): Pair<ULong, ULong>? {
|
||||
val (start, end) = getArguments() ?: return null
|
||||
if (start?.isSignedValueConstant(context) == true) return null
|
||||
val startValue = start?.uLongValueOrNull(context) ?: return null
|
||||
val endValue = end?.uLongValueOrNull(context) ?: return null
|
||||
return startValue to endValue
|
||||
}
|
||||
|
||||
private fun KtExpression.isSignedValueConstant(context: BindingContext) = constantValueOrNull(context) is IntegerValueConstant<*>
|
||||
|
||||
private fun KtExpression.longValueOrNull(context: BindingContext): Long? {
|
||||
return when (val constantValue = constantValueOrNull(context)?.value) {
|
||||
is Number -> constantValue.toLong()
|
||||
is Char -> constantValue.code.toLong()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtExpression.uLongValueOrNull(context: BindingContext): ULong? {
|
||||
return when (val constantValue = constantValueOrNull(context)) {
|
||||
is UByteValue -> constantValue.value.toUByte().toULong()
|
||||
is UShortValue -> constantValue.value.toUShort().toULong()
|
||||
is UIntValue -> constantValue.value.toUInt().toULong()
|
||||
is ULongValue -> constantValue.value.toULong()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getType
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.typeUtil.isDouble
|
||||
import org.jetbrains.kotlin.types.typeUtil.isFloat
|
||||
@@ -73,7 +74,7 @@ sealed class AbstractReplaceRangeToWithRangeUntilInspection : AbstractRangeInspe
|
||||
private fun isApplicable(expression: KtExpression, context: BindingContext, useRangeUntil: Boolean): Boolean {
|
||||
val (left, right) = expression.getArguments() ?: return false
|
||||
// `until` isn't available for floating point numbers
|
||||
fun KtExpression.isRangeUntilOrUntilApplicable() = context.getType(this)
|
||||
fun KtExpression.isRangeUntilOrUntilApplicable() = getType(context)
|
||||
?.let { it.isPrimitiveNumberType() && (useRangeUntil || !it.isDouble() && !it.isFloat()) }
|
||||
return right?.deparenthesize()?.isMinusOne() == true &&
|
||||
left?.isRangeUntilOrUntilApplicable() == true && right.isRangeUntilOrUntilApplicable() == true
|
||||
|
||||
+10
@@ -4650,6 +4650,11 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/emptyRange/char.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("double.kt")
|
||||
public void testDouble() throws Exception {
|
||||
runTest("testData/inspectionsLocal/emptyRange/double.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("downTo.kt")
|
||||
public void testDownTo() throws Exception {
|
||||
runTest("testData/inspectionsLocal/emptyRange/downTo.kt");
|
||||
@@ -4670,6 +4675,11 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
runTest("testData/inspectionsLocal/emptyRange/rangeUntil2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("rangeUntilFloat.kt")
|
||||
public void testRangeUntilFloat() throws Exception {
|
||||
runTest("testData/inspectionsLocal/emptyRange/rangeUntilFloat.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("testData/inspectionsLocal/emptyRange/simple.kt");
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// PROBLEM: This range is empty.
|
||||
// FIX: none
|
||||
// WITH_STDLIB
|
||||
val range = 1.0<caret>..0.0
|
||||
@@ -0,0 +1,5 @@
|
||||
// PROBLEM: This range is empty. Did you mean to use 'rangeTo'?
|
||||
// WITH_STDLIB
|
||||
fun test() {
|
||||
<caret>0f..<0f
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// PROBLEM: This range is empty. Did you mean to use 'rangeTo'?
|
||||
// WITH_STDLIB
|
||||
fun test() {
|
||||
<caret>0f..0f
|
||||
}
|
||||
Reference in New Issue
Block a user