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