Skip to content

Commit a70e857

Browse files
[stdlib] Fix issue with UTF16 index(_:offsetBy:limitedBy) (#12378)
* Fix issue with empty string ranges * Add tests for basic offsetBy operation on UTF16View.Index
1 parent 2f932ff commit a70e857

File tree

3 files changed

+48
-11
lines changed

3 files changed

+48
-11
lines changed

stdlib/public/core/StringUTF16.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ extension String {
176176
) -> Index? {
177177
// FIXME: swift-3-indexing-model: range check i?
178178
let d = i.encodedOffset.distance(to: limit.encodedOffset)
179-
if (d > 0) ? (d < n) : (d > n) {
179+
if (d >= 0) ? (d < n) : (d > n) {
180180
return nil
181181
}
182182
return Index(encodedOffset: i.encodedOffset.advanced(by: n))

test/Interpreter/SDK/Foundation_test.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ FoundationTestSuite.test("RangeConversion") {
177177
let nsrFrom = NSRange(b..., in: s)
178178
expectEqual(nsrFrom.location,5)
179179
expectEqual(nsrFrom.length, 5)
180+
181+
expectNil(Range(NSRange(location: 100, length: 0), in: s))
182+
expectNil(Range(NSRange(location: 0, length: 100), in: s))
183+
184+
let empty = ""
185+
expectNil(Range(NSRange(location: 1, length: 0), in: empty))
186+
expectNil(Range(NSRange(location: 0, length: 1), in: empty))
187+
expectNotNil(Range(NSRange(location: 0, length: 0), in: empty))
180188

181189
// FIXME: enable once indices conform to RangeExpression
182190
// let nsrFull = NSRange(s.indices, in: s)

test/stdlib/StringTraps.swift

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ StringTraps.test("startIndex/predecessor")
1717
{ _isFastAssertConfiguration() },
1818
reason: "this trap is not guaranteed to happen in -Ounchecked"))
1919
.code {
20-
var s = "abc"
20+
let s = "abc"
2121
var i = s.startIndex
2222
i = s.index(after: i)
2323
i = s.index(before: i)
@@ -30,7 +30,7 @@ StringTraps.test("endIndex/successor")
3030
{ _isFastAssertConfiguration() },
3131
reason: "this trap is not guaranteed to happen in -Ounchecked"))
3232
.code {
33-
var s = "abc"
33+
let s = "abc"
3434
var i = s.startIndex
3535
i = s.index(after: i)
3636
i = s.index(after: i)
@@ -44,21 +44,21 @@ StringTraps.test("subscript(_:)/endIndex")
4444
{ _isFastAssertConfiguration() },
4545
reason: "this trap is not guaranteed to happen in -Ounchecked"))
4646
.code {
47-
var s = "abc"
47+
let s = "abc"
4848
var i = s.startIndex
4949
i = s.index(after: i)
5050
i = s.index(after: i)
5151
i = s.index(after: i)
5252
expectCrashLater()
53-
s[i]
53+
_ = s[i]
5454
}
5555

5656
StringTraps.test("UTF8ViewEndIndexSuccessor")
5757
.skip(.custom(
5858
{ _isFastAssertConfiguration() },
5959
reason: "this trap is not guaranteed to happen in -Ounchecked"))
6060
.code {
61-
var s = "abc"
61+
let s = "abc"
6262
var i = s.utf8.startIndex
6363
i = s.utf8.index(after: i)
6464
i = s.utf8.index(after: i)
@@ -72,25 +72,25 @@ StringTraps.test("UTF8ViewSubscript/endIndex")
7272
{ _isFastAssertConfiguration() },
7373
reason: "this trap is not guaranteed to happen in -Ounchecked"))
7474
.code {
75-
var s = "abc"
75+
let s = "abc"
7676
var i = s.utf8.startIndex
7777
i = s.utf8.index(after: i)
7878
i = s.utf8.index(after: i)
7979
i = s.utf8.index(after: i)
8080
expectCrashLater()
81-
s.utf8[i]
81+
_ = s.utf8[i]
8282
}
8383

8484
StringTraps.test("UTF16ViewSubscript/DecrementedStartIndex")
8585
.skip(.custom(
8686
{ _isFastAssertConfiguration() },
8787
reason: "this trap is not guaranteed to happen in -Ounchecked"))
8888
.code {
89-
var s = "abc"
89+
let s = "abc"
9090
var i = s.utf16.startIndex
9191
expectCrashLater()
9292
i = s.utf16.index(before: i)
93-
s.utf16[i]
93+
_ = s.utf16[i]
9494
}
9595

9696
StringTraps.test("UTF16ViewSubscript/endIndex")
@@ -104,7 +104,36 @@ StringTraps.test("UTF16ViewSubscript/endIndex")
104104
i = s.utf16.index(after: i)
105105
i = s.utf16.index(after: i)
106106
expectCrashLater()
107-
s.utf16[i]
107+
_ = s.utf16[i]
108+
}
109+
110+
StringTraps.test("UTF16ViewIndex/offsetLimited")
111+
.code {
112+
let sa = "foo"
113+
let u16a = sa.utf16
114+
let s16 = sa + "🤦🏻‍♀️"
115+
let u16 = s16.utf16
116+
117+
let iaBegin = u16a.index(sa.startIndex, offsetBy: 99, limitedBy: sa.endIndex)
118+
expectNil(iaBegin)
119+
let iaEnd = u16a.index(sa.endIndex, offsetBy: 99, limitedBy: sa.endIndex)
120+
expectNil(iaEnd)
121+
let i16Begin = u16.index(u16.startIndex, offsetBy: 99, limitedBy: u16.endIndex)
122+
expectNil(i16Begin)
123+
let i16End = u16.index(u16.startIndex, offsetBy: 99, limitedBy: u16.endIndex)
124+
expectNil(i16End)
125+
}
126+
127+
StringTraps.test("UTF16ViewIndex/offsetCrash")
128+
.skip(.custom(
129+
{ _isFastAssertConfiguration() },
130+
reason: "this trap is not guaranteed to happen in -Ounchecked"))
131+
.code {
132+
let s16 = "foo🤦🏻‍♀️"
133+
let u16 = s16.utf16
134+
expectCrashLater()
135+
let i = u16.index(u16.startIndex, offsetBy: 99)
136+
_ = s16.utf16[i]
108137
}
109138

110139
runAllTests()

0 commit comments

Comments
 (0)