diff --git a/Sources/_StringProcessing/Algorithms/Searchers/TwoWaySearcher.swift b/Sources/_StringProcessing/Algorithms/Searchers/TwoWaySearcher.swift index c3537d415..2428f89cd 100644 --- a/Sources/_StringProcessing/Algorithms/Searchers/TwoWaySearcher.swift +++ b/Sources/_StringProcessing/Algorithms/Searchers/TwoWaySearcher.swift @@ -133,7 +133,10 @@ extension TwoWaySearcher: CollectionSearcher { searched.formIndex(before: &lIndex) if pattern[i] != searched[lIndex] { - searched.formIndex(&state.criticalIndex, offsetBy: period) + _ = searched.formIndex( + &state.criticalIndex, + offsetBy: period, + limitedBy: searched.endIndex) if periodIsExact { state.memory = (pattern.count - period, end) } return nil } diff --git a/Tests/RegexTests/AlgorithmsTests.swift b/Tests/RegexTests/AlgorithmsTests.swift index 71eff7c6d..8ff3ad607 100644 --- a/Tests/RegexTests/AlgorithmsTests.swift +++ b/Tests/RegexTests/AlgorithmsTests.swift @@ -174,6 +174,16 @@ class AlgorithmTests: XCTestCase { expectRanges("ababacabababa", "abababa", [6..<13]) expectRanges("ababacabababa", "aba", [0..<3, 6..<9, 10..<13]) } + + // rdar://105154010 + func testFirstRangeMissingCrash() { + let str = "%2$@ %#@AROUND_TIME@" + let target = "%@" + XCTAssertNil(str.firstRange(of: target)) + XCTAssertNil(str.dropFirst().dropLast().firstRange(of: target)) + XCTAssertNil(str.dropFirst().dropLast().firstRange(of: target[...])) + XCTAssertNil(str.firstRange(of: target[...])) + } func testRegexSplit() { func expectSplit(