From ceace40628047b986ef556b3b4d7663f9d166701 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sun, 10 Jul 2022 20:36:27 -0400 Subject: [PATCH] Extend and expose API for {:x?} and {:X?} formatting --- compiler/rustc_parse_format/src/lib.rs | 91 ++++++++++++------ compiler/rustc_parse_format/src/tests.rs | 103 ++++++++++++-------- library/alloc/src/boxed.rs | 6 +- library/alloc/src/fmt.rs | 14 +-- library/alloc/src/lib.rs | 1 + library/alloc/src/rc.rs | 6 +- library/alloc/src/sync.rs | 6 +- library/core/src/fmt/float.rs | 8 +- library/core/src/fmt/mod.rs | 115 ++++++++++++++++++----- library/core/src/fmt/num.rs | 14 +-- library/core/src/sync/atomic.rs | 6 +- library/core/tests/fmt/mod.rs | 8 ++ library/core/tests/fmt/num.rs | 15 ++- 13 files changed, 279 insertions(+), 114 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index df22d79f82e85..249b0f64bccf9 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -142,19 +142,36 @@ pub enum Alignment { #[derive(Copy, Clone, Debug, PartialEq)] pub enum Flag { /// A `+` will be used to denote positive numbers. - FlagSignPlus, + FlagSignPlus = 0b0000_0001, /// A `-` will be used to denote negative numbers. This is the default. - FlagSignMinus, + FlagSignMinus = 0b0000_0010, /// An alternate form will be used for the value. In the case of numbers, /// this means that the number will be prefixed with the supplied string. - FlagAlternate, + FlagAlternate = 0b0000_0100, /// For numbers, this means that the number will be padded with zeroes, /// and the sign (`+` or `-`) will precede them. - FlagSignAwareZeroPad, - /// For Debug / `?`, format integers in lower-case hexadecimal. - FlagDebugLowerHex, - /// For Debug / `?`, format integers in upper-case hexadecimal. - FlagDebugUpperHex, + FlagSignAwareZeroPad = 0b0000_1000, + /// For Debug / `?`, the variant used. + FlagDebugVariant = 0b1111_0000_0000, +} + +/// Version of `DebugVariant` that can be used on stable. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum DebugVariant { + /// [`LowerHex`]-like formatting. (`{:x?}`) + LowerHex = 0b0001_0000_0000, + /// [`UpperHex`]-like formatting. (`{:X?}`) + UpperHex = 0b0010_0000_0000, + /// [`Octal`]-like formatting. (`{:o?}`) + Octal = 0b0100_0000_0000, + /// [`Binary`]-like formatting. (`{:b?}`) + Binary = 0b1000_0000_0000, + /// [`LowerExp`]-like formatting. (`{:e?}`) + LowerExp = 0b0011_0000_0000, + /// [`UpperExp`]-like formatting. (`{:E?}`) + UpperExp = 0b0110_0000_0000, + /// [`Pointer`]-like formatting. (`{:p?}`) + Pointer = 0b1100_0000_0000, } /// A count is used for the precision and width parameters of an integer, and @@ -580,13 +597,13 @@ impl<'a> Parser<'a> { } // Sign flags if self.consume('+') { - spec.flags |= 1 << (FlagSignPlus as u32); + spec.flags |= FlagSignPlus as u32; } else if self.consume('-') { - spec.flags |= 1 << (FlagSignMinus as u32); + spec.flags |= FlagSignMinus as u32; } // Alternate marker if self.consume('#') { - spec.flags |= 1 << (FlagAlternate as u32); + spec.flags |= FlagAlternate as u32; } // Width and precision let mut havewidth = false; @@ -601,7 +618,7 @@ impl<'a> Parser<'a> { spec.width_span = Some(self.span(end - 1, end + 1)); havewidth = true; } else { - spec.flags |= 1 << (FlagSignAwareZeroPad as u32); + spec.flags |= FlagSignAwareZeroPad as u32; } } @@ -628,31 +645,43 @@ impl<'a> Parser<'a> { spec.precision_span = Some(self.span(start, end)); } - let ty_span_start = self.current_pos(); + let mut ty_span_start = self.cur.peek().map(|(pos, _)| *pos); // Optional radix followed by the actual format specifier - if self.consume('x') { - if self.consume('?') { - spec.flags |= 1 << (FlagDebugLowerHex as u32); - spec.ty = "?"; - } else { - spec.ty = "x"; - } - } else if self.consume('X') { - if self.consume('?') { - spec.flags |= 1 << (FlagDebugUpperHex as u32); - spec.ty = "?"; - } else { - spec.ty = "X"; - } - } else if self.consume('?') { + if self.consume('?') { spec.ty = "?"; } else { spec.ty = self.word(); - if !spec.ty.is_empty() { - let ty_span_end = self.current_pos(); - spec.ty_span = Some(self.span(ty_span_start, ty_span_end)); + + // FIXME: let chains + if let Some(&(pos, c)) = self.cur.peek() { + if c == '?' { + let variant = match spec.ty { + "x" => Some(DebugVariant::LowerHex), + "X" => Some(DebugVariant::UpperHex), + "o" => Some(DebugVariant::Octal), + "b" => Some(DebugVariant::Binary), + "e" => Some(DebugVariant::LowerExp), + "E" => Some(DebugVariant::UpperExp), + "p" => Some(DebugVariant::Pointer), + _ => None, + }; + if let Some(variant) = variant { + spec.ty = "?"; + spec.flags |= variant as u32; + ty_span_start = Some(pos); + + // only advance if we have a valid variant + self.cur.next(); + } + } } } + let ty_span_end = self.cur.peek().map(|(pos, _)| *pos); + if !spec.ty.is_empty() { + spec.ty_span = ty_span_start + .and_then(|s| ty_span_end.map(|e| (s, e))) + .map(|(start, end)| self.to_span_index(start).to(self.to_span_index(end))); + } spec } diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 2f8c229c68ffe..cea6383455719 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -63,7 +63,7 @@ fn format_nothing() { "{}", &[NextArgument(Argument { position: ArgumentImplicitlyIs(0), - position_span: InnerSpan { start: 2, end: 2 }, + position_span: InnerSpan::new(2, 2), format: fmtdflt(), })], ); @@ -74,7 +74,7 @@ fn format_position() { "{3}", &[NextArgument(Argument { position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, + position_span: InnerSpan::new(2, 3), format: fmtdflt(), })], ); @@ -85,7 +85,7 @@ fn format_position_nothing_else() { "{3:}", &[NextArgument(Argument { position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, + position_span: InnerSpan::new(2, 3), format: fmtdflt(), })], ); @@ -96,7 +96,7 @@ fn format_named() { "{name}", &[NextArgument(Argument { position: ArgumentNamed("name"), - position_span: InnerSpan { start: 2, end: 6 }, + position_span: InnerSpan::new(2, 6), format: fmtdflt(), })], ) @@ -107,7 +107,7 @@ fn format_type() { "{3:x}", &[NextArgument(Argument { position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, + position_span: InnerSpan::new(2, 3), format: FormatSpec { fill: None, align: AlignUnknown, @@ -117,7 +117,28 @@ fn format_type() { precision_span: None, width_span: None, ty: "x", - ty_span: None, + ty_span: Some(InnerSpan::new(4, 5)), + }, + })], + ); +} +#[test] +fn format_variant() { + same( + "{3:x?}", + &[NextArgument(Argument { + position: ArgumentIs(3), + position_span: InnerSpan::new(2, 3), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: (DebugVariant::LowerHex as u32), + precision: CountImplied, + width: CountImplied, + precision_span: None, + width_span: None, + ty: "?", + ty_span: Some(InnerSpan::new(5, 6)), }, })], ); @@ -128,7 +149,7 @@ fn format_align_fill() { "{3:>}", &[NextArgument(Argument { position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, + position_span: InnerSpan::new(2, 3), format: FormatSpec { fill: None, align: AlignRight, @@ -146,7 +167,7 @@ fn format_align_fill() { "{3:0<}", &[NextArgument(Argument { position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, + position_span: InnerSpan::new(2, 3), format: FormatSpec { fill: Some('0'), align: AlignLeft, @@ -164,7 +185,7 @@ fn format_align_fill() { "{3:* fmt::Display for Box { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Box { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) + if f.debug_variant() == fmt::DebugVariant::Pointer { + fmt::Pointer::fmt(self, f) + } else { + fmt::Debug::fmt(&**self, f) + } } } diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index ed398b56612ce..04c094858ebca 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -319,7 +319,7 @@ //! sign := '+' | '-' //! width := count //! precision := count | '*' -//! type := '' | '?' | 'x?' | 'X?' | identifier +//! type := '' | '?' | identifier | identifier '?' //! count := parameter | integer //! parameter := argument '$' //! ``` @@ -338,17 +338,19 @@ //! well as [`isize`]). The current mapping of types to traits is: //! //! * *nothing* ⇒ [`Display`] -//! * `?` ⇒ [`Debug`] -//! * `x?` ⇒ [`Debug`] with lower-case hexadecimal integers -//! * `X?` ⇒ [`Debug`] with upper-case hexadecimal integers -//! * `o` ⇒ [`Octal`] //! * `x` ⇒ [`LowerHex`] //! * `X` ⇒ [`UpperHex`] -//! * `p` ⇒ [`Pointer`] +//! * `o` ⇒ [`Octal`] //! * `b` ⇒ [`Binary`] +//! * `p` ⇒ [`Pointer`] //! * `e` ⇒ [`LowerExp`] //! * `E` ⇒ [`UpperExp`] //! +//! Additionally, any formatting kind can add `?` to be converted into [`Debug`] formatting, +//! which gives a hint to use a particular kind of formatting if possible. This means that `?` +//! by itself will fall back to the default debug-formatting, but `x?` will hint to use +//! `LowerHex`-style formatting if possible. +//! //! What this means is that any type of argument which implements the //! [`fmt::Binary`][`Binary`] trait can then be formatted with `{:b}`. Implementations //! are provided for these traits for a number of primitive types by the diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7fde8f670a231..a616e3f981fb8 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -113,6 +113,7 @@ #![feature(const_pin)] #![feature(const_waker)] #![feature(cstr_from_bytes_until_nul)] +#![feature(debug_variants)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] #![feature(error_in_core)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 6d247681c6661..191bb46bb1b18 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1843,7 +1843,11 @@ impl fmt::Display for Rc { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Rc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) + if f.debug_variant() == fmt::DebugVariant::Pointer { + fmt::Pointer::fmt(self, f) + } else { + fmt::Debug::fmt(&**self, f) + } } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a5322953d4921..c8b5c14ed2b86 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -2421,7 +2421,11 @@ impl fmt::Display for Arc { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Arc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) + if f.debug_variant() == fmt::DebugVariant::Pointer { + fmt::Pointer::fmt(self, f) + } else { + fmt::Debug::fmt(&**self, f) + } } } diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 89d5fac30d357..62ba14517e185 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -1,4 +1,4 @@ -use crate::fmt::{Debug, Display, Formatter, LowerExp, Result, UpperExp}; +use crate::fmt::{Debug, DebugVariant, Display, Formatter, LowerExp, Result, UpperExp}; use crate::mem::MaybeUninit; use crate::num::flt2dec; use crate::num::fmt as numfmt; @@ -195,7 +195,11 @@ macro_rules! floating { #[stable(feature = "rust1", since = "1.0.0")] impl Debug for $ty { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_general_debug(fmt, self) + match fmt.debug_variant() { + DebugVariant::LowerExp => LowerExp::fmt(self, fmt), + DebugVariant::UpperExp => UpperExp::fmt(self, fmt), + _ => float_to_general_debug(fmt, self), + } } } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 372439f14ec83..7c2b8772a1cb2 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -376,12 +376,46 @@ impl<'a> ArgumentV1<'a> { // flags available in the v1 format of format_args #[derive(Copy, Clone)] enum FlagV1 { - SignPlus, - SignMinus, - Alternate, - SignAwareZeroPad, - DebugLowerHex, - DebugUpperHex, + // boolean flags + SignPlus = 0b0000_0001, + SignMinus = 0b0000_0010, + Alternate = 0b0000_0100, + SignAwareZeroPad = 0b0000_1000, + + // bitfields + DebugVariant = 0b1111_0000_0000, +} + +/// Variant for a debug format. +/// +/// This exposes the type given alongside debug formatting. +#[unstable(feature = "debug_variants", issue = "none")] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub enum DebugVariant { + /// [`Display`]-like formatting. (`{:?}`) + #[default] + Display = 0b0000_0000_0000, + + /// [`LowerHex`]-like formatting. (`{:x?}`) + LowerHex = 0b0001_0000_0000, + + /// [`UpperHex`]-like formatting. (`{:X?}`) + UpperHex = 0b0010_0000_0000, + + /// [`Octal`]-like formatting. (`{:o?}`) + Octal = 0b0100_0000_0000, + + /// [`Binary`]-like formatting. (`{:b?}`) + Binary = 0b1000_0000_0000, + + /// [`LowerExp`]-like formatting. (`{:e?}`) + LowerExp = 0b0011_0000_0000, + + /// [`UpperExp`]-like formatting. (`{:E?}`) + UpperExp = 0b0110_0000_0000, + + /// [`Pointer`]-like formatting. (`{:p?}`) + Pointer = 0b1100_0000_0000, } impl<'a> Arguments<'a> { @@ -1840,7 +1874,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_plus(&self) -> bool { - self.flags & (1 << FlagV1::SignPlus as u32) != 0 + self.flags & FlagV1::SignPlus as u32 != 0 } /// Determines if the `-` flag was specified. @@ -1869,7 +1903,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_minus(&self) -> bool { - self.flags & (1 << FlagV1::SignMinus as u32) != 0 + self.flags & FlagV1::SignMinus as u32 != 0 } /// Determines if the `#` flag was specified. @@ -1897,7 +1931,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn alternate(&self) -> bool { - self.flags & (1 << FlagV1::Alternate as u32) != 0 + self.flags & FlagV1::Alternate as u32 != 0 } /// Determines if the `0` flag was specified. @@ -1923,17 +1957,31 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_aware_zero_pad(&self) -> bool { - self.flags & (1 << FlagV1::SignAwareZeroPad as u32) != 0 - } - - // FIXME: Decide what public API we want for these two flags. - // /~https://github.com/rust-lang/rust/issues/48584 - fn debug_lower_hex(&self) -> bool { - self.flags & (1 << FlagV1::DebugLowerHex as u32) != 0 - } - - fn debug_upper_hex(&self) -> bool { - self.flags & (1 << FlagV1::DebugUpperHex as u32) != 0 + self.flags & FlagV1::SignAwareZeroPad as u32 != 0 + } + + /// Determines the variant for a [`Debug`] format. + #[unstable(feature = "debug_variants", issue = "none")] + pub fn debug_variant(&self) -> DebugVariant { + // FIXME: use const patterns + const LOWER_HEX: u32 = DebugVariant::LowerHex as u32; + const UPPER_HEX: u32 = DebugVariant::UpperHex as u32; + const OCTAL: u32 = DebugVariant::Octal as u32; + const BINARY: u32 = DebugVariant::Binary as u32; + const LOWER_EXP: u32 = DebugVariant::LowerExp as u32; + const UPPER_EXP: u32 = DebugVariant::UpperExp as u32; + const POINTER: u32 = DebugVariant::Pointer as u32; + + match self.flags & FlagV1::DebugVariant as u32 { + LOWER_HEX => DebugVariant::LowerHex, + UPPER_HEX => DebugVariant::UpperHex, + OCTAL => DebugVariant::Octal, + BINARY => DebugVariant::Binary, + LOWER_EXP => DebugVariant::LowerExp, + UPPER_EXP => DebugVariant::UpperExp, + POINTER => DebugVariant::Pointer, + _ => DebugVariant::Display, + } } /// Creates a [`DebugStruct`] builder designed to assist with creation of @@ -2380,7 +2428,7 @@ macro_rules! fmt_refs { } } -fmt_refs! { Debug, Display, Octal, Binary, LowerHex, UpperHex, LowerExp, UpperExp } +fmt_refs! { Display, Octal, Binary, LowerHex, UpperHex, LowerExp, UpperExp } #[unstable(feature = "never_type", issue = "35121")] impl Debug for ! { @@ -2493,13 +2541,13 @@ pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Resul // or not to zero extend, and then unconditionally set it to get the // prefix. if f.alternate() { - f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32); + f.flags |= FlagV1::SignAwareZeroPad as u32; if f.width.is_none() { f.width = Some((usize::BITS / 4) as usize + 2); } } - f.flags |= 1 << (FlagV1::Alternate as u32); + f.flags |= FlagV1::Alternate as u32; let ret = LowerHex::fmt(&ptr_addr, f); @@ -2545,6 +2593,27 @@ impl Debug for *mut T { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + if f.debug_variant() == DebugVariant::Pointer { + Pointer::fmt(self, f) + } else { + Debug::fmt(&**self, f) + } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for &mut T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + if f.debug_variant() == DebugVariant::Pointer { + Pointer::fmt(self, f) + } else { + Debug::fmt(&**self, f) + } + } +} + macro_rules! peel { ($name:ident, $($other:ident,)*) => (tuple! { $($other,)* }) } diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index d8365ae9bf920..e4346a99e31c9 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -183,12 +183,14 @@ macro_rules! debug { impl fmt::Debug for $T { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.debug_lower_hex() { - fmt::LowerHex::fmt(self, f) - } else if f.debug_upper_hex() { - fmt::UpperHex::fmt(self, f) - } else { - fmt::Display::fmt(self, f) + match f.debug_variant() { + fmt::DebugVariant::LowerHex => fmt::LowerHex::fmt(self, f), + fmt::DebugVariant::UpperHex => fmt::UpperHex::fmt(self, f), + fmt::DebugVariant::Octal => fmt::Octal::fmt(self, f), + fmt::DebugVariant::Binary => fmt::Binary::fmt(self, f), + fmt::DebugVariant::LowerExp => fmt::LowerExp::fmt(self, f), + fmt::DebugVariant::UpperExp => fmt::UpperExp::fmt(self, f), + _ => fmt::Display::fmt(self, f), } } } diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 3c96290fc537e..d1b915378fce2 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -3405,7 +3405,11 @@ impl fmt::Debug for AtomicBool { #[stable(feature = "atomic_debug", since = "1.3.0")] impl fmt::Debug for AtomicPtr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.load(Ordering::Relaxed), f) + if f.debug_variant() == fmt::DebugVariant::Pointer { + fmt::Pointer::fmt(self, f) + } else { + fmt::Debug::fmt(&self.load(Ordering::Relaxed), f) + } } } diff --git a/library/core/tests/fmt/mod.rs b/library/core/tests/fmt/mod.rs index 61807635813c4..ad3bc14fe30a6 100644 --- a/library/core/tests/fmt/mod.rs +++ b/library/core/tests/fmt/mod.rs @@ -19,6 +19,14 @@ fn test_pointer_formats_data_pointer() { assert_eq!(format!("{b:p}"), format!("{:p}", b.as_ptr())); } +#[cfg(not(bootstrap))] +#[test] +fn test_pointer_debug() { + let data = &1; + assert_eq!(format!("{:?}", data), "1"); + assert_eq!(format!("{data:p?}"), format!("{data:p}")); +} + #[test] fn test_estimated_capacity() { assert_eq!(format_args!("").estimated_capacity(), 0); diff --git a/library/core/tests/fmt/num.rs b/library/core/tests/fmt/num.rs index b9ede65c9ff09..e136c5dde6b51 100644 --- a/library/core/tests/fmt/num.rs +++ b/library/core/tests/fmt/num.rs @@ -218,8 +218,21 @@ fn test_format_int_twos_complement() { assert_eq!(format!("{}", i64::MIN), "-9223372036854775808"); } +#[cfg(not(bootstrap))] #[test] -fn test_format_debug_hex() { +fn test_format_debug_bases() { assert_eq!(format!("{:02x?}", b"Foo\0"), "[46, 6f, 6f, 00]"); assert_eq!(format!("{:02X?}", b"Foo\0"), "[46, 6F, 6F, 00]"); + #[cfg(not(bootstrap))] + { + assert_eq!(format!("{:03o?}", b"Foo\0"), "[106, 157, 157, 000]"); + assert_eq!(format!("{:07b?}", b"Foo\0"), "[1000110, 1101111, 1101111, 0000000]"); + } +} + +#[cfg(not(bootstrap))] +#[test] +fn test_format_debug_exp() { + assert_eq!(format!("{:e?}", &[1, 20, 300]), "[1e0, 2e1, 3e2]"); + assert_eq!(format!("{:E?}", &[1, 20, 300]), "[1E0, 2E1, 3E2]"); }