From 0ae1df7dfc9cc3ecaa45b8879889f2b45eda078b 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 24083ee6af44f..660a3b091b3d1 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -13,7 +13,7 @@ use super::from_utf8_unchecked; 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 @@ generate_pattern_iterators! { #[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 ee66895a7ed2a..296fb4792eae6 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -995,7 +995,7 @@ impl str { #[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. @@ -2588,7 +2588,7 @@ impl Default for &mut str { 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 e5377776044ee6c45453fa8fc83d37c130397b2b 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/lib.rs | 1 + library/core/src/str/iter.rs | 2 +- library/core/src/str/mod.rs | 8 ++++---- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 7379569dd68fe..193d46ed20b73 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/lib.rs b/library/core/src/lib.rs index 24742bb49b9a5..afa22b7ece3da 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -184,6 +184,7 @@ #![feature(intra_doc_pointers)] #![feature(intrinsics)] #![feature(lang_items)] +#![feature(let_else)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] #![feature(min_specialization)] diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 660a3b091b3d1..579f5806b1cd6 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -1091,7 +1091,7 @@ generate_pattern_iterators! { #[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 296fb4792eae6..4cf83ffd9d2a1 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -995,7 +995,7 @@ impl str { #[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. @@ -2589,9 +2589,9 @@ impl_fn_for_zst! { /// 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)]