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