Skip to content

Commit

Permalink
Auto merge of #83079 - osa1:issue83046, r=m-ou-se
Browse files Browse the repository at this point in the history
Update char::escape_debug_ext to handle different escapes in strings and chars

Fixes #83046

The program

    fn main() {
        println!("{:?}", '"');
        println!("{:?}", "'");
    }

would previously print

    '\"'
    "\'"

With this patch it now prints:

    '"'
    "'"
  • Loading branch information
bors committed Mar 26, 2021
2 parents 1316702 + 819247f commit 4137088
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 19 deletions.
5 changes: 1 addition & 4 deletions library/alloc/tests/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
35 changes: 29 additions & 6 deletions library/core/src/char/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/char/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 11 additions & 2 deletions library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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])?;
Expand All @@ -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('\'')
Expand Down
10 changes: 7 additions & 3 deletions library/core/src/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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)),
Expand Down Expand Up @@ -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)]
Expand Down
4 changes: 2 additions & 2 deletions src/test/rustdoc-ui/check-doc-alias-attr.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "\"")]
Expand Down Expand Up @@ -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("\""))]
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/rustdoc/check-doc-alias-attr.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "\"")]
Expand Down Expand Up @@ -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("\""))]
Expand Down

0 comments on commit 4137088

Please sign in to comment.