1 #![allow(clippy::uninlined_format_args)]
2
3 use std::fmt::{self, Display};
4 use thiserror::Error;
5
assert<T: Display>(expected: &str, value: T)6 fn assert<T: Display>(expected: &str, value: T) {
7 assert_eq!(expected, value.to_string());
8 }
9
10 #[test]
test_braced()11 fn test_braced() {
12 #[derive(Error, Debug)]
13 #[error("braced error: {msg}")]
14 struct Error {
15 msg: String,
16 }
17
18 let msg = "T".to_owned();
19 assert("braced error: T", Error { msg });
20 }
21
22 #[test]
test_braced_unused()23 fn test_braced_unused() {
24 #[derive(Error, Debug)]
25 #[error("braced error")]
26 struct Error {
27 extra: usize,
28 }
29
30 assert("braced error", Error { extra: 0 });
31 }
32
33 #[test]
test_tuple()34 fn test_tuple() {
35 #[derive(Error, Debug)]
36 #[error("tuple error: {0}")]
37 struct Error(usize);
38
39 assert("tuple error: 0", Error(0));
40 }
41
42 #[test]
test_unit()43 fn test_unit() {
44 #[derive(Error, Debug)]
45 #[error("unit error")]
46 struct Error;
47
48 assert("unit error", Error);
49 }
50
51 #[test]
test_enum()52 fn test_enum() {
53 #[derive(Error, Debug)]
54 enum Error {
55 #[error("braced error: {id}")]
56 Braced { id: usize },
57 #[error("tuple error: {0}")]
58 Tuple(usize),
59 #[error("unit error")]
60 Unit,
61 }
62
63 assert("braced error: 0", Error::Braced { id: 0 });
64 assert("tuple error: 0", Error::Tuple(0));
65 assert("unit error", Error::Unit);
66 }
67
68 #[test]
test_constants()69 fn test_constants() {
70 #[derive(Error, Debug)]
71 #[error("{MSG}: {id:?} (code {CODE:?})")]
72 struct Error {
73 id: &'static str,
74 }
75
76 const MSG: &str = "failed to do";
77 const CODE: usize = 9;
78
79 assert("failed to do: \"\" (code 9)", Error { id: "" });
80 }
81
82 #[test]
test_inherit()83 fn test_inherit() {
84 #[derive(Error, Debug)]
85 #[error("{0}")]
86 enum Error {
87 Some(&'static str),
88 #[error("other error")]
89 Other(&'static str),
90 }
91
92 assert("some error", Error::Some("some error"));
93 assert("other error", Error::Other("..."));
94 }
95
96 #[test]
test_brace_escape()97 fn test_brace_escape() {
98 #[derive(Error, Debug)]
99 #[error("fn main() {{}}")]
100 struct Error;
101
102 assert("fn main() {}", Error);
103 }
104
105 #[test]
test_expr()106 fn test_expr() {
107 #[derive(Error, Debug)]
108 #[error("1 + 1 = {}", 1 + 1)]
109 struct Error;
110 assert("1 + 1 = 2", Error);
111 }
112
113 #[test]
test_nested()114 fn test_nested() {
115 #[derive(Error, Debug)]
116 #[error("!bool = {}", not(.0))]
117 struct Error(bool);
118
119 #[allow(clippy::trivially_copy_pass_by_ref)]
120 fn not(bool: &bool) -> bool {
121 !*bool
122 }
123
124 assert("!bool = false", Error(true));
125 }
126
127 #[test]
test_match()128 fn test_match() {
129 #[derive(Error, Debug)]
130 #[error("{}: {0}", match .1 {
131 Some(n) => format!("error occurred with {}", n),
132 None => "there was an empty error".to_owned(),
133 })]
134 struct Error(String, Option<usize>);
135
136 assert(
137 "error occurred with 1: ...",
138 Error("...".to_owned(), Some(1)),
139 );
140 assert(
141 "there was an empty error: ...",
142 Error("...".to_owned(), None),
143 );
144 }
145
146 #[test]
test_nested_display()147 fn test_nested_display() {
148 // Same behavior as the one in `test_match`, but without String allocations.
149 #[derive(Error, Debug)]
150 #[error("{}", {
151 struct Msg<'a>(&'a String, &'a Option<usize>);
152 impl<'a> Display for Msg<'a> {
153 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
154 match self.1 {
155 Some(n) => write!(formatter, "error occurred with {}", n),
156 None => write!(formatter, "there was an empty error"),
157 }?;
158 write!(formatter, ": {}", self.0)
159 }
160 }
161 Msg(.0, .1)
162 })]
163 struct Error(String, Option<usize>);
164
165 assert(
166 "error occurred with 1: ...",
167 Error("...".to_owned(), Some(1)),
168 );
169 assert(
170 "there was an empty error: ...",
171 Error("...".to_owned(), None),
172 );
173 }
174
175 #[test]
test_void()176 fn test_void() {
177 #[allow(clippy::empty_enum)]
178 #[derive(Error, Debug)]
179 #[error("...")]
180 pub enum Error {}
181
182 let _: Error;
183 }
184
185 #[test]
test_mixed()186 fn test_mixed() {
187 #[derive(Error, Debug)]
188 #[error("a={a} :: b={} :: c={c} :: d={d}", 1, c = 2, d = 3)]
189 struct Error {
190 a: usize,
191 d: usize,
192 }
193
194 assert("a=0 :: b=1 :: c=2 :: d=3", Error { a: 0, d: 0 });
195 }
196
197 #[test]
test_ints()198 fn test_ints() {
199 #[derive(Error, Debug)]
200 enum Error {
201 #[error("error {0}")]
202 Tuple(usize, usize),
203 #[error("error {0}", '?')]
204 Struct { v: usize },
205 }
206
207 assert("error 9", Error::Tuple(9, 0));
208 assert("error ?", Error::Struct { v: 0 });
209 }
210
211 #[test]
test_trailing_comma()212 fn test_trailing_comma() {
213 #[derive(Error, Debug)]
214 #[error(
215 "error {0}",
216 )]
217 #[rustfmt::skip]
218 struct Error(char);
219
220 assert("error ?", Error('?'));
221 }
222
223 #[test]
test_field()224 fn test_field() {
225 #[derive(Debug)]
226 struct Inner {
227 data: usize,
228 }
229
230 #[derive(Error, Debug)]
231 #[error("{}", .0.data)]
232 struct Error(Inner);
233
234 assert("0", Error(Inner { data: 0 }));
235 }
236
237 #[test]
test_macro_rules()238 fn test_macro_rules() {
239 // Regression test for https://github.com/dtolnay/thiserror/issues/86
240
241 macro_rules! decl_error {
242 ($variant:ident($value:ident)) => {
243 #[derive(Debug, Error)]
244 pub enum Error0 {
245 #[error("{0:?}")]
246 $variant($value),
247 }
248
249 #[derive(Debug, Error)]
250 #[error("{0:?}")]
251 pub enum Error1 {
252 $variant($value),
253 }
254 };
255 }
256
257 decl_error!(Repro(u8));
258
259 assert("0", Error0::Repro(0));
260 assert("0", Error1::Repro(0));
261 }
262
263 #[test]
test_raw()264 fn test_raw() {
265 #[derive(Error, Debug)]
266 #[error("braced raw error: {r#fn}")]
267 struct Error {
268 r#fn: &'static str,
269 }
270
271 assert("braced raw error: T", Error { r#fn: "T" });
272 }
273
274 #[test]
test_raw_enum()275 fn test_raw_enum() {
276 #[derive(Error, Debug)]
277 enum Error {
278 #[error("braced raw error: {r#fn}")]
279 Braced { r#fn: &'static str },
280 }
281
282 assert("braced raw error: T", Error::Braced { r#fn: "T" });
283 }
284
285 #[test]
test_raw_conflict()286 fn test_raw_conflict() {
287 #[derive(Error, Debug)]
288 enum Error {
289 #[error("braced raw error: {r#func}, {func}", func = "U")]
290 Braced { r#func: &'static str },
291 }
292
293 assert("braced raw error: T, U", Error::Braced { r#func: "T" });
294 }
295
296 #[test]
test_keyword()297 fn test_keyword() {
298 #[derive(Error, Debug)]
299 #[error("error: {type}", type = 1)]
300 struct Error;
301
302 assert("error: 1", Error);
303 }
304