syn_pub_items/
error.rs

1use std;
2use std::fmt::{self, Display};
3use std::iter::FromIterator;
4
5use proc_macro2::{
6    Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree,
7};
8#[cfg(feature = "printing")]
9use quote::ToTokens;
10
11#[cfg(feature = "parsing")]
12use buffer::Cursor;
13#[cfg(all(procmacro2_semver_exempt, feature = "parsing"))]
14use private;
15use thread::ThreadBound;
16
17/// The result of a Syn parser.
18pub type Result<T> = std::result::Result<T, Error>;
19
20/// Error returned when a Syn parser cannot parse the input tokens.
21///
22/// Refer to the [module documentation] for details about parsing in Syn.
23///
24/// [module documentation]: index.html
25///
26/// *This type is available if Syn is built with the `"parsing"` feature.*
27#[derive(Debug)]
28pub struct Error {
29    // Span is implemented as an index into a thread-local interner to keep the
30    // size small. It is not safe to access from a different thread. We want
31    // errors to be Send and Sync to play nicely with the Failure crate, so pin
32    // the span we're given to its original thread and assume it is
33    // Span::call_site if accessed from any other thread.
34    start_span: ThreadBound<Span>,
35    end_span: ThreadBound<Span>,
36    message: String,
37}
38
39#[cfg(test)]
40struct _Test
41where
42    Error: Send + Sync;
43
44impl Error {
45    /// Usually the [`ParseStream::error`] method will be used instead, which
46    /// automatically uses the correct span from the current position of the
47    /// parse stream.
48    ///
49    /// Use `Error::new` when the error needs to be triggered on some span other
50    /// than where the parse stream is currently positioned.
51    ///
52    /// [`ParseStream::error`]: struct.ParseBuffer.html#method.error
53    ///
54    /// # Example
55    ///
56    /// ```edition2018
57    /// use syn::{Error, Ident, LitStr, Result, Token};
58    /// use syn::parse::ParseStream;
59    ///
60    /// // Parses input that looks like `name = "string"` where the key must be
61    /// // the identifier `name` and the value may be any string literal.
62    /// // Returns the string literal.
63    /// fn parse_name(input: ParseStream) -> Result<LitStr> {
64    ///     let name_token: Ident = input.parse()?;
65    ///     if name_token != "name" {
66    ///         // Trigger an error not on the current position of the stream,
67    ///         // but on the position of the unexpected identifier.
68    ///         return Err(Error::new(name_token.span(), "expected `name`"));
69    ///     }
70    ///     input.parse::<Token![=]>()?;
71    ///     let s: LitStr = input.parse()?;
72    ///     Ok(s)
73    /// }
74    /// ```
75    pub fn new<T: Display>(span: Span, message: T) -> Self {
76        Error {
77            start_span: ThreadBound::new(span),
78            end_span: ThreadBound::new(span),
79            message: message.to_string(),
80        }
81    }
82
83    /// Creates an error with the specified message spanning the given syntax
84    /// tree node.
85    ///
86    /// Unlike the `Error::new` constructor, this constructor takes an argument
87    /// `tokens` which is a syntax tree node. This allows the resulting `Error`
88    /// to attempt to span all tokens inside of `tokens`. While you would
89    /// typically be able to use the `Spanned` trait with the above `Error::new`
90    /// constructor, implementation limitations today mean that
91    /// `Error::new_spanned` may provide a higher-quality error message on
92    /// stable Rust.
93    ///
94    /// When in doubt it's recommended to stick to `Error::new` (or
95    /// `ParseStream::error`)!
96    #[cfg(feature = "printing")]
97    pub fn new_spanned<T: ToTokens, U: Display>(tokens: T, message: U) -> Self {
98        let mut iter = tokens.into_token_stream().into_iter();
99        let start = iter.next().map_or_else(Span::call_site, |t| t.span());
100        let end = iter.last().map_or(start, |t| t.span());
101        Error {
102            start_span: ThreadBound::new(start),
103            end_span: ThreadBound::new(end),
104            message: message.to_string(),
105        }
106    }
107
108    /// The source location of the error.
109    ///
110    /// Spans are not thread-safe so this function returns `Span::call_site()`
111    /// if called from a different thread than the one on which the `Error` was
112    /// originally created.
113    pub fn span(&self) -> Span {
114        let start = match self.start_span.get() {
115            Some(span) => *span,
116            None => return Span::call_site(),
117        };
118
119        #[cfg(procmacro2_semver_exempt)]
120        {
121            let end = match self.end_span.get() {
122                Some(span) => *span,
123                None => return Span::call_site(),
124            };
125            start.join(end).unwrap_or(start)
126        }
127        #[cfg(not(procmacro2_semver_exempt))]
128        {
129            start
130        }
131    }
132
133    /// Render the error as an invocation of [`compile_error!`].
134    ///
135    /// The [`parse_macro_input!`] macro provides a convenient way to invoke
136    /// this method correctly in a procedural macro.
137    ///
138    /// [`compile_error!`]: https://doc.rust-lang.org/std/macro.compile_error.html
139    /// [`parse_macro_input!`]: ../macro.parse_macro_input.html
140    pub fn to_compile_error(&self) -> TokenStream {
141        let start = self
142            .start_span
143            .get()
144            .cloned()
145            .unwrap_or_else(Span::call_site);
146        let end = self.end_span.get().cloned().unwrap_or_else(Span::call_site);
147
148        // compile_error!($message)
149        TokenStream::from_iter(vec![
150            TokenTree::Ident(Ident::new("compile_error", start)),
151            TokenTree::Punct({
152                let mut punct = Punct::new('!', Spacing::Alone);
153                punct.set_span(start);
154                punct
155            }),
156            TokenTree::Group({
157                let mut group = Group::new(Delimiter::Brace, {
158                    TokenStream::from_iter(vec![TokenTree::Literal({
159                        let mut string = Literal::string(&self.message);
160                        string.set_span(end);
161                        string
162                    })])
163                });
164                group.set_span(end);
165                group
166            }),
167        ])
168    }
169}
170
171#[cfg(feature = "parsing")]
172pub fn new_at<T: Display>(scope: Span, cursor: Cursor, message: T) -> Error {
173    if cursor.eof() {
174        Error::new(scope, format!("unexpected end of input, {}", message))
175    } else {
176        #[cfg(procmacro2_semver_exempt)]
177        let span = private::open_span_of_group(cursor);
178        #[cfg(not(procmacro2_semver_exempt))]
179        let span = cursor.span();
180        Error::new(span, message)
181    }
182}
183
184impl Display for Error {
185    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
186        formatter.write_str(&self.message)
187    }
188}
189
190impl Clone for Error {
191    fn clone(&self) -> Self {
192        let start = self
193            .start_span
194            .get()
195            .cloned()
196            .unwrap_or_else(Span::call_site);
197        let end = self.end_span.get().cloned().unwrap_or_else(Span::call_site);
198        Error {
199            start_span: ThreadBound::new(start),
200            end_span: ThreadBound::new(end),
201            message: self.message.clone(),
202        }
203    }
204}
205
206impl std::error::Error for Error {
207    fn description(&self) -> &str {
208        "parse error"
209    }
210}
211
212impl From<LexError> for Error {
213    fn from(err: LexError) -> Self {
214        Error::new(Span::call_site(), format!("{:?}", err))
215    }
216}