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(doc_cfg, 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 self.messages
227 .iter()
228 .map(ErrorMessage::to_compile_error)
229 .collect()
230 }
231
232 /// Render the error as an invocation of [`compile_error!`].
233 ///
234 /// [`compile_error!`]: std::compile_error!
235 ///
236 /// # Example
237 ///
238 /// ```
239 /// # extern crate proc_macro;
240 /// #
241 /// use proc_macro::TokenStream;
242 /// use syn::{parse_macro_input, DeriveInput, Error};
243 ///
244 /// # const _: &str = stringify! {
245 /// #[proc_macro_derive(MyTrait)]
246 /// # };
247 /// pub fn derive_my_trait(input: TokenStream) -> TokenStream {
248 /// let input = parse_macro_input!(input as DeriveInput);
249 /// my_trait::expand(input)
250 /// .unwrap_or_else(Error::into_compile_error)
251 /// .into()
252 /// }
253 ///
254 /// mod my_trait {
255 /// use proc_macro2::TokenStream;
256 /// use syn::{DeriveInput, Result};
257 ///
258 /// pub(crate) fn expand(input: DeriveInput) -> Result<TokenStream> {
259 /// /* ... */
260 /// # unimplemented!()
261 /// }
262 /// }
263 /// ```
264 pub fn into_compile_error(self) -> TokenStream {
265 self.to_compile_error()
266 }
267
268 /// Add another error message to self such that when `to_compile_error()` is
269 /// called, both errors will be emitted together.
270 pub fn combine(&mut self, another: Error) {
271 self.messages.extend(another.messages);
272 }
273}
274
275impl ErrorMessage {
276 fn to_compile_error(&self) -> TokenStream {
277 let (start, end) = match self.span.get() {
278 Some(range) => (range.start, range.end),
279 None => (Span::call_site(), Span::call_site()),
280 };
281
282 // ::core::compile_error!($message)
283 TokenStream::from_iter(vec![
284 TokenTree::Punct({
285 let mut punct = Punct::new(':', Spacing::Joint);
286 punct.set_span(start);
287 punct
288 }),
289 TokenTree::Punct({
290 let mut punct = Punct::new(':', Spacing::Alone);
291 punct.set_span(start);
292 punct
293 }),
294 TokenTree::Ident(Ident::new("core", start)),
295 TokenTree::Punct({
296 let mut punct = Punct::new(':', Spacing::Joint);
297 punct.set_span(start);
298 punct
299 }),
300 TokenTree::Punct({
301 let mut punct = Punct::new(':', Spacing::Alone);
302 punct.set_span(start);
303 punct
304 }),
305 TokenTree::Ident(Ident::new("compile_error", start)),
306 TokenTree::Punct({
307 let mut punct = Punct::new('!', Spacing::Alone);
308 punct.set_span(start);
309 punct
310 }),
311 TokenTree::Group({
312 let mut group = Group::new(Delimiter::Brace, {
313 TokenStream::from_iter(vec![TokenTree::Literal({
314 let mut string = Literal::string(&self.message);
315 string.set_span(end);
316 string
317 })])
318 });
319 group.set_span(end);
320 group
321 }),
322 ])
323 }
324}
325
326#[cfg(feature = "parsing")]
327pub(crate) fn new_at<T: Display>(scope: Span, cursor: Cursor, message: T) -> Error {
328 if cursor.eof() {
329 Error::new(span:scope, message:format!("unexpected end of input, {}", message))
330 } else {
331 let span: Span = crate::buffer::open_span_of_group(cursor);
332 Error::new(span, message)
333 }
334}
335
336#[cfg(all(feature = "parsing", any(feature = "full", feature = "derive")))]
337pub(crate) fn new2<T: Display>(start: Span, end: Span, message: T) -> Error {
338 return new2(start, end, message:message.to_string());
339
340 fn new2(start: Span, end: Span, message: String) -> Error {
341 Error {
342 messages: vec![ErrorMessage {
343 span: ThreadBound::new(SpanRange { start, end }),
344 message,
345 }],
346 }
347 }
348}
349
350impl Debug for Error {
351 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
352 if self.messages.len() == 1 {
353 formatter&mut DebugTuple<'_, '_>
354 .debug_tuple(name:"Error")
355 .field(&self.messages[0])
356 .finish()
357 } else {
358 formatter&mut DebugTuple<'_, '_>
359 .debug_tuple(name:"Error")
360 .field(&self.messages)
361 .finish()
362 }
363 }
364}
365
366impl Debug for ErrorMessage {
367 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
368 Debug::fmt(&self.message, f:formatter)
369 }
370}
371
372impl Display for Error {
373 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
374 formatter.write_str(&self.messages[0].message)
375 }
376}
377
378impl Clone for Error {
379 fn clone(&self) -> Self {
380 Error {
381 messages: self.messages.clone(),
382 }
383 }
384}
385
386impl Clone for ErrorMessage {
387 fn clone(&self) -> Self {
388 ErrorMessage {
389 span: self.span,
390 message: self.message.clone(),
391 }
392 }
393}
394
395impl Clone for SpanRange {
396 fn clone(&self) -> Self {
397 *self
398 }
399}
400
401impl Copy for SpanRange {}
402
403impl std::error::Error for Error {}
404
405impl From<LexError> for Error {
406 fn from(err: LexError) -> Self {
407 Error::new(err.span(), message:err)
408 }
409}
410
411impl IntoIterator for Error {
412 type Item = Error;
413 type IntoIter = IntoIter;
414
415 fn into_iter(self) -> Self::IntoIter {
416 IntoIter {
417 messages: self.messages.into_iter(),
418 }
419 }
420}
421
422pub struct IntoIter {
423 messages: vec::IntoIter<ErrorMessage>,
424}
425
426impl Iterator for IntoIter {
427 type Item = Error;
428
429 fn next(&mut self) -> Option<Self::Item> {
430 Some(Error {
431 messages: vec![self.messages.next()?],
432 })
433 }
434}
435
436impl<'a> IntoIterator for &'a Error {
437 type Item = Error;
438 type IntoIter = Iter<'a>;
439
440 fn into_iter(self) -> Self::IntoIter {
441 Iter {
442 messages: self.messages.iter(),
443 }
444 }
445}
446
447pub struct Iter<'a> {
448 messages: slice::Iter<'a, ErrorMessage>,
449}
450
451impl<'a> Iterator for Iter<'a> {
452 type Item = Error;
453
454 fn next(&mut self) -> Option<Self::Item> {
455 Some(Error {
456 messages: vec![self.messages.next()?.clone()],
457 })
458 }
459}
460
461impl Extend<Error> for Error {
462 fn extend<T: IntoIterator<Item = Error>>(&mut self, iter: T) {
463 for err: Error in iter {
464 self.combine(another:err);
465 }
466 }
467}
468