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