Deprecate Range<T> in generated code.

Provide hidden overloads of downTo and until with byte and short parameters for binary compatibility.
This commit is contained in:
Ilya Gorbunov
2015-11-06 23:05:02 +03:00
parent af9020a010
commit 0786027271
3 changed files with 486 additions and 33 deletions
@@ -48,9 +48,12 @@ fun comparables(): List<GenericFunction> {
templates add f("coerceIn(range: Range<T>)") {
sourceFile(SourceFile.Ranges)
only(Primitives)
only(Generic, Primitives)
only(numericPrimitives.filter { it.isIntegral() })
typeParam("T: Comparable<T>")
returns("SELF")
deprecate("Range<T> is deprecated. Use ClosedRange<T> instead.")
deprecate(Generic) { forBinaryCompatibility }
doc {
"""
Ensures that this value lies in the specified [range].
@@ -68,9 +71,30 @@ fun comparables(): List<GenericFunction> {
templates add f("coerceIn(range: Range<T>)") {
sourceFile(SourceFile.Ranges)
only(Primitives, Generic)
only(Primitives)
only(numericPrimitives.filterNot { it.isIntegral() })
returns("SELF")
deprecate { forBinaryCompatibility } // force use generic overload instead
doc {
"""
Ensures that this value lies in the specified [range].
@return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end.
"""
}
body {
"""
if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: ${'$'}range.")
return if (this < range.start) range.start else if (this > range.end) range.end else this
"""
}
}
templates add f("coerceIn(range: ClosedRange<T>)") {
sourceFile(SourceFile.Ranges)
only(Primitives, Generic)
only(PrimitiveType.Int, PrimitiveType.Long)
returns("SELF")
typeParam("T: Comparable<T>")
doc {
"""
@@ -82,7 +106,7 @@ fun comparables(): List<GenericFunction> {
body {
"""
if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: ${'$'}range.")
return if (this < range.start) range.start else if (this > range.end) range.end else this
return if (this < range.start) range.start else if (this > range.endInclusive) range.endInclusive else this
"""
}
}
@@ -11,6 +11,7 @@ fun ranges(): List<GenericFunction> {
fun rangeElementType(fromType: PrimitiveType, toType: PrimitiveType)
= maxByCapacity(fromType, toType).let { if (it == PrimitiveType.Char) it else maxByCapacity(it, PrimitiveType.Int) }
fun <T> Collection<T>.permutations(): List<Pair<T, T>> = flatMap { a -> map { b -> a to b } }
templates add f("reversed()") {
only(RangesOfPrimitives, ProgressionsOfPrimitives)
@@ -18,6 +19,7 @@ fun ranges(): List<GenericFunction> {
doc(ProgressionsOfPrimitives) { "Returns a progression that goes over the same range in the opposite direction with the same step." }
doc(RangesOfPrimitives) { "Returns a progression that goes over this range in reverse direction." }
returns("TProgression")
deprecate(RangesOfPrimitives) { forBinaryCompatibility }
body(RangesOfPrimitives) {
"return TProgression.fromClosedRange(last, first, -ONE)"
}
@@ -33,6 +35,7 @@ fun ranges(): List<GenericFunction> {
doc(RangesOfPrimitives) { "Returns a progression that goes over this range in reverse direction." }
returns("TProgression")
deprecate(Deprecation("This range implementation has unclear semantics and will be removed soon.", level = DeprecationLevel.WARNING))
deprecate(RangesOfPrimitives) { forBinaryCompatibility }
annotations("""@Suppress("DEPRECATION_ERROR")""")
body(RangesOfPrimitives) {
"return TProgression.fromClosedRange(last, first, -ONE)"
@@ -50,6 +53,7 @@ fun ranges(): List<GenericFunction> {
doc(ProgressionsOfPrimitives) { "Returns a progression that goes over the same range with the given step." }
doc(RangesOfPrimitives) { "Returns a progression that goes over this range with given step." }
returns("TProgression")
deprecate(RangesOfPrimitives) { forBinaryCompatibility }
body(RangesOfPrimitives) {
"""
checkStepIsPositive(step > 0, step)
@@ -73,6 +77,7 @@ fun ranges(): List<GenericFunction> {
doc(RangesOfPrimitives) { "Returns a progression that goes over this range with given step." }
returns("TProgression")
deprecate(Deprecation("This range implementation has unclear semantics and will be removed soon.", level = DeprecationLevel.WARNING))
deprecate(RangesOfPrimitives) { forBinaryCompatibility }
annotations("""@Suppress("DEPRECATION_ERROR")""")
body(RangesOfPrimitives) {
"""
@@ -130,12 +135,34 @@ fun ranges(): List<GenericFunction> {
body { "return $progressionType.fromClosedRange($fromExpr, $toExpr, $incrementExpr)" }
}
fun downToDeprecated(fromType: PrimitiveType, toType: PrimitiveType) = f("`-downTo`(to: $toType)") {
deprecate { forBinaryCompatibility }
sourceFile(SourceFile.Ranges)
only(Primitives)
only(fromType)
val elementType = maxByCapacity(fromType, toType)
val progressionType = elementType.name + "Progression"
returns(progressionType)
annotations("""@[kotlin.jvm.JvmName("downTo") kotlin.jvm.JvmVersion]""")
val fromExpr = if (elementType == fromType) "this" else "this.to$elementType()"
val toExpr = if (elementType == toType) "to" else "to.to$elementType()"
val incrementExpr = when (elementType) {
PrimitiveType.Long -> "-1L"
PrimitiveType.Float -> "-1.0F"
PrimitiveType.Double -> "-1.0"
else -> "-1"
}
body { "return $progressionType.fromClosedRange($fromExpr, $toExpr, $incrementExpr)" }
}
val numericPrimitives = PrimitiveType.numericPrimitives
val numericPermutations = numericPrimitives.flatMap { fromType -> numericPrimitives.map { toType -> fromType to toType }}
val numericPermutations = numericPrimitives.permutations()
val primitivePermutations = numericPermutations + (PrimitiveType.Char to PrimitiveType.Char)
val integralPermutations = primitivePermutations.filter { it.first.isIntegral() && it.second.isIntegral() }
templates addAll integralPermutations.map { downTo(it.first, it.second) }
templates addAll listOf(PrimitiveType.Byte, PrimitiveType.Short).permutations().map { downToDeprecated(it.first, it.second) }
fun until(fromType: PrimitiveType, toType: PrimitiveType) = f("until(to: $toType)") {
infix(true)
@@ -180,18 +207,41 @@ fun ranges(): List<GenericFunction> {
}
}
templates addAll integralPermutations.map { until(it.first, it.second) }
fun untilDeprecated(fromType: PrimitiveType, toType: PrimitiveType) = f("`-until`(to: $toType)") {
deprecate { forBinaryCompatibility }
fun contains(rangeType: PrimitiveType, itemType: PrimitiveType) = f("contains(item: $itemType)") {
sourceFile(SourceFile.Ranges)
only(Primitives)
only(fromType)
val elementType = maxByCapacity(fromType, toType)
val progressionType = elementType.name + "Range"
returns(progressionType)
annotations("""@[kotlin.jvm.JvmName("until") kotlin.jvm.JvmVersion]""")
val fromExpr = if (elementType == fromType) "this" else "this.to$elementType()"
if (elementType == toType) {
// hack to work around incorrect char overflow behavior in JVM and int overflow behavior in JS
val toExpr = "to"
body {
"""
val to_ = ($toExpr - 1).to$elementType()
if (to_ > to) throw IllegalArgumentException("The to argument value '${'$'}to' was too small.")
return $progressionType($fromExpr, to_)
"""
}
} else {
body { "return $progressionType($fromExpr, (to.to$elementType() - 1).to$elementType())" }
}
}
templates addAll integralPermutations.map { until(it.first, it.second) }
templates addAll listOf(PrimitiveType.Byte, PrimitiveType.Short).permutations().map { untilDeprecated(it.first, it.second) }
fun containsDeprecated(rangeType: PrimitiveType, itemType: PrimitiveType) = f("contains(item: $itemType)") {
operator(true)
// if (!rangeType.isIntegral()) {
// deprecate(Deprecation("This range implementation has unclear semantics and will be removed soon.", level = DeprecationLevel.WARNING))
// annotations("""@Suppress("DEPRECATION_ERROR")""")
// }
// else if (rangeType !in rangePrimitives) {
// deprecate(Deprecation("This range will be removed soon.", level = DeprecationLevel.WARNING))
// }
deprecate("Range<T> is deprecated. Use ClosedRange<T> instead.")
check(rangeType.isNumeric() == itemType.isNumeric()) { "Required rangeType and itemType both to be numeric or both not, got: $rangeType, $itemType" }
only(Ranges)
@@ -202,8 +252,23 @@ fun ranges(): List<GenericFunction> {
body { "return start <= item && item <= end" }
}
fun contains(rangeType: PrimitiveType, itemType: PrimitiveType) = f("contains(item: $itemType)") {
operator(true)
check(rangeType.isNumeric() == itemType.isNumeric()) { "Required rangeType and itemType both to be numeric or both not, got: $rangeType, $itemType" }
only(Ranges)
onlyPrimitives(Ranges, rangeType)
customReceiver("ClosedRange<T>")
platformName("${rangeType.name.decapitalize()}RangeContains")
returns("Boolean")
doc { "Checks if the specified [item] belongs to this range." }
body { "return start <= item && item <= endInclusive" }
}
templates addAll numericPermutations.filter { it.first != it.second }
.flatMap { listOf(containsDeprecated(it.first, it.second), contains(it.first, it.second)) }
templates addAll numericPermutations.filter { it.first != it.second }.map { contains(it.first, it.second) }
return templates
}