Skip to main content

syn/
error.rs

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