1 #![allow(
2 clippy::needless_lifetimes,
3 clippy::needless_raw_string_hashes,
4 clippy::trivially_copy_pass_by_ref,
5 clippy::uninlined_format_args
6 )]
7
8 use core::fmt::{self, Display};
9 use thiserror::Error;
10
assert<T: Display>(expected: &str, value: T)11 fn assert<T: Display>(expected: &str, value: T) {
12 assert_eq!(expected, value.to_string());
13 }
14
15 #[test]
test_braced()16 fn test_braced() {
17 #[derive(Error, Debug)]
18 #[error("braced error: {msg}")]
19 struct Error {
20 msg: String,
21 }
22
23 let msg = "T".to_owned();
24 assert("braced error: T", Error { msg });
25 }
26
27 #[test]
test_braced_unused()28 fn test_braced_unused() {
29 #[derive(Error, Debug)]
30 #[error("braced error")]
31 struct Error {
32 extra: usize,
33 }
34
35 assert("braced error", Error { extra: 0 });
36 }
37
38 #[test]
test_tuple()39 fn test_tuple() {
40 #[derive(Error, Debug)]
41 #[error("tuple error: {0}")]
42 struct Error(usize);
43
44 assert("tuple error: 0", Error(0));
45 }
46
47 #[test]
test_unit()48 fn test_unit() {
49 #[derive(Error, Debug)]
50 #[error("unit error")]
51 struct Error;
52
53 assert("unit error", Error);
54 }
55
56 #[test]
test_enum()57 fn test_enum() {
58 #[derive(Error, Debug)]
59 enum Error {
60 #[error("braced error: {id}")]
61 Braced { id: usize },
62 #[error("tuple error: {0}")]
63 Tuple(usize),
64 #[error("unit error")]
65 Unit,
66 }
67
68 assert("braced error: 0", Error::Braced { id: 0 });
69 assert("tuple error: 0", Error::Tuple(0));
70 assert("unit error", Error::Unit);
71 }
72
73 #[test]
test_constants()74 fn test_constants() {
75 #[derive(Error, Debug)]
76 #[error("{MSG}: {id:?} (code {CODE:?})")]
77 struct Error {
78 id: &'static str,
79 }
80
81 const MSG: &str = "failed to do";
82 const CODE: usize = 9;
83
84 assert("failed to do: \"\" (code 9)", Error { id: "" });
85 }
86
87 #[test]
test_inherit()88 fn test_inherit() {
89 #[derive(Error, Debug)]
90 #[error("{0}")]
91 enum Error {
92 Some(&'static str),
93 #[error("other error")]
94 Other(&'static str),
95 }
96
97 assert("some error", Error::Some("some error"));
98 assert("other error", Error::Other("..."));
99 }
100
101 #[test]
test_brace_escape()102 fn test_brace_escape() {
103 #[derive(Error, Debug)]
104 #[error("fn main() {{}}")]
105 struct Error;
106
107 assert("fn main() {}", Error);
108 }
109
110 #[test]
test_expr()111 fn test_expr() {
112 #[derive(Error, Debug)]
113 #[error("1 + 1 = {}", 1 + 1)]
114 struct Error;
115 assert("1 + 1 = 2", Error);
116 }
117
118 #[test]
test_nested()119 fn test_nested() {
120 #[derive(Error, Debug)]
121 #[error("!bool = {}", not(.0))]
122 struct Error(bool);
123
124 #[allow(clippy::trivially_copy_pass_by_ref)]
125 fn not(bool: &bool) -> bool {
126 !*bool
127 }
128
129 assert("!bool = false", Error(true));
130 }
131
132 #[test]
test_match()133 fn test_match() {
134 #[derive(Error, Debug)]
135 #[error("{intro}: {0}", intro = match .1 {
136 Some(n) => format!("error occurred with {}", n),
137 None => "there was an empty error".to_owned(),
138 })]
139 struct Error(String, Option<usize>);
140
141 assert(
142 "error occurred with 1: ...",
143 Error("...".to_owned(), Some(1)),
144 );
145 assert(
146 "there was an empty error: ...",
147 Error("...".to_owned(), None),
148 );
149 }
150
151 #[test]
test_nested_display()152 fn test_nested_display() {
153 // Same behavior as the one in `test_match`, but without String allocations.
154 #[derive(Error, Debug)]
155 #[error("{}", {
156 struct Msg<'a>(&'a String, &'a Option<usize>);
157 impl<'a> Display for Msg<'a> {
158 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
159 match self.1 {
160 Some(n) => write!(formatter, "error occurred with {}", n),
161 None => write!(formatter, "there was an empty error"),
162 }?;
163 write!(formatter, ": {}", self.0)
164 }
165 }
166 Msg(.0, .1)
167 })]
168 struct Error(String, Option<usize>);
169
170 assert(
171 "error occurred with 1: ...",
172 Error("...".to_owned(), Some(1)),
173 );
174 assert(
175 "there was an empty error: ...",
176 Error("...".to_owned(), None),
177 );
178 }
179
180 #[test]
test_void()181 fn test_void() {
182 #[allow(clippy::empty_enum)]
183 #[derive(Error, Debug)]
184 #[error("...")]
185 pub enum Error {}
186
187 let _: Error;
188 }
189
190 #[test]
test_mixed()191 fn test_mixed() {
192 #[derive(Error, Debug)]
193 #[error("a={a} :: b={} :: c={c} :: d={d}", 1, c = 2, d = 3)]
194 struct Error {
195 a: usize,
196 d: usize,
197 }
198
199 assert("a=0 :: b=1 :: c=2 :: d=3", Error { a: 0, d: 0 });
200 }
201
202 #[test]
test_ints()203 fn test_ints() {
204 #[derive(Error, Debug)]
205 enum Error {
206 #[error("error {0}")]
207 Tuple(usize, usize),
208 #[error("error {0}", '?')]
209 Struct { v: usize },
210 }
211
212 assert("error 9", Error::Tuple(9, 0));
213 assert("error ?", Error::Struct { v: 0 });
214 }
215
216 #[test]
test_trailing_comma()217 fn test_trailing_comma() {
218 #[derive(Error, Debug)]
219 #[error(
220 "error {0}",
221 )]
222 #[rustfmt::skip]
223 struct Error(char);
224
225 assert("error ?", Error('?'));
226 }
227
228 #[test]
test_field()229 fn test_field() {
230 #[derive(Debug)]
231 struct Inner {
232 data: usize,
233 }
234
235 #[derive(Error, Debug)]
236 #[error("{}", .0.data)]
237 struct Error(Inner);
238
239 assert("0", Error(Inner { data: 0 }));
240 }
241
242 #[test]
test_nested_tuple_field()243 fn test_nested_tuple_field() {
244 #[derive(Debug)]
245 struct Inner(usize);
246
247 #[derive(Error, Debug)]
248 #[error("{}", .0.0)]
249 struct Error(Inner);
250
251 assert("0", Error(Inner(0)));
252 }
253
254 #[test]
test_pointer()255 fn test_pointer() {
256 #[derive(Error, Debug)]
257 #[error("{field:p}")]
258 pub struct Struct {
259 field: Box<i32>,
260 }
261
262 let s = Struct {
263 field: Box::new(-1),
264 };
265 assert_eq!(s.to_string(), format!("{:p}", s.field));
266 }
267
268 #[test]
test_macro_rules_variant_from_call_site()269 fn test_macro_rules_variant_from_call_site() {
270 // Regression test for https://github.com/dtolnay/thiserror/issues/86
271
272 macro_rules! decl_error {
273 ($variant:ident($value:ident)) => {
274 #[derive(Error, Debug)]
275 pub enum Error0 {
276 #[error("{0:?}")]
277 $variant($value),
278 }
279
280 #[derive(Error, Debug)]
281 #[error("{0:?}")]
282 pub enum Error1 {
283 $variant($value),
284 }
285 };
286 }
287
288 decl_error!(Repro(u8));
289
290 assert("0", Error0::Repro(0));
291 assert("0", Error1::Repro(0));
292 }
293
294 #[test]
test_macro_rules_message_from_call_site()295 fn test_macro_rules_message_from_call_site() {
296 // Regression test for https://github.com/dtolnay/thiserror/issues/398
297
298 macro_rules! decl_error {
299 ($($errors:tt)*) => {
300 #[derive(Error, Debug)]
301 pub enum Error {
302 $($errors)*
303 }
304 };
305 }
306
307 decl_error! {
308 #[error("{0}")]
309 Unnamed(u8),
310 #[error("{x}")]
311 Named { x: u8 },
312 }
313
314 assert("0", Error::Unnamed(0));
315 assert("0", Error::Named { x: 0 });
316 }
317
318 #[test]
test_raw()319 fn test_raw() {
320 #[derive(Error, Debug)]
321 #[error("braced raw error: {fn}")]
322 struct Error {
323 r#fn: &'static str,
324 }
325
326 assert("braced raw error: T", Error { r#fn: "T" });
327 }
328
329 #[test]
test_raw_enum()330 fn test_raw_enum() {
331 #[derive(Error, Debug)]
332 enum Error {
333 #[error("braced raw error: {fn}")]
334 Braced { r#fn: &'static str },
335 }
336
337 assert("braced raw error: T", Error::Braced { r#fn: "T" });
338 }
339
340 #[test]
test_keyword()341 fn test_keyword() {
342 #[derive(Error, Debug)]
343 #[error("error: {type}", type = 1)]
344 struct Error;
345
346 assert("error: 1", Error);
347 }
348
349 #[test]
test_self()350 fn test_self() {
351 #[derive(Error, Debug)]
352 #[error("error: {self:?}")]
353 struct Error;
354
355 assert("error: Error", Error);
356 }
357
358 #[test]
test_str_special_chars()359 fn test_str_special_chars() {
360 #[derive(Error, Debug)]
361 pub enum Error {
362 #[error("brace left {{")]
363 BraceLeft,
364 #[error("brace left 2 \x7B\x7B")]
365 BraceLeft2,
366 #[error("brace left 3 \u{7B}\u{7B}")]
367 BraceLeft3,
368 #[error("brace right }}")]
369 BraceRight,
370 #[error("brace right 2 \x7D\x7D")]
371 BraceRight2,
372 #[error("brace right 3 \u{7D}\u{7D}")]
373 BraceRight3,
374 #[error(
375 "new_\
376 line"
377 )]
378 NewLine,
379 #[error("escape24 \u{78}")]
380 Escape24,
381 }
382
383 assert("brace left {", Error::BraceLeft);
384 assert("brace left 2 {", Error::BraceLeft2);
385 assert("brace left 3 {", Error::BraceLeft3);
386 assert("brace right }", Error::BraceRight);
387 assert("brace right 2 }", Error::BraceRight2);
388 assert("brace right 3 }", Error::BraceRight3);
389 assert("new_line", Error::NewLine);
390 assert("escape24 x", Error::Escape24);
391 }
392
393 #[test]
test_raw_str()394 fn test_raw_str() {
395 #[derive(Error, Debug)]
396 pub enum Error {
397 #[error(r#"raw brace left {{"#)]
398 BraceLeft,
399 #[error(r#"raw brace left 2 \x7B"#)]
400 BraceLeft2,
401 #[error(r#"raw brace right }}"#)]
402 BraceRight,
403 #[error(r#"raw brace right 2 \x7D"#)]
404 BraceRight2,
405 }
406
407 assert(r#"raw brace left {"#, Error::BraceLeft);
408 assert(r#"raw brace left 2 \x7B"#, Error::BraceLeft2);
409 assert(r#"raw brace right }"#, Error::BraceRight);
410 assert(r#"raw brace right 2 \x7D"#, Error::BraceRight2);
411 }
412
413 mod util {
414 use core::fmt::{self, Octal};
415
octal<T: Octal>(value: &T, formatter: &mut fmt::Formatter) -> fmt::Result416 pub fn octal<T: Octal>(value: &T, formatter: &mut fmt::Formatter) -> fmt::Result {
417 write!(formatter, "0o{:o}", value)
418 }
419 }
420
421 #[test]
test_fmt_path()422 fn test_fmt_path() {
423 fn unit(formatter: &mut fmt::Formatter) -> fmt::Result {
424 formatter.write_str("unit=")
425 }
426
427 fn pair(k: &i32, v: &i32, formatter: &mut fmt::Formatter) -> fmt::Result {
428 write!(formatter, "pair={k}:{v}")
429 }
430
431 #[derive(Error, Debug)]
432 pub enum Error {
433 #[error(fmt = unit)]
434 Unit,
435 #[error(fmt = pair)]
436 Tuple(i32, i32),
437 #[error(fmt = pair)]
438 Entry { k: i32, v: i32 },
439 #[error(fmt = crate::util::octal)]
440 I16(i16),
441 #[error(fmt = crate::util::octal::<i32>)]
442 I32 { n: i32 },
443 #[error(fmt = core::fmt::Octal::fmt)]
444 I64(i64),
445 #[error("...{0}")]
446 Other(bool),
447 }
448
449 assert("unit=", Error::Unit);
450 assert("pair=10:0", Error::Tuple(10, 0));
451 assert("pair=10:0", Error::Entry { k: 10, v: 0 });
452 assert("0o777", Error::I16(0o777));
453 assert("0o777", Error::I32 { n: 0o777 });
454 assert("777", Error::I64(0o777));
455 assert("...false", Error::Other(false));
456 }
457
458 #[test]
test_fmt_path_inherited()459 fn test_fmt_path_inherited() {
460 #[derive(Error, Debug)]
461 #[error(fmt = crate::util::octal)]
462 pub enum Error {
463 I16(i16),
464 I32 {
465 n: i32,
466 },
467 #[error(fmt = core::fmt::Octal::fmt)]
468 I64(i64),
469 #[error("...{0}")]
470 Other(bool),
471 }
472
473 assert("0o777", Error::I16(0o777));
474 assert("0o777", Error::I32 { n: 0o777 });
475 assert("777", Error::I64(0o777));
476 assert("...false", Error::Other(false));
477 }
478