]> git.lizzy.rs Git - rust.git/blob - library/core/src/panic/panic_info.rs
Rollup merge of #106897 - estebank:issue-99430, r=davidtwco
[rust.git] / library / core / src / panic / panic_info.rs
1 use crate::any::Any;
2 use crate::fmt;
3 use crate::panic::Location;
4
5 /// A struct providing information about a panic.
6 ///
7 /// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
8 /// function.
9 ///
10 /// [`set_hook`]: ../../std/panic/fn.set_hook.html
11 ///
12 /// # Examples
13 ///
14 /// ```should_panic
15 /// use std::panic;
16 ///
17 /// panic::set_hook(Box::new(|panic_info| {
18 ///     if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
19 ///         println!("panic occurred: {s:?}");
20 ///     } else {
21 ///         println!("panic occurred");
22 ///     }
23 /// }));
24 ///
25 /// panic!("Normal panic");
26 /// ```
27 #[lang = "panic_info"]
28 #[stable(feature = "panic_hooks", since = "1.10.0")]
29 #[derive(Debug)]
30 pub struct PanicInfo<'a> {
31     payload: &'a (dyn Any + Send),
32     message: Option<&'a fmt::Arguments<'a>>,
33     location: &'a Location<'a>,
34     can_unwind: bool,
35 }
36
37 impl<'a> PanicInfo<'a> {
38     #[unstable(
39         feature = "panic_internals",
40         reason = "internal details of the implementation of the `panic!` and related macros",
41         issue = "none"
42     )]
43     #[doc(hidden)]
44     #[inline]
45     pub fn internal_constructor(
46         message: Option<&'a fmt::Arguments<'a>>,
47         location: &'a Location<'a>,
48         can_unwind: bool,
49     ) -> Self {
50         struct NoPayload;
51         PanicInfo { location, message, payload: &NoPayload, can_unwind }
52     }
53
54     #[unstable(
55         feature = "panic_internals",
56         reason = "internal details of the implementation of the `panic!` and related macros",
57         issue = "none"
58     )]
59     #[doc(hidden)]
60     #[inline]
61     pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) {
62         self.payload = info;
63     }
64
65     /// Returns the payload associated with the panic.
66     ///
67     /// This will commonly, but not always, be a `&'static str` or [`String`].
68     ///
69     /// [`String`]: ../../std/string/struct.String.html
70     ///
71     /// # Examples
72     ///
73     /// ```should_panic
74     /// use std::panic;
75     ///
76     /// panic::set_hook(Box::new(|panic_info| {
77     ///     if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
78     ///         println!("panic occurred: {s:?}");
79     ///     } else {
80     ///         println!("panic occurred");
81     ///     }
82     /// }));
83     ///
84     /// panic!("Normal panic");
85     /// ```
86     #[must_use]
87     #[stable(feature = "panic_hooks", since = "1.10.0")]
88     pub fn payload(&self) -> &(dyn Any + Send) {
89         self.payload
90     }
91
92     /// If the `panic!` macro from the `core` crate (not from `std`)
93     /// was used with a formatting string and some additional arguments,
94     /// returns that message ready to be used for example with [`fmt::write`]
95     #[must_use]
96     #[unstable(feature = "panic_info_message", issue = "66745")]
97     pub fn message(&self) -> Option<&fmt::Arguments<'_>> {
98         self.message
99     }
100
101     /// Returns information about the location from which the panic originated,
102     /// if available.
103     ///
104     /// This method will currently always return [`Some`], but this may change
105     /// in future versions.
106     ///
107     /// # Examples
108     ///
109     /// ```should_panic
110     /// use std::panic;
111     ///
112     /// panic::set_hook(Box::new(|panic_info| {
113     ///     if let Some(location) = panic_info.location() {
114     ///         println!("panic occurred in file '{}' at line {}",
115     ///             location.file(),
116     ///             location.line(),
117     ///         );
118     ///     } else {
119     ///         println!("panic occurred but can't get location information...");
120     ///     }
121     /// }));
122     ///
123     /// panic!("Normal panic");
124     /// ```
125     #[must_use]
126     #[stable(feature = "panic_hooks", since = "1.10.0")]
127     pub fn location(&self) -> Option<&Location<'_>> {
128         // NOTE: If this is changed to sometimes return None,
129         // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt.
130         Some(&self.location)
131     }
132
133     /// Returns whether the panic handler is allowed to unwind the stack from
134     /// the point where the panic occurred.
135     ///
136     /// This is true for most kinds of panics with the exception of panics
137     /// caused by trying to unwind out of a `Drop` implementation or a function
138     /// whose ABI does not support unwinding.
139     ///
140     /// It is safe for a panic handler to unwind even when this function returns
141     /// true, however this will simply cause the panic handler to be called
142     /// again.
143     #[must_use]
144     #[unstable(feature = "panic_can_unwind", issue = "92988")]
145     pub fn can_unwind(&self) -> bool {
146         self.can_unwind
147     }
148 }
149
150 #[stable(feature = "panic_hook_display", since = "1.26.0")]
151 impl fmt::Display for PanicInfo<'_> {
152     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
153         formatter.write_str("panicked at ")?;
154         if let Some(message) = self.message {
155             write!(formatter, "'{}', ", message)?
156         } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() {
157             write!(formatter, "'{}', ", payload)?
158         }
159         // NOTE: we cannot use downcast_ref::<String>() here
160         // since String is not available in core!
161         // The payload is a String when `std::panic!` is called with multiple arguments,
162         // but in that case the message is also available.
163
164         self.location.fmt(formatter)
165     }
166 }