From 819247f179c0af011ea7219dc361209851eb487d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 26 Mar 2021 11:23:51 +0300 Subject: [PATCH] Update char::escape_debug_ext to handle different escapes in strings vs. chars Fixes #83046 The program fn main() { println!("{:?}", '"'); println!("{:?}", "'"); } would previously print '\"' "\'" With this patch it now prints: '"' "'" --- library/alloc/tests/fmt.rs | 5 +-- library/core/src/char/methods.rs | 35 +++++++++++++++---- library/core/src/char/mod.rs | 2 ++ library/core/src/fmt/mod.rs | 13 +++++-- library/core/src/str/mod.rs | 10 ++++-- .../rustdoc-ui/check-doc-alias-attr.stderr | 4 +-- .../ui/rustdoc/check-doc-alias-attr.stderr | 4 +-- 7 files changed, 54 insertions(+), 19 deletions(-) diff --git a/library/alloc/tests/fmt.rs b/library/alloc/tests/fmt.rs index 757fddd241857..a121c54428ca4 100644 --- a/library/alloc/tests/fmt.rs +++ b/library/alloc/tests/fmt.rs @@ -68,10 +68,7 @@ fn test_format_macro_interface() { t!(format!("{:?}", 10_usize), "10"); t!(format!("{:?}", "true"), "\"true\""); t!(format!("{:?}", "foo\nbar"), "\"foo\\nbar\""); - t!( - format!("{:?}", "foo\n\"bar\"\r\n\'baz\'\t\\qux\\"), - r#""foo\n\"bar\"\r\n\'baz\'\t\\qux\\""# - ); + t!(format!("{:?}", "foo\n\"bar\"\r\n\'baz\'\t\\qux\\"), r#""foo\n\"bar\"\r\n'baz'\t\\qux\\""#); t!(format!("{:?}", "foo\0bar\x01baz\u{7f}q\u{75}x"), r#""foo\u{0}bar\u{1}baz\u{7f}qux""#); t!(format!("{:o}", 10_usize), "12"); t!(format!("{:x}", 10_usize), "a"); diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 87a3d375a6951..dcab2cd2d9db1 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -403,16 +403,20 @@ impl char { } /// An extended version of `escape_debug` that optionally permits escaping - /// Extended Grapheme codepoints. This allows us to format characters like - /// nonspacing marks better when they're at the start of a string. + /// Extended Grapheme codepoints, single quotes, and double quotes. This + /// allows us to format characters like nonspacing marks better when they're + /// at the start of a string, and allows escaping single quotes in + /// characters, and double quotes in strings. #[inline] - pub(crate) fn escape_debug_ext(self, escape_grapheme_extended: bool) -> EscapeDebug { + pub(crate) fn escape_debug_ext(self, args: EscapeDebugExtArgs) -> EscapeDebug { let init_state = match self { '\t' => EscapeDefaultState::Backslash('t'), '\r' => EscapeDefaultState::Backslash('r'), '\n' => EscapeDefaultState::Backslash('n'), - '\\' | '\'' | '"' => EscapeDefaultState::Backslash(self), - _ if escape_grapheme_extended && self.is_grapheme_extended() => { + '\\' => EscapeDefaultState::Backslash(self), + '"' if args.escape_double_quote => EscapeDefaultState::Backslash(self), + '\'' if args.escape_single_quote => EscapeDefaultState::Backslash(self), + _ if args.escape_grapheme_extended && self.is_grapheme_extended() => { EscapeDefaultState::Unicode(self.escape_unicode()) } _ if is_printable(self) => EscapeDefaultState::Char(self), @@ -458,7 +462,7 @@ impl char { #[stable(feature = "char_escape_debug", since = "1.20.0")] #[inline] pub fn escape_debug(self) -> EscapeDebug { - self.escape_debug_ext(true) + self.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL) } /// Returns an iterator that yields the literal escape code of a character @@ -1565,6 +1569,25 @@ impl char { } } +pub(crate) struct EscapeDebugExtArgs { + /// Escape Extended Grapheme codepoints? + pub(crate) escape_grapheme_extended: bool, + + /// Escape single quotes? + pub(crate) escape_single_quote: bool, + + /// Escape double quotes? + pub(crate) escape_double_quote: bool, +} + +impl EscapeDebugExtArgs { + pub(crate) const ESCAPE_ALL: Self = Self { + escape_grapheme_extended: true, + escape_single_quote: true, + escape_double_quote: true, + }; +} + #[inline] const fn len_utf8(code: u32) -> usize { if code < MAX_ONE_B { diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 788fafa0adc5c..25a7c1de9de4b 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -45,6 +45,8 @@ pub use self::methods::encode_utf8_raw; use crate::fmt::{self, Write}; use crate::iter::FusedIterator; +pub(crate) use self::methods::EscapeDebugExtArgs; + // UTF-8 ranges and tags for encoding characters const TAG_CONT: u8 = 0b1000_0000; const TAG_TWO_B: u8 = 0b1100_0000; diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 2df5e562745d0..d211ad4b2f7f4 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -3,6 +3,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; +use crate::char::EscapeDebugExtArgs; use crate::marker::PhantomData; use crate::mem; use crate::num::flt2dec; @@ -2054,7 +2055,11 @@ impl Debug for str { f.write_char('"')?; let mut from = 0; for (i, c) in self.char_indices() { - let esc = c.escape_debug(); + let esc = c.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: true, + escape_single_quote: false, + escape_double_quote: true, + }); // If char needs escaping, flush backlog so far and write, else skip if esc.len() != 1 { f.write_str(&self[from..i])?; @@ -2080,7 +2085,11 @@ impl Display for str { impl Debug for char { fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.write_char('\'')?; - for c in self.escape_debug() { + for c in self.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: true, + escape_single_quote: true, + escape_double_quote: false, + }) { f.write_char(c)? } f.write_char('\'') diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 03ed301eacf8c..95dd54976b2c0 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -15,7 +15,7 @@ mod validations; use self::pattern::Pattern; use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; -use crate::char; +use crate::char::{self, EscapeDebugExtArgs}; use crate::mem; use crate::slice::{self, SliceIndex}; @@ -2342,7 +2342,7 @@ impl str { EscapeDebug { inner: chars .next() - .map(|first| first.escape_debug_ext(true)) + .map(|first| first.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL)) .into_iter() .flatten() .chain(chars.flat_map(CharEscapeDebugContinue)), @@ -2460,7 +2460,11 @@ impl_fn_for_zst! { #[derive(Clone)] struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { - c.escape_debug_ext(false) + c.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: false, + escape_single_quote: true, + escape_double_quote: true + }) }; #[derive(Clone)] diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.stderr b/src/test/rustdoc-ui/check-doc-alias-attr.stderr index f99d69dc101b6..250568be3333f 100644 --- a/src/test/rustdoc-ui/check-doc-alias-attr.stderr +++ b/src/test/rustdoc-ui/check-doc-alias-attr.stderr @@ -10,7 +10,7 @@ error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of s LL | #[doc(alias = 0)] | ^^^^^^^^^ -error: '\"' character isn't allowed in `#[doc(alias = "...")]` +error: '"' character isn't allowed in `#[doc(alias = "...")]` --> $DIR/check-doc-alias-attr.rs:9:15 | LL | #[doc(alias = "\"")] @@ -60,7 +60,7 @@ error: `#[doc(alias("a"))]` expects string literals LL | #[doc(alias(0))] | ^ -error: '\"' character isn't allowed in `#[doc(alias("..."))]` +error: '"' character isn't allowed in `#[doc(alias("..."))]` --> $DIR/check-doc-alias-attr.rs:20:13 | LL | #[doc(alias("\""))] diff --git a/src/test/ui/rustdoc/check-doc-alias-attr.stderr b/src/test/ui/rustdoc/check-doc-alias-attr.stderr index f99d69dc101b6..250568be3333f 100644 --- a/src/test/ui/rustdoc/check-doc-alias-attr.stderr +++ b/src/test/ui/rustdoc/check-doc-alias-attr.stderr @@ -10,7 +10,7 @@ error: doc alias attribute expects a string `#[doc(alias = "a")]` or a list of s LL | #[doc(alias = 0)] | ^^^^^^^^^ -error: '\"' character isn't allowed in `#[doc(alias = "...")]` +error: '"' character isn't allowed in `#[doc(alias = "...")]` --> $DIR/check-doc-alias-attr.rs:9:15 | LL | #[doc(alias = "\"")] @@ -60,7 +60,7 @@ error: `#[doc(alias("a"))]` expects string literals LL | #[doc(alias(0))] | ^ -error: '\"' character isn't allowed in `#[doc(alias("..."))]` +error: '"' character isn't allowed in `#[doc(alias("..."))]` --> $DIR/check-doc-alias-attr.rs:20:13 | LL | #[doc(alias("\""))]