Skip to content

Commit 2b0cca1

Browse files
author
Wojciech Liberda
committed
Throw IllegalArgumentException with meaningful message for missing format arguments
1 parent 3ea093f commit 2b0cca1

File tree

2 files changed

+31
-11
lines changed

2 files changed

+31
-11
lines changed

components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResourcesUtils.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ internal fun String.replaceWithArgs(args: List<String>): String {
1919
)
2020
}
2121
}
22-
args[index]
22+
args.getOrElse(index) {
23+
throw IllegalArgumentException(
24+
"Formatting failed: Placeholder '${match.value}' at position ${index + 1} is out of bounds. Only ${args.size} argument(s) provided for format string \"$this\""
25+
)
26+
}
2327
}
2428
}
2529

@@ -30,14 +34,10 @@ internal sealed interface StringItem {
3034
}
3135

3236
private val stringItemsCache = AsyncCache<String, StringItem>()
33-
//@TestOnly
34-
internal fun dropStringItemsCache() {
35-
stringItemsCache.clear()
36-
}
3737

3838
internal suspend fun getStringItem(
3939
resourceItem: ResourceItem,
40-
resourceReader: ResourceReader
40+
resourceReader: ResourceReader,
4141
): StringItem = stringItemsCache.getOrLoad(
4242
key = "${resourceItem.path}/${resourceItem.offset}-${resourceItem.size}"
4343
) {

components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/StringFormatTest.kt

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ class StringFormatTest {
1616

1717
assertEquals("Hello Alice, you have 5 new messages!", result)
1818
}
19+
//@TestOnly
20+
internal fun dropStringItemsCache() {
21+
stringItemsCache.clear()
22+
}
1923

2024
@Test
2125
fun `replaceWithArgs works with multiple placeholders referring to the same argument`() {
@@ -52,9 +56,13 @@ class StringFormatTest {
5256
val template = "Hello %1\$s, %2\$s!"
5357
val args = listOf("Alice")
5458

55-
assertFailsWith<IndexOutOfBoundsException> {
59+
val exception = assertFailsWith<IllegalArgumentException> {
5660
template.replaceWithArgs(args)
5761
}
62+
assertEquals(
63+
"Formatting failed: Placeholder '%2\$s' at position 2 is out of bounds. Only 1 argument(s) provided for format string \"Hello %1\$s, %2\$s!\"",
64+
exception.message
65+
)
5866
}
5967

6068
@Test
@@ -72,9 +80,13 @@ class StringFormatTest {
7280
val template = "Hello %1\$s, you have %3\$s messages"
7381
val args = listOf("Alice")
7482

75-
assertFailsWith<IndexOutOfBoundsException> {
83+
val exception = assertFailsWith<IllegalArgumentException> {
7684
template.replaceWithArgs(args)
7785
}
86+
assertEquals(
87+
"Formatting failed: Placeholder '%3\$s' at position 3 is out of bounds. Only 1 argument(s) provided for format string \"Hello %1\$s, you have %3\$s messages\"",
88+
exception.message
89+
)
7890
}
7991

8092
@Test
@@ -137,26 +149,34 @@ class StringFormatTest {
137149
}
138150

139151
@Test
152+
@IgnoreWasmTest // https://youtrack.jetbrains.com/issue/KT-69014, wasm throws RuntimeError instead of IndexOutOfBounds
140153
fun `replaceWithArgs throws exception for missing arguments`() {
141154
val template = "Hello %1\$s, you have %2\$d messages!"
142155
val args = listOf("Alice")
143156

144157
// An exception should be thrown because the second argument (%2$d) is missing
145-
assertFailsWith<IndexOutOfBoundsException> {
158+
val exception = assertFailsWith<IllegalArgumentException> {
146159
template.replaceWithArgs(args)
147160
}
161+
assertEquals(
162+
"Formatting failed: Placeholder '%2\$d' at position 2 is out of bounds. Only 1 argument(s) provided for format string \"Hello %1\$s, you have %2\$d messages!\"",
163+
exception.message
164+
)
148165
}
149166

150167
@Test
151-
@IgnoreWasmTest // https://youtrack.jetbrains.com/issue/KT-69014, wasm throws RuntimeError instead of IndexOutOfBounds
152168
fun `replaceWithArgs throws exception for unmatched placeholders`() {
153169
val template = "Hello %1\$s, your rank is %3\$s"
154170
val args = listOf("Alice", "1")
155171

156172
// The template has a %3$s placeholder, but there is no third argument
157-
assertFailsWith<IndexOutOfBoundsException> {
173+
val exception = assertFailsWith<IllegalArgumentException> {
158174
template.replaceWithArgs(args)
159175
}
176+
assertEquals(
177+
"Formatting failed: Placeholder '%3\$s' at position 3 is out of bounds. Only 2 argument(s) provided for format string \"Hello %1\$s, your rank is %3\$s\"",
178+
exception.message
179+
)
160180
}
161181

162182
@Test

0 commit comments

Comments
 (0)