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