syn/
error.rs

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