Skip to content

Commit c51607e

Browse files
committed
Auto merge of #87062 - poliorcetics:fix-85462, r=dtolnay
Make StrSearcher behave correctly on empty needle Fix #85462. This will not affect ABI since the other variant of the enum is bigger. It may break some code, but that would be very strange: usually people don't continue after the first `Done` (or `None` for a normal iterator). `@rustbot` label T-libs A-str A-patterns
2 parents 8bebfe5 + cd04731 commit c51607e

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

library/alloc/tests/str.rs

+41
Original file line numberDiff line numberDiff line change
@@ -1873,6 +1873,47 @@ mod pattern {
18731873
"* \t",
18741874
[Reject(0, 1), Reject(1, 2), Reject(2, 3),]
18751875
);
1876+
1877+
// See #85462
1878+
#[test]
1879+
fn str_searcher_empty_needle_after_done() {
1880+
// Empty needle and haystack
1881+
{
1882+
let mut searcher = "".into_searcher("");
1883+
1884+
assert_eq!(searcher.next(), SearchStep::Match(0, 0));
1885+
assert_eq!(searcher.next(), SearchStep::Done);
1886+
assert_eq!(searcher.next(), SearchStep::Done);
1887+
assert_eq!(searcher.next(), SearchStep::Done);
1888+
1889+
let mut searcher = "".into_searcher("");
1890+
1891+
assert_eq!(searcher.next_back(), SearchStep::Match(0, 0));
1892+
assert_eq!(searcher.next_back(), SearchStep::Done);
1893+
assert_eq!(searcher.next_back(), SearchStep::Done);
1894+
assert_eq!(searcher.next_back(), SearchStep::Done);
1895+
}
1896+
// Empty needle and non-empty haystack
1897+
{
1898+
let mut searcher = "".into_searcher("a");
1899+
1900+
assert_eq!(searcher.next(), SearchStep::Match(0, 0));
1901+
assert_eq!(searcher.next(), SearchStep::Reject(0, 1));
1902+
assert_eq!(searcher.next(), SearchStep::Match(1, 1));
1903+
assert_eq!(searcher.next(), SearchStep::Done);
1904+
assert_eq!(searcher.next(), SearchStep::Done);
1905+
assert_eq!(searcher.next(), SearchStep::Done);
1906+
1907+
let mut searcher = "".into_searcher("a");
1908+
1909+
assert_eq!(searcher.next_back(), SearchStep::Match(1, 1));
1910+
assert_eq!(searcher.next_back(), SearchStep::Reject(0, 1));
1911+
assert_eq!(searcher.next_back(), SearchStep::Match(0, 0));
1912+
assert_eq!(searcher.next_back(), SearchStep::Done);
1913+
assert_eq!(searcher.next_back(), SearchStep::Done);
1914+
assert_eq!(searcher.next_back(), SearchStep::Done);
1915+
}
1916+
}
18761917
}
18771918

18781919
macro_rules! generate_iterator_test {

library/core/src/str/pattern.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,8 @@ struct EmptyNeedle {
928928
end: usize,
929929
is_match_fw: bool,
930930
is_match_bw: bool,
931+
// Needed in case of an empty haystack, see #85462
932+
is_finished: bool,
931933
}
932934

933935
impl<'a, 'b> StrSearcher<'a, 'b> {
@@ -941,6 +943,7 @@ impl<'a, 'b> StrSearcher<'a, 'b> {
941943
end: haystack.len(),
942944
is_match_fw: true,
943945
is_match_bw: true,
946+
is_finished: false,
944947
}),
945948
}
946949
} else {
@@ -966,13 +969,19 @@ unsafe impl<'a, 'b> Searcher<'a> for StrSearcher<'a, 'b> {
966969
fn next(&mut self) -> SearchStep {
967970
match self.searcher {
968971
StrSearcherImpl::Empty(ref mut searcher) => {
972+
if searcher.is_finished {
973+
return SearchStep::Done;
974+
}
969975
// empty needle rejects every char and matches every empty string between them
970976
let is_match = searcher.is_match_fw;
971977
searcher.is_match_fw = !searcher.is_match_fw;
972978
let pos = searcher.position;
973979
match self.haystack[pos..].chars().next() {
974980
_ if is_match => SearchStep::Match(pos, pos),
975-
None => SearchStep::Done,
981+
None => {
982+
searcher.is_finished = true;
983+
SearchStep::Done
984+
}
976985
Some(ch) => {
977986
searcher.position += ch.len_utf8();
978987
SearchStep::Reject(pos, searcher.position)
@@ -1045,12 +1054,18 @@ unsafe impl<'a, 'b> ReverseSearcher<'a> for StrSearcher<'a, 'b> {
10451054
fn next_back(&mut self) -> SearchStep {
10461055
match self.searcher {
10471056
StrSearcherImpl::Empty(ref mut searcher) => {
1057+
if searcher.is_finished {
1058+
return SearchStep::Done;
1059+
}
10481060
let is_match = searcher.is_match_bw;
10491061
searcher.is_match_bw = !searcher.is_match_bw;
10501062
let end = searcher.end;
10511063
match self.haystack[..end].chars().next_back() {
10521064
_ if is_match => SearchStep::Match(end, end),
1053-
None => SearchStep::Done,
1065+
None => {
1066+
searcher.is_finished = true;
1067+
SearchStep::Done
1068+
}
10541069
Some(ch) => {
10551070
searcher.end -= ch.len_utf8();
10561071
SearchStep::Reject(searcher.end, end)

0 commit comments

Comments
 (0)