Skip to content

Commit

Permalink
Add PanicMessage type for PanicInfo::message().
Browse files Browse the repository at this point in the history
  • Loading branch information
m-ou-se committed Jun 17, 2024
1 parent 8cd20cb commit ca458ba
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 11 deletions.
2 changes: 2 additions & 0 deletions core/src/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use crate::any::Any;
pub use self::location::Location;
#[stable(feature = "panic_hooks", since = "1.10.0")]
pub use self::panic_info::PanicInfo;
#[unstable(feature = "panic_info_message", issue = "66745")]
pub use self::panic_info::PanicMessage;
#[stable(feature = "catch_unwind", since = "1.9.0")]
pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};

Expand Down
75 changes: 69 additions & 6 deletions core/src/panic/panic_info.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::fmt;
use crate::fmt::{self, Display};
use crate::panic::Location;

/// A struct providing information about a panic.
Expand All @@ -18,6 +18,17 @@ pub struct PanicInfo<'a> {
force_no_backtrace: bool,
}

/// A message that was given to the `panic!()` macro.
///
/// The [`Display`] implementation of this type will format the message with the arguments
/// that were given to the `panic!()` macro.
///
/// See [`PanicInfo::message`].
#[unstable(feature = "panic_info_message", issue = "66745")]
pub struct PanicMessage<'a> {
message: fmt::Arguments<'a>,
}

impl<'a> PanicInfo<'a> {
#[inline]
pub(crate) fn new(
Expand All @@ -29,12 +40,26 @@ impl<'a> PanicInfo<'a> {
PanicInfo { location, message, can_unwind, force_no_backtrace }
}

/// The message that was given to the `panic!` macro,
/// ready to be formatted with e.g. [`fmt::write`].
/// The message that was given to the `panic!` macro.
///
/// # Example
///
/// The type returned by this method implements `Display`, so it can
/// be passed directly to [`write!()`] and similar macros.
///
/// [`write!()`]: core::write
///
/// ```ignore (no_std)
/// #[panic_handler]
/// fn panic_handler(panic_info: &PanicInfo<'_>) -> ! {
/// write!(DEBUG_OUTPUT, "panicked: {}", panic_info.message());
/// loop {}
/// }
/// ```
#[must_use]
#[unstable(feature = "panic_info_message", issue = "66745")]
pub fn message(&self) -> fmt::Arguments<'_> {
self.message
pub fn message(&self) -> PanicMessage<'_> {
PanicMessage { message: self.message }
}

/// Returns information about the location from which the panic originated,
Expand Down Expand Up @@ -116,7 +141,7 @@ impl<'a> PanicInfo<'a> {
}

#[stable(feature = "panic_hook_display", since = "1.26.0")]
impl fmt::Display for PanicInfo<'_> {
impl Display for PanicInfo<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("panicked at ")?;
self.location.fmt(formatter)?;
Expand All @@ -125,3 +150,41 @@ impl fmt::Display for PanicInfo<'_> {
Ok(())
}
}

impl<'a> PanicMessage<'a> {
/// Get the formatted message, if it has no arguments to be formatted at runtime.
///
/// This can be used to avoid allocations in some cases.
///
/// # Guarantees
///
/// For `panic!("just a literal")`, this function is guaranteed to
/// return `Some("just a literal")`.
///
/// For most cases with placeholders, this function will return `None`.
///
/// See [`fmt::Arguments::as_str`] for details.
#[unstable(feature = "panic_info_message", issue = "66745")]
#[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")]
#[must_use]
#[inline]
pub const fn as_str(&self) -> Option<&'static str> {
self.message.as_str()
}
}

#[unstable(feature = "panic_info_message", issue = "66745")]
impl Display for PanicMessage<'_> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_fmt(self.message)
}
}

#[unstable(feature = "panic_info_message", issue = "66745")]
impl fmt::Debug for PanicMessage<'_> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_fmt(self.message)
}
}
13 changes: 8 additions & 5 deletions std/src/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,19 +593,18 @@ pub fn panicking() -> bool {
#[panic_handler]
pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
struct FormatStringPayload<'a> {
inner: &'a fmt::Arguments<'a>,
inner: &'a core::panic::PanicMessage<'a>,
string: Option<String>,
}

impl FormatStringPayload<'_> {
fn fill(&mut self) -> &mut String {
use crate::fmt::Write;

let inner = self.inner;
// Lazily, the first time this gets called, run the actual string formatting.
self.string.get_or_insert_with(|| {
let mut s = String::new();
let _err = s.write_fmt(*inner);
let mut fmt = fmt::Formatter::new(&mut s);
let _err = fmt::Display::fmt(&inner, &mut fmt);
s
})
}
Expand All @@ -627,7 +626,11 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {

impl fmt::Display for FormatStringPayload<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(s) = &self.string { f.write_str(s) } else { f.write_fmt(*self.inner) }
if let Some(s) = &self.string {
f.write_str(s)
} else {
fmt::Display::fmt(&self.inner, f)
}
}
}

Expand Down

0 comments on commit ca458ba

Please sign in to comment.