at master 14 kB view raw
1// SPDX-License-Identifier: Apache-2.0 OR MIT 2 3#[cfg(feature = "parsing")] 4use crate::buffer::Cursor; 5use crate::thread::ThreadBound; 6use proc_macro2::{ 7 Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree, 8}; 9#[cfg(feature = "printing")] 10use quote::ToTokens; 11use std::fmt::{self, Debug, Display}; 12use std::slice; 13use std::vec; 14 15/// The result of a Syn parser. 16pub 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/// ``` 102pub struct Error { 103 messages: Vec<ErrorMessage>, 104} 105 106struct 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. 119struct SpanRange { 120 start: Span, 121 end: Span, 122} 123 124#[cfg(test)] 125struct _Test 126where 127 Error: Send + Sync; 128 129impl 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 277impl 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")] 329pub(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")))] 339pub(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 352impl 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 368impl Debug for ErrorMessage { 369 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 370 Debug::fmt(&self.message, formatter) 371 } 372} 373 374impl Display for Error { 375 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 376 formatter.write_str(&self.messages[0].message) 377 } 378} 379 380impl Clone for Error { 381 fn clone(&self) -> Self { 382 Error { 383 messages: self.messages.clone(), 384 } 385 } 386} 387 388impl Clone for ErrorMessage { 389 fn clone(&self) -> Self { 390 ErrorMessage { 391 span: self.span, 392 message: self.message.clone(), 393 } 394 } 395} 396 397impl Clone for SpanRange { 398 fn clone(&self) -> Self { 399 *self 400 } 401} 402 403impl Copy for SpanRange {} 404 405impl std::error::Error for Error {} 406 407impl From<LexError> for Error { 408 fn from(err: LexError) -> Self { 409 Error::new(err.span(), err) 410 } 411} 412 413impl 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 424pub struct IntoIter { 425 messages: vec::IntoIter<ErrorMessage>, 426} 427 428impl 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 438impl<'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 449pub struct Iter<'a> { 450 messages: slice::Iter<'a, ErrorMessage>, 451} 452 453impl<'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 463impl 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}