Fix KT-50777 The problem is that the last valid start-index of a substring is the length of the string, not the last valid index of the string. This is only a problem, when searching for the empty string. The fix is to start any search for a last occurence of a substring from index=length instead of index=lastIndex.
diff --git a/libraries/stdlib/src/kotlin/text/Strings.kt b/libraries/stdlib/src/kotlin/text/Strings.kt index a7e49c0..cf283ce 100644 --- a/libraries/stdlib/src/kotlin/text/Strings.kt +++ b/libraries/stdlib/src/kotlin/text/Strings.kt
@@ -996,7 +996,7 @@ return if (index < 0) null else index to string } - val indices = if (!last) startIndex.coerceAtLeast(0)..length else startIndex.coerceAtMost(lastIndex) downTo 0 + val indices = if (!last) startIndex.coerceAtLeast(0)..length else startIndex.coerceAtMost(length) downTo 0 if (this is String) { for (index in indices) { @@ -1042,7 +1042,7 @@ * the end toward the beginning of this string, and finds at each position the first element in [strings] * that matches this string at that position. */ -public fun CharSequence.findLastAnyOf(strings: Collection<String>, startIndex: Int = lastIndex, ignoreCase: Boolean = false): Pair<Int, String>? = +public fun CharSequence.findLastAnyOf(strings: Collection<String>, startIndex: Int = length, ignoreCase: Boolean = false): Pair<Int, String>? = findAnyOf(strings, startIndex, ignoreCase, last = true) /** @@ -1071,7 +1071,7 @@ * the end toward the beginning of this string, and finds at each position the first element in [strings] * that matches this string at that position. */ -public fun CharSequence.lastIndexOfAny(strings: Collection<String>, startIndex: Int = lastIndex, ignoreCase: Boolean = false): Int = +public fun CharSequence.lastIndexOfAny(strings: Collection<String>, startIndex: Int = length, ignoreCase: Boolean = false): Int = findAnyOf(strings, startIndex, ignoreCase, last = true)?.first ?: -1 @@ -1128,7 +1128,7 @@ * @param ignoreCase `true` to ignore character case when matching a string. By default `false`. * @return An index of the last occurrence of [string] or -1 if none is found. */ -public fun CharSequence.lastIndexOf(string: String, startIndex: Int = lastIndex, ignoreCase: Boolean = false): Int { +public fun CharSequence.lastIndexOf(string: String, startIndex: Int = length, ignoreCase: Boolean = false): Int { return if (ignoreCase || this !is String) indexOf(string, startIndex, 0, ignoreCase, last = true) else
diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index 7d4e491..6aa60c1 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt
@@ -750,6 +750,24 @@ assertEquals(-1, string.lastIndexOfAny(substrings, startIndex = 1, ignoreCase = true)) } + @Test fun lastIndexOfEmptyString() { + assertEquals(0, "".lastIndexOf("")) + assertEquals(1, " ".lastIndexOf("")) + assertEquals(11, "abracadabra".lastIndexOf("")) + } + + @Test fun lastIndexOfAnyWithEmpty() { + assertEquals(0, "".lastIndexOfAny(listOf(""))) + assertEquals(0, "".lastIndexOfAny(listOf("", "a"))) + assertEquals(0, "".lastIndexOfAny(listOf("a", ""))) + assertEquals(1, "a".lastIndexOfAny(listOf(""))) + assertEquals(1, "a".lastIndexOfAny(listOf("", "a"))) + assertEquals(1, "a".lastIndexOfAny(listOf("a", ""))) + assertEquals(11, "abracadabra".lastIndexOfAny(listOf(""))) + assertEquals(11, "abracadabra".lastIndexOfAny(listOf("", "a"))) + assertEquals(11, "abracadabra".lastIndexOfAny(listOf("a", ""))) + } + @Test fun findAnyOfStrings() = withOneCharSequenceArg("abracadabra") { string -> val substrings = listOf("rac", "ra") assertEquals(2 to "rac", string.findAnyOf(substrings))