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