core/panic/
location.rs

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