1use crate::ffi::CStr;
2use crate::fmt;
3
4/// A struct containing information about the location of a panic.
5///
6/// This structure is created by [`PanicHookInfo::location()`] and [`PanicInfo::location()`].
7///
8/// [`PanicInfo::location()`]: crate::panic::PanicInfo::location
9/// [`PanicHookInfo::location()`]: ../../std/panic/struct.PanicHookInfo.html#method.location
10///
11/// # Examples
12///
13/// ```should_panic
14/// use std::panic;
15///
16/// panic::set_hook(Box::new(|panic_info| {
17/// if let Some(location) = panic_info.location() {
18/// println!("panic occurred in file '{}' at line {}", location.file(), location.line());
19/// } else {
20/// println!("panic occurred but can't get location information...");
21/// }
22/// }));
23///
24/// panic!("Normal panic");
25/// ```
26///
27/// # Comparisons
28///
29/// Comparisons for equality and ordering are made in file, line, then column priority.
30/// Files are compared as strings, not `Path`, which could be unexpected.
31/// See [`Location::file`]'s documentation for more discussion.
32#[lang = "panic_location"]
33#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
34#[stable(feature = "panic_hooks", since = "1.10.0")]
35pub struct Location<'a> {
36 // Note: this filename will have exactly one nul byte at its end, but otherwise
37 // it must never contain interior nul bytes. This is relied on for the conversion
38 // to `CStr` below.
39 //
40 // The prefix of the string without the trailing nul byte will be a regular UTF8 `str`.
41 file_bytes_with_nul: &'a [u8],
42 line: u32,
43 col: u32,
44}
45
46impl<'a> Location<'a> {
47 /// Returns the source location of the caller of this function. If that function's caller is
48 /// annotated then its call location will be returned, and so on up the stack to the first call
49 /// within a non-tracked function body.
50 ///
51 /// # Examples
52 ///
53 /// ```standalone_crate
54 /// use std::panic::Location;
55 ///
56 /// /// Returns the [`Location`] at which it is called.
57 /// #[track_caller]
58 /// fn get_caller_location() -> &'static Location<'static> {
59 /// Location::caller()
60 /// }
61 ///
62 /// /// Returns a [`Location`] from within this function's definition.
63 /// fn get_just_one_location() -> &'static Location<'static> {
64 /// get_caller_location()
65 /// }
66 ///
67 /// let fixed_location = get_just_one_location();
68 /// assert_eq!(fixed_location.file(), file!());
69 /// assert_eq!(fixed_location.line(), 14);
70 /// assert_eq!(fixed_location.column(), 5);
71 ///
72 /// // running the same untracked function in a different location gives us the same result
73 /// let second_fixed_location = get_just_one_location();
74 /// assert_eq!(fixed_location.file(), second_fixed_location.file());
75 /// assert_eq!(fixed_location.line(), second_fixed_location.line());
76 /// assert_eq!(fixed_location.column(), second_fixed_location.column());
77 ///
78 /// let this_location = get_caller_location();
79 /// assert_eq!(this_location.file(), file!());
80 /// assert_eq!(this_location.line(), 28);
81 /// assert_eq!(this_location.column(), 21);
82 ///
83 /// // running the tracked function in a different location produces a different value
84 /// let another_location = get_caller_location();
85 /// assert_eq!(this_location.file(), another_location.file());
86 /// assert_ne!(this_location.line(), another_location.line());
87 /// assert_ne!(this_location.column(), another_location.column());
88 /// ```
89 #[must_use]
90 #[stable(feature = "track_caller", since = "1.46.0")]
91 #[rustc_const_stable(feature = "const_caller_location", since = "1.79.0")]
92 #[track_caller]
93 #[inline]
94 pub const fn caller() -> &'static Location<'static> {
95 crate::intrinsics::caller_location()
96 }
97
98 /// Returns the name of the source file from which the panic originated.
99 ///
100 /// # `&str`, not `&Path`
101 ///
102 /// The returned name refers to a source path on the compiling system, but it isn't valid to
103 /// represent this directly as a `&Path`. The compiled code may run on a different system with
104 /// a different `Path` implementation than the system providing the contents and this library
105 /// does not currently have a different "host path" type.
106 ///
107 /// The most surprising behavior occurs when "the same" file is reachable via multiple paths in
108 /// the module system (usually using the `#[path = "..."]` attribute or similar), which can
109 /// cause what appears to be identical code to return differing values from this function.
110 ///
111 /// # Cross-compilation
112 ///
113 /// This value is not suitable for passing to `Path::new` or similar constructors when the host
114 /// platform and target platform differ.
115 ///
116 /// # Examples
117 ///
118 /// ```should_panic
119 /// use std::panic;
120 ///
121 /// panic::set_hook(Box::new(|panic_info| {
122 /// if let Some(location) = panic_info.location() {
123 /// println!("panic occurred in file '{}'", location.file());
124 /// } else {
125 /// println!("panic occurred but can't get location information...");
126 /// }
127 /// }));
128 ///
129 /// panic!("Normal panic");
130 /// ```
131 #[must_use]
132 #[stable(feature = "panic_hooks", since = "1.10.0")]
133 #[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")]
134 pub const fn file(&self) -> &str {
135 let str_len = self.file_bytes_with_nul.len() - 1;
136 // SAFETY: `file_bytes_with_nul` without the trailing nul byte is guaranteed to be
137 // valid UTF8.
138 unsafe { crate::str::from_raw_parts(self.file_bytes_with_nul.as_ptr(), str_len) }
139 }
140
141 /// Returns the name of the source file as a nul-terminated `CStr`.
142 ///
143 /// This is useful for interop with APIs that expect C/C++ `__FILE__` or
144 /// `std::source_location::file_name`, both of which return a nul-terminated `const char*`.
145 #[must_use]
146 #[unstable(feature = "file_with_nul", issue = "141727")]
147 #[inline]
148 pub const fn file_with_nul(&self) -> &CStr {
149 // SAFETY: `file_bytes_with_nul` is guaranteed to have a trailing nul byte and no
150 // interior nul bytes.
151 unsafe { CStr::from_bytes_with_nul_unchecked(self.file_bytes_with_nul) }
152 }
153
154 /// Returns the line number from which the panic originated.
155 ///
156 /// # Examples
157 ///
158 /// ```should_panic
159 /// use std::panic;
160 ///
161 /// panic::set_hook(Box::new(|panic_info| {
162 /// if let Some(location) = panic_info.location() {
163 /// println!("panic occurred at line {}", location.line());
164 /// } else {
165 /// println!("panic occurred but can't get location information...");
166 /// }
167 /// }));
168 ///
169 /// panic!("Normal panic");
170 /// ```
171 #[must_use]
172 #[stable(feature = "panic_hooks", since = "1.10.0")]
173 #[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")]
174 #[inline]
175 pub const fn line(&self) -> u32 {
176 self.line
177 }
178
179 /// Returns the column from which the panic originated.
180 ///
181 /// # Examples
182 ///
183 /// ```should_panic
184 /// use std::panic;
185 ///
186 /// panic::set_hook(Box::new(|panic_info| {
187 /// if let Some(location) = panic_info.location() {
188 /// println!("panic occurred at column {}", location.column());
189 /// } else {
190 /// println!("panic occurred but can't get location information...");
191 /// }
192 /// }));
193 ///
194 /// panic!("Normal panic");
195 /// ```
196 #[must_use]
197 #[stable(feature = "panic_col", since = "1.25.0")]
198 #[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")]
199 #[inline]
200 pub const fn column(&self) -> u32 {
201 self.col
202 }
203}
204
205#[stable(feature = "panic_hook_display", since = "1.26.0")]
206impl fmt::Display for Location<'_> {
207 #[inline]
208 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
209 write!(formatter, "{}:{}:{}", self.file(), self.line, self.col)
210 }
211}
212

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more