1 | #[cfg (feature = "parsing" )] |
2 | use crate::buffer::Cursor; |
3 | use crate::thread::ThreadBound; |
4 | use proc_macro2::{ |
5 | Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree, |
6 | }; |
7 | #[cfg (feature = "printing" )] |
8 | use quote::ToTokens; |
9 | use std::fmt::{self, Debug, Display}; |
10 | use std::slice; |
11 | use std::vec; |
12 | |
13 | /// The result of a Syn parser. |
14 | pub type Result<T> = std::result::Result<T, Error>; |
15 | |
16 | /// Error returned when a Syn parser cannot parse the input tokens. |
17 | /// |
18 | /// # Error reporting in proc macros |
19 | /// |
20 | /// The correct way to report errors back to the compiler from a procedural |
21 | /// macro is by emitting an appropriately spanned invocation of |
22 | /// [`compile_error!`] in the generated code. This produces a better diagnostic |
23 | /// message than simply panicking the macro. |
24 | /// |
25 | /// [`compile_error!`]: std::compile_error! |
26 | /// |
27 | /// When parsing macro input, the [`parse_macro_input!`] macro handles the |
28 | /// conversion to `compile_error!` automatically. |
29 | /// |
30 | /// [`parse_macro_input!`]: crate::parse_macro_input! |
31 | /// |
32 | /// ``` |
33 | /// # extern crate proc_macro; |
34 | /// # |
35 | /// use proc_macro::TokenStream; |
36 | /// use syn::parse::{Parse, ParseStream, Result}; |
37 | /// use syn::{parse_macro_input, ItemFn}; |
38 | /// |
39 | /// # const IGNORE: &str = stringify! { |
40 | /// #[proc_macro_attribute] |
41 | /// # }; |
42 | /// pub fn my_attr(args: TokenStream, input: TokenStream) -> TokenStream { |
43 | /// let args = parse_macro_input!(args as MyAttrArgs); |
44 | /// let input = parse_macro_input!(input as ItemFn); |
45 | /// |
46 | /// /* ... */ |
47 | /// # TokenStream::new() |
48 | /// } |
49 | /// |
50 | /// struct MyAttrArgs { |
51 | /// # _k: [(); { stringify! { |
52 | /// ... |
53 | /// # }; 0 }] |
54 | /// } |
55 | /// |
56 | /// impl Parse for MyAttrArgs { |
57 | /// fn parse(input: ParseStream) -> Result<Self> { |
58 | /// # stringify! { |
59 | /// ... |
60 | /// # }; |
61 | /// # unimplemented!() |
62 | /// } |
63 | /// } |
64 | /// ``` |
65 | /// |
66 | /// For errors that arise later than the initial parsing stage, the |
67 | /// [`.to_compile_error()`] or [`.into_compile_error()`] methods can be used to |
68 | /// perform an explicit conversion to `compile_error!`. |
69 | /// |
70 | /// [`.to_compile_error()`]: Error::to_compile_error |
71 | /// [`.into_compile_error()`]: Error::into_compile_error |
72 | /// |
73 | /// ``` |
74 | /// # extern crate proc_macro; |
75 | /// # |
76 | /// # use proc_macro::TokenStream; |
77 | /// # use syn::{parse_macro_input, DeriveInput}; |
78 | /// # |
79 | /// # const IGNORE: &str = stringify! { |
80 | /// #[proc_macro_derive(MyDerive)] |
81 | /// # }; |
82 | /// pub fn my_derive(input: TokenStream) -> TokenStream { |
83 | /// let input = parse_macro_input!(input as DeriveInput); |
84 | /// |
85 | /// // fn(DeriveInput) -> syn::Result<proc_macro2::TokenStream> |
86 | /// expand::my_derive(input) |
87 | /// .unwrap_or_else(syn::Error::into_compile_error) |
88 | /// .into() |
89 | /// } |
90 | /// # |
91 | /// # mod expand { |
92 | /// # use proc_macro2::TokenStream; |
93 | /// # use syn::{DeriveInput, Result}; |
94 | /// # |
95 | /// # pub fn my_derive(input: DeriveInput) -> Result<TokenStream> { |
96 | /// # unimplemented!() |
97 | /// # } |
98 | /// # } |
99 | /// ``` |
100 | pub struct Error { |
101 | messages: Vec<ErrorMessage>, |
102 | } |
103 | |
104 | struct ErrorMessage { |
105 | // Span is implemented as an index into a thread-local interner to keep the |
106 | // size small. It is not safe to access from a different thread. We want |
107 | // errors to be Send and Sync to play nicely with ecosystem crates for error |
108 | // handling, so pin the span we're given to its original thread and assume |
109 | // it is Span::call_site if accessed from any other thread. |
110 | span: ThreadBound<SpanRange>, |
111 | message: String, |
112 | } |
113 | |
114 | // Cannot use std::ops::Range<Span> because that does not implement Copy, |
115 | // whereas ThreadBound<T> requires a Copy impl as a way to ensure no Drop impls |
116 | // are involved. |
117 | struct SpanRange { |
118 | start: Span, |
119 | end: Span, |
120 | } |
121 | |
122 | #[cfg (test)] |
123 | struct _Test |
124 | where |
125 | Error: Send + Sync; |
126 | |
127 | impl Error { |
128 | /// Usually the [`ParseStream::error`] method will be used instead, which |
129 | /// automatically uses the correct span from the current position of the |
130 | /// parse stream. |
131 | /// |
132 | /// Use `Error::new` when the error needs to be triggered on some span other |
133 | /// than where the parse stream is currently positioned. |
134 | /// |
135 | /// [`ParseStream::error`]: crate::parse::ParseBuffer::error |
136 | /// |
137 | /// # Example |
138 | /// |
139 | /// ``` |
140 | /// use syn::{Error, Ident, LitStr, Result, Token}; |
141 | /// use syn::parse::ParseStream; |
142 | /// |
143 | /// // Parses input that looks like `name = "string"` where the key must be |
144 | /// // the identifier `name` and the value may be any string literal. |
145 | /// // Returns the string literal. |
146 | /// fn parse_name(input: ParseStream) -> Result<LitStr> { |
147 | /// let name_token: Ident = input.parse()?; |
148 | /// if name_token != "name" { |
149 | /// // Trigger an error not on the current position of the stream, |
150 | /// // but on the position of the unexpected identifier. |
151 | /// return Err(Error::new(name_token.span(), "expected `name`" )); |
152 | /// } |
153 | /// input.parse::<Token![=]>()?; |
154 | /// let s: LitStr = input.parse()?; |
155 | /// Ok(s) |
156 | /// } |
157 | /// ``` |
158 | pub fn new<T: Display>(span: Span, message: T) -> Self { |
159 | return new(span, message.to_string()); |
160 | |
161 | fn new(span: Span, message: String) -> Error { |
162 | Error { |
163 | messages: vec![ErrorMessage { |
164 | span: ThreadBound::new(SpanRange { |
165 | start: span, |
166 | end: span, |
167 | }), |
168 | message, |
169 | }], |
170 | } |
171 | } |
172 | } |
173 | |
174 | /// Creates an error with the specified message spanning the given syntax |
175 | /// tree node. |
176 | /// |
177 | /// Unlike the `Error::new` constructor, this constructor takes an argument |
178 | /// `tokens` which is a syntax tree node. This allows the resulting `Error` |
179 | /// to attempt to span all tokens inside of `tokens`. While you would |
180 | /// typically be able to use the `Spanned` trait with the above `Error::new` |
181 | /// constructor, implementation limitations today mean that |
182 | /// `Error::new_spanned` may provide a higher-quality error message on |
183 | /// stable Rust. |
184 | /// |
185 | /// When in doubt it's recommended to stick to `Error::new` (or |
186 | /// `ParseStream::error`)! |
187 | #[cfg (feature = "printing" )] |
188 | pub fn new_spanned<T: ToTokens, U: Display>(tokens: T, message: U) -> Self { |
189 | return new_spanned(tokens.into_token_stream(), message.to_string()); |
190 | |
191 | fn new_spanned(tokens: TokenStream, message: String) -> Error { |
192 | let mut iter = tokens.into_iter(); |
193 | let start = iter.next().map_or_else(Span::call_site, |t| t.span()); |
194 | let end = iter.last().map_or(start, |t| t.span()); |
195 | Error { |
196 | messages: vec![ErrorMessage { |
197 | span: ThreadBound::new(SpanRange { start, end }), |
198 | message, |
199 | }], |
200 | } |
201 | } |
202 | } |
203 | |
204 | /// The source location of the error. |
205 | /// |
206 | /// Spans are not thread-safe so this function returns `Span::call_site()` |
207 | /// if called from a different thread than the one on which the `Error` was |
208 | /// originally created. |
209 | pub fn span(&self) -> Span { |
210 | let SpanRange { start, end } = match self.messages[0].span.get() { |
211 | Some(span) => *span, |
212 | None => return Span::call_site(), |
213 | }; |
214 | start.join(end).unwrap_or(start) |
215 | } |
216 | |
217 | /// Render the error as an invocation of [`compile_error!`]. |
218 | /// |
219 | /// The [`parse_macro_input!`] macro provides a convenient way to invoke |
220 | /// this method correctly in a procedural macro. |
221 | /// |
222 | /// [`compile_error!`]: std::compile_error! |
223 | /// [`parse_macro_input!`]: crate::parse_macro_input! |
224 | pub fn to_compile_error(&self) -> TokenStream { |
225 | self.messages |
226 | .iter() |
227 | .map(ErrorMessage::to_compile_error) |
228 | .collect() |
229 | } |
230 | |
231 | /// Render the error as an invocation of [`compile_error!`]. |
232 | /// |
233 | /// [`compile_error!`]: std::compile_error! |
234 | /// |
235 | /// # Example |
236 | /// |
237 | /// ``` |
238 | /// # extern crate proc_macro; |
239 | /// # |
240 | /// use proc_macro::TokenStream; |
241 | /// use syn::{parse_macro_input, DeriveInput, Error}; |
242 | /// |
243 | /// # const _: &str = stringify! { |
244 | /// #[proc_macro_derive(MyTrait)] |
245 | /// # }; |
246 | /// pub fn derive_my_trait(input: TokenStream) -> TokenStream { |
247 | /// let input = parse_macro_input!(input as DeriveInput); |
248 | /// my_trait::expand(input) |
249 | /// .unwrap_or_else(Error::into_compile_error) |
250 | /// .into() |
251 | /// } |
252 | /// |
253 | /// mod my_trait { |
254 | /// use proc_macro2::TokenStream; |
255 | /// use syn::{DeriveInput, Result}; |
256 | /// |
257 | /// pub(crate) fn expand(input: DeriveInput) -> Result<TokenStream> { |
258 | /// /* ... */ |
259 | /// # unimplemented!() |
260 | /// } |
261 | /// } |
262 | /// ``` |
263 | pub fn into_compile_error(self) -> TokenStream { |
264 | self.to_compile_error() |
265 | } |
266 | |
267 | /// Add another error message to self such that when `to_compile_error()` is |
268 | /// called, both errors will be emitted together. |
269 | pub fn combine(&mut self, another: Error) { |
270 | self.messages.extend(another.messages); |
271 | } |
272 | } |
273 | |
274 | impl ErrorMessage { |
275 | fn to_compile_error(&self) -> TokenStream { |
276 | let (start, end) = match self.span.get() { |
277 | Some(range) => (range.start, range.end), |
278 | None => (Span::call_site(), Span::call_site()), |
279 | }; |
280 | |
281 | // ::core::compile_error!($message) |
282 | TokenStream::from_iter(vec![ |
283 | TokenTree::Punct({ |
284 | let mut punct = Punct::new(':' , Spacing::Joint); |
285 | punct.set_span(start); |
286 | punct |
287 | }), |
288 | TokenTree::Punct({ |
289 | let mut punct = Punct::new(':' , Spacing::Alone); |
290 | punct.set_span(start); |
291 | punct |
292 | }), |
293 | TokenTree::Ident(Ident::new("core" , start)), |
294 | TokenTree::Punct({ |
295 | let mut punct = Punct::new(':' , Spacing::Joint); |
296 | punct.set_span(start); |
297 | punct |
298 | }), |
299 | TokenTree::Punct({ |
300 | let mut punct = Punct::new(':' , Spacing::Alone); |
301 | punct.set_span(start); |
302 | punct |
303 | }), |
304 | TokenTree::Ident(Ident::new("compile_error" , start)), |
305 | TokenTree::Punct({ |
306 | let mut punct = Punct::new('!' , Spacing::Alone); |
307 | punct.set_span(start); |
308 | punct |
309 | }), |
310 | TokenTree::Group({ |
311 | let mut group = Group::new(Delimiter::Brace, { |
312 | TokenStream::from_iter(vec![TokenTree::Literal({ |
313 | let mut string = Literal::string(&self.message); |
314 | string.set_span(end); |
315 | string |
316 | })]) |
317 | }); |
318 | group.set_span(end); |
319 | group |
320 | }), |
321 | ]) |
322 | } |
323 | } |
324 | |
325 | #[cfg (feature = "parsing" )] |
326 | pub(crate) fn new_at<T: Display>(scope: Span, cursor: Cursor, message: T) -> Error { |
327 | if cursor.eof() { |
328 | Error::new(scope, format!("unexpected end of input, {}" , message)) |
329 | } else { |
330 | let span = crate::buffer::open_span_of_group(cursor); |
331 | Error::new(span, message) |
332 | } |
333 | } |
334 | |
335 | #[cfg (all(feature = "parsing" , any(feature = "full" , feature = "derive" )))] |
336 | pub(crate) fn new2<T: Display>(start: Span, end: Span, message: T) -> Error { |
337 | return new2(start, end, message.to_string()); |
338 | |
339 | fn new2(start: Span, end: Span, message: String) -> Error { |
340 | Error { |
341 | messages: vec![ErrorMessage { |
342 | span: ThreadBound::new(SpanRange { start, end }), |
343 | message, |
344 | }], |
345 | } |
346 | } |
347 | } |
348 | |
349 | impl Debug for Error { |
350 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
351 | if self.messages.len() == 1 { |
352 | formatter |
353 | .debug_tuple("Error" ) |
354 | .field(&self.messages[0]) |
355 | .finish() |
356 | } else { |
357 | formatter |
358 | .debug_tuple("Error" ) |
359 | .field(&self.messages) |
360 | .finish() |
361 | } |
362 | } |
363 | } |
364 | |
365 | impl Debug for ErrorMessage { |
366 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
367 | Debug::fmt(&self.message, formatter) |
368 | } |
369 | } |
370 | |
371 | impl Display for Error { |
372 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
373 | formatter.write_str(&self.messages[0].message) |
374 | } |
375 | } |
376 | |
377 | impl Clone for Error { |
378 | fn clone(&self) -> Self { |
379 | Error { |
380 | messages: self.messages.clone(), |
381 | } |
382 | } |
383 | } |
384 | |
385 | impl Clone for ErrorMessage { |
386 | fn clone(&self) -> Self { |
387 | ErrorMessage { |
388 | span: self.span, |
389 | message: self.message.clone(), |
390 | } |
391 | } |
392 | } |
393 | |
394 | impl Clone for SpanRange { |
395 | fn clone(&self) -> Self { |
396 | *self |
397 | } |
398 | } |
399 | |
400 | impl Copy for SpanRange {} |
401 | |
402 | impl std::error::Error for Error {} |
403 | |
404 | impl From<LexError> for Error { |
405 | fn from(err: LexError) -> Self { |
406 | Error::new(err.span(), "lex error" ) |
407 | } |
408 | } |
409 | |
410 | impl IntoIterator for Error { |
411 | type Item = Error; |
412 | type IntoIter = IntoIter; |
413 | |
414 | fn into_iter(self) -> Self::IntoIter { |
415 | IntoIter { |
416 | messages: self.messages.into_iter(), |
417 | } |
418 | } |
419 | } |
420 | |
421 | pub struct IntoIter { |
422 | messages: vec::IntoIter<ErrorMessage>, |
423 | } |
424 | |
425 | impl Iterator for IntoIter { |
426 | type Item = Error; |
427 | |
428 | fn next(&mut self) -> Option<Self::Item> { |
429 | Some(Error { |
430 | messages: vec![self.messages.next()?], |
431 | }) |
432 | } |
433 | } |
434 | |
435 | impl<'a> IntoIterator for &'a Error { |
436 | type Item = Error; |
437 | type IntoIter = Iter<'a>; |
438 | |
439 | fn into_iter(self) -> Self::IntoIter { |
440 | Iter { |
441 | messages: self.messages.iter(), |
442 | } |
443 | } |
444 | } |
445 | |
446 | pub struct Iter<'a> { |
447 | messages: slice::Iter<'a, ErrorMessage>, |
448 | } |
449 | |
450 | impl<'a> Iterator for Iter<'a> { |
451 | type Item = Error; |
452 | |
453 | fn next(&mut self) -> Option<Self::Item> { |
454 | Some(Error { |
455 | messages: vec![self.messages.next()?.clone()], |
456 | }) |
457 | } |
458 | } |
459 | |
460 | impl Extend<Error> for Error { |
461 | fn extend<T: IntoIterator<Item = Error>>(&mut self, iter: T) { |
462 | for err in iter { |
463 | self.combine(err); |
464 | } |
465 | } |
466 | } |
467 | |