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