From 0d8a0c56fee71dac218eb949817669ab8bb00c5e Mon Sep 17 00:00:00 2001 From: Konrad Borowski Date: Tue, 9 Aug 2022 07:07:29 +0000 Subject: [PATCH 1/2] Rename LinesAnyMap to LinesMap lines_any method was replaced with lines method, so it makes sense to rename this structure to match new name. Co-authored-by: Ian Jackson --- library/core/src/str/iter.rs | 4 ++-- library/core/src/str/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 24083ee6af4..660a3b091b3 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -13,7 +13,7 @@ use super::pattern::Pattern; use super::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; use super::validations::{next_code_point, next_code_point_reverse}; -use super::LinesAnyMap; +use super::LinesMap; use super::{BytesIsNotEmpty, UnsafeBytesToStr}; use super::{CharEscapeDebugContinue, CharEscapeDefault, CharEscapeUnicode}; use super::{IsAsciiWhitespace, IsNotEmpty, IsWhitespace}; @@ -1091,7 +1091,7 @@ fn next_back(&mut self) -> Option<&'a str> #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] -pub struct Lines<'a>(pub(super) Map, LinesAnyMap>); +pub struct Lines<'a>(pub(super) Map, LinesMap>); #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Iterator for Lines<'a> { diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index fbc0fc397a5..434c1598ee4 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -997,7 +997,7 @@ pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn lines(&self) -> Lines<'_> { - Lines(self.split_terminator('\n').map(LinesAnyMap)) + Lines(self.split_terminator('\n').map(LinesMap)) } /// An iterator over the lines of a string. @@ -2590,7 +2590,7 @@ fn default() -> Self { impl_fn_for_zst! { /// A nameable, cloneable fn type #[derive(Clone)] - struct LinesAnyMap impl<'a> Fn = |line: &'a str| -> &'a str { + struct LinesMap impl<'a> Fn = |line: &'a str| -> &'a str { let l = line.len(); if l > 0 && line.as_bytes()[l - 1] == b'\r' { &line[0 .. l - 1] } else { line } From cef81dcd0a9503ff8a8f915c43688a78c1c11d83 Mon Sep 17 00:00:00 2001 From: Konrad Borowski Date: Tue, 9 Aug 2022 07:12:15 +0000 Subject: [PATCH 2/2] Fix handling of trailing bare CR in str::lines Previously "bare\r" was split into ["bare"] even though the documentation said that only LF and CRLF count as newlines. This fix is a behavioural change, even though it brings the behaviour into line with the documentation, and into line with that of `std::io::BufRead::lines()`. This is an alternative to #91051, which proposes to document rather than fix the behaviour. Fixes #94435. Co-authored-by: Ian Jackson --- library/alloc/tests/str.rs | 26 +++++++++++++++++++------- library/core/src/str/iter.rs | 2 +- library/core/src/str/mod.rs | 8 ++++---- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index e30329aa1cb..aa767d5691a 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -1499,13 +1499,25 @@ fn test_split_whitespace() { #[test] fn test_lines() { - let data = "\nMäry häd ä little lämb\n\r\nLittle lämb\n"; - let lines: Vec<&str> = data.lines().collect(); - assert_eq!(lines, ["", "Märy häd ä little lämb", "", "Little lämb"]); - - let data = "\r\nMäry häd ä little lämb\n\nLittle lämb"; // no trailing \n - let lines: Vec<&str> = data.lines().collect(); - assert_eq!(lines, ["", "Märy häd ä little lämb", "", "Little lämb"]); + fn t(data: &str, expected: &[&str]) { + let lines: Vec<&str> = data.lines().collect(); + assert_eq!(lines, expected); + } + t("", &[]); + t("\n", &[""]); + t("\n2nd", &["", "2nd"]); + t("\r\n", &[""]); + t("bare\r", &["bare\r"]); + t("bare\rcr", &["bare\rcr"]); + t("Text\n\r", &["Text", "\r"]); + t( + "\nMäry häd ä little lämb\n\r\nLittle lämb\n", + &["", "Märy häd ä little lämb", "", "Little lämb"], + ); + t( + "\r\nMäry häd ä little lämb\n\nLittle lämb", + &["", "Märy häd ä little lämb", "", "Little lämb"], + ); } #[test] diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 660a3b091b3..579f5806b1c 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -1091,7 +1091,7 @@ fn next_back(&mut self) -> Option<&'a str> #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] -pub struct Lines<'a>(pub(super) Map, LinesMap>); +pub struct Lines<'a>(pub(super) Map, LinesMap>); #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Iterator for Lines<'a> { diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 434c1598ee4..49478a72f0e 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -997,7 +997,7 @@ pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn lines(&self) -> Lines<'_> { - Lines(self.split_terminator('\n').map(LinesMap)) + Lines(self.split_inclusive('\n').map(LinesMap)) } /// An iterator over the lines of a string. @@ -2591,9 +2591,9 @@ fn default() -> Self { /// A nameable, cloneable fn type #[derive(Clone)] struct LinesMap impl<'a> Fn = |line: &'a str| -> &'a str { - let l = line.len(); - if l > 0 && line.as_bytes()[l - 1] == b'\r' { &line[0 .. l - 1] } - else { line } + let Some(line) = line.strip_suffix('\n') else { return line }; + let Some(line) = line.strip_suffix('\r') else { return line }; + line }; #[derive(Clone)]