mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
These trigger Objective-C exceptions in Foundation, but the fact these operations reliably trap when applied to foreign strings is part of the String contract in the stdlib, and we should have regression tests for them.
358 lines
10 KiB
Swift
358 lines
10 KiB
Swift
// RUN: %empty-directory(%t)
|
||
// RUN: %target-build-swift %s -o %t/a.out_Debug -Onone
|
||
// RUN: %target-build-swift %s -o %t/a.out_Release -O
|
||
//
|
||
// RUN: %target-codesign %t/a.out_Debug
|
||
// RUN: %target-codesign %t/a.out_Release
|
||
// RUN: %target-run %t/a.out_Debug
|
||
// RUN: %target-run %t/a.out_Release
|
||
// REQUIRES: executable_test
|
||
// UNSUPPORTED: OS=wasi
|
||
|
||
import StdlibUnittest
|
||
#if _runtime(_ObjC)
|
||
import Foundation // For NSString
|
||
#endif
|
||
|
||
let testSuiteSuffix = _isDebugAssertConfiguration() ? "_debug" : "_release"
|
||
|
||
var StringTraps = TestSuite("StringTraps" + testSuiteSuffix)
|
||
|
||
StringTraps.test("startIndex/predecessor")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.startIndex
|
||
i = s.index(after: i)
|
||
i = s.index(before: i)
|
||
expectCrashLater()
|
||
i = s.index(before: i)
|
||
}
|
||
|
||
StringTraps.test("endIndex/successor")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.startIndex
|
||
i = s.index(after: i)
|
||
i = s.index(after: i)
|
||
i = s.index(after: i)
|
||
expectCrashLater()
|
||
i = s.index(after: i)
|
||
}
|
||
|
||
StringTraps.test("subscript(_:)/endIndex")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.startIndex
|
||
i = s.index(after: i)
|
||
i = s.index(after: i)
|
||
i = s.index(after: i)
|
||
expectCrashLater()
|
||
_ = s[i]
|
||
}
|
||
|
||
StringTraps.test("String.index(before:) trap on i > endIndex")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let long = String(repeating: "X", count: 1024)
|
||
let short = "This is a short string"
|
||
expectCrashLater()
|
||
let i = short.index(before: long.endIndex)
|
||
print(i)
|
||
}
|
||
|
||
StringTraps.test("String.index(before:) trap on i == startIndex after scalar alignment")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let s = "🥯 Bagel with schmear"
|
||
let i = s.utf8.index(after: s.utf8.startIndex)
|
||
expectCrashLater()
|
||
// `i` is equivalent to `s.startIndex` as far as `String` is concerned
|
||
let j = s.index(before: i)
|
||
print(j)
|
||
}
|
||
|
||
StringTraps.test("UTF8ViewSubscript/endIndexSuccessor")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.utf8.startIndex
|
||
i = s.utf8.index(after: i)
|
||
i = s.utf8.index(after: i)
|
||
i = s.utf8.index(after: i)
|
||
expectCrashLater()
|
||
i = s.utf8.index(after: i)
|
||
_ = s.utf8[i]
|
||
}
|
||
|
||
StringTraps.test("UTF8ViewSubscript/endIndex")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.utf8.startIndex
|
||
i = s.utf8.index(after: i)
|
||
i = s.utf8.index(after: i)
|
||
i = s.utf8.index(after: i)
|
||
expectCrashLater()
|
||
_ = s.utf8[i]
|
||
}
|
||
|
||
StringTraps.test("UTF16ViewSubscript/DecrementedStartIndex")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.utf16.startIndex
|
||
expectCrashLater()
|
||
i = s.utf16.index(before: i)
|
||
_ = s.utf16[i]
|
||
}
|
||
|
||
StringTraps.test("UTF16ViewSubscript/endIndex")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s = "abc"
|
||
var i = s.utf16.startIndex
|
||
i = s.utf16.index(after: i)
|
||
i = s.utf16.index(after: i)
|
||
i = s.utf16.index(after: i)
|
||
expectCrashLater()
|
||
_ = s.utf16[i]
|
||
}
|
||
|
||
StringTraps.test("UTF16ViewIndex/offsetLimited")
|
||
.code {
|
||
let sa = "foo"
|
||
let u16a = sa.utf16
|
||
let s16 = sa + "🤦🏻♀️"
|
||
let u16 = s16.utf16
|
||
|
||
let iaBegin = u16a.index(sa.startIndex, offsetBy: 99, limitedBy: sa.endIndex)
|
||
expectNil(iaBegin)
|
||
let iaEnd = u16a.index(sa.endIndex, offsetBy: 99, limitedBy: sa.endIndex)
|
||
expectNil(iaEnd)
|
||
let i16Begin = u16.index(u16.startIndex, offsetBy: 99, limitedBy: u16.endIndex)
|
||
expectNil(i16Begin)
|
||
let i16End = u16.index(u16.startIndex, offsetBy: 99, limitedBy: u16.endIndex)
|
||
expectNil(i16End)
|
||
}
|
||
|
||
StringTraps.test("UTF16ViewIndex/offsetCrash")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s16 = "foo🤦🏻♀️"
|
||
let u16 = s16.utf16
|
||
expectCrashLater()
|
||
let i = u16.index(u16.startIndex, offsetBy: 99)
|
||
_ = s16.utf16[i]
|
||
}
|
||
|
||
StringTraps.test("UTF8ViewIndex/offsetLimited")
|
||
.code {
|
||
let sa = "foo"
|
||
let u8a = sa.utf8
|
||
let s8 = sa + "🤦🏻♀️"
|
||
let u8 = s8.utf8
|
||
|
||
let iaBegin = u8a.index(sa.startIndex, offsetBy: 99, limitedBy: sa.endIndex)
|
||
expectNil(iaBegin)
|
||
let iaEnd = u8a.index(sa.endIndex, offsetBy: 99, limitedBy: sa.endIndex)
|
||
expectNil(iaEnd)
|
||
let i8Begin = u8.index(u8.startIndex, offsetBy: 99, limitedBy: u8.endIndex)
|
||
expectNil(i8Begin)
|
||
let i8End = u8.index(u8.startIndex, offsetBy: 99, limitedBy: u8.endIndex)
|
||
expectNil(i8End)
|
||
}
|
||
|
||
StringTraps.test("UTF8ViewIndex/offsetCrash")
|
||
.skip(.custom(
|
||
{ _isFastAssertConfiguration() },
|
||
reason: "this trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
let s8 = "foo🤦🏻♀️"
|
||
let u8 = s8.utf8
|
||
expectCrashLater()
|
||
let i = u8.index(u8.startIndex, offsetBy: 99)
|
||
_ = s8.utf8[i]
|
||
}
|
||
|
||
StringTraps.test("UnicodeScalarView index(before:) trap on startIndex")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let s = "abc"
|
||
var i = s.unicodeScalars.endIndex
|
||
i = s.unicodeScalars.index(before: i)
|
||
i = s.unicodeScalars.index(before: i)
|
||
i = s.unicodeScalars.index(before: i)
|
||
expectCrashLater()
|
||
i = s.unicodeScalars.index(before: i)
|
||
}
|
||
|
||
StringTraps.test("UnicodeScalarView index(before:) trap on startIndex after scalar alignment")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let s = "🥦 Floret of broccoli"
|
||
var i = s.utf8.index(after: s.utf8.startIndex)
|
||
expectCrashLater()
|
||
// `i` is equivalent to `s.startIndex` as far as `String.UnicodeScalarView` is
|
||
// concerned
|
||
i = s.unicodeScalars.index(before: i)
|
||
}
|
||
|
||
StringTraps.test("UnicodeScalarView index(after:) trap on endIndex")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let s = "abc"
|
||
var i = s.unicodeScalars.startIndex
|
||
i = s.unicodeScalars.index(after: i)
|
||
i = s.unicodeScalars.index(after: i)
|
||
i = s.unicodeScalars.index(after: i)
|
||
expectCrashLater()
|
||
i = s.unicodeScalars.index(after: i)
|
||
}
|
||
|
||
StringTraps.test("UnicodeScalarView index(after:) trap on i > endIndex")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let long = "abcd"
|
||
var i = long.unicodeScalars.endIndex
|
||
|
||
let s = "abc"
|
||
expectCrashLater()
|
||
i = s.unicodeScalars.index(after: i)
|
||
}
|
||
|
||
StringTraps.test("UnicodeScalarView index(before:) trap on i > endIndex")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let long = "abcd"
|
||
var i = long.unicodeScalars.endIndex
|
||
|
||
let s = "abc"
|
||
expectCrashLater()
|
||
i = s.unicodeScalars.index(before: i)
|
||
}
|
||
|
||
#if _runtime(_ObjC)
|
||
StringTraps.test("UTF8View foreign index(after:) trap on i > endIndex")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let long = "🐘 This is a quite large string, with lots of data"
|
||
let short = ("🐭 I'm much smaller" as NSString) as String
|
||
|
||
var i = long.utf8.endIndex
|
||
expectCrashLater()
|
||
// Note: we expect that `short` will be UTF-16 encoded here -- this trap only
|
||
// happens on the foreign path. For native/shared strings, the UTF-8 view's
|
||
// `index(after:)` is essentially doing a simple `i + 1`, like Array does.
|
||
i = short.utf8.index(after: i)
|
||
}
|
||
#endif
|
||
|
||
#if _runtime(_ObjC)
|
||
StringTraps.test("UTF8View foreign index(before:) trap on i > endIndex")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let long = "🐘 This is a quite large string, with lots of data"
|
||
let short = ("🐭 I'm much smaller" as NSString) as String
|
||
|
||
var i = long.utf8.endIndex
|
||
expectCrashLater()
|
||
// Note: we expect that `short` will be UTF-16 encoded here -- this trap only
|
||
// happens on the foreign path. For native/shared strings, the UTF-8 view's
|
||
// `index(before:)` is essentially doing a simple `i - 1`, like Array does.
|
||
// (Following the unconditional i != startIndex check.)
|
||
i = short.utf8.index(before: i)
|
||
}
|
||
#endif
|
||
|
||
#if _runtime(_ObjC)
|
||
StringTraps.test("UTF8View foreign index(after:) trap on i == endIndex")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let s = ("🦧 The Librarian" as NSString) as String
|
||
|
||
var i = s.utf8.endIndex
|
||
expectCrashLater()
|
||
// Note: we expect that `short` will be UTF-16 encoded here -- this trap only
|
||
// happens on the foreign path. For native/shared strings, the UTF-8 view's
|
||
// `index(after:)` is essentially doing a simple `i + 1`, like Array does.
|
||
i = s.utf8.index(after: i)
|
||
}
|
||
#endif
|
||
|
||
#if _runtime(_ObjC)
|
||
StringTraps.test("UTF8View foreign index(before:) trap on i == startIndex")
|
||
.skip(
|
||
.custom({ _isFastAssertConfiguration() },
|
||
reason: "trap is not guaranteed to happen in -Ounchecked"))
|
||
.code {
|
||
guard #available(SwiftStdlib 5.7, *) else { return }
|
||
|
||
let s = ("🦧 The Librarian" as NSString) as String
|
||
var i = s.utf8.startIndex
|
||
expectCrashLater()
|
||
i = s.utf8.index(before: i)
|
||
}
|
||
#endif
|
||
|
||
runAllTests()
|
||
|