• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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