mirror of
https://github.com/dtolnay/thiserror.git
synced 2025-04-04 13:27:38 +03:00
Merge pull request #87 from dtolnay/span
Reconstruct span information lost by compiler
This commit is contained in:
commit
72aaea5e1f
4 changed files with 72 additions and 19 deletions
|
@ -1,4 +1,5 @@
|
|||
use crate::attr::{self, Attrs};
|
||||
use proc_macro2::Span;
|
||||
use syn::{
|
||||
Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Generics, Ident, Index, Member, Result,
|
||||
Type,
|
||||
|
@ -55,7 +56,8 @@ impl<'a> Input<'a> {
|
|||
impl<'a> Struct<'a> {
|
||||
fn from_syn(node: &'a DeriveInput, data: &'a DataStruct) -> Result<Self> {
|
||||
let mut attrs = attr::get(&node.attrs)?;
|
||||
let fields = Field::multiple_from_syn(&data.fields)?;
|
||||
let span = attrs.span().unwrap_or_else(Span::call_site);
|
||||
let fields = Field::multiple_from_syn(&data.fields, span)?;
|
||||
if let Some(display) = &mut attrs.display {
|
||||
display.expand_shorthand(&fields);
|
||||
}
|
||||
|
@ -72,11 +74,12 @@ impl<'a> Struct<'a> {
|
|||
impl<'a> Enum<'a> {
|
||||
fn from_syn(node: &'a DeriveInput, data: &'a DataEnum) -> Result<Self> {
|
||||
let attrs = attr::get(&node.attrs)?;
|
||||
let span = attrs.span().unwrap_or_else(Span::call_site);
|
||||
let variants = data
|
||||
.variants
|
||||
.iter()
|
||||
.map(|node| {
|
||||
let mut variant = Variant::from_syn(node)?;
|
||||
let mut variant = Variant::from_syn(node, span)?;
|
||||
if let display @ None = &mut variant.attrs.display {
|
||||
*display = attrs.display.clone();
|
||||
}
|
||||
|
@ -99,35 +102,50 @@ impl<'a> Enum<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Variant<'a> {
|
||||
fn from_syn(node: &'a syn::Variant) -> Result<Self> {
|
||||
fn from_syn(node: &'a syn::Variant, span: Span) -> Result<Self> {
|
||||
let attrs = attr::get(&node.attrs)?;
|
||||
let span = attrs.span().unwrap_or(span);
|
||||
Ok(Variant {
|
||||
original: node,
|
||||
attrs: attr::get(&node.attrs)?,
|
||||
attrs,
|
||||
ident: node.ident.clone(),
|
||||
fields: Field::multiple_from_syn(&node.fields)?,
|
||||
fields: Field::multiple_from_syn(&node.fields, span)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Field<'a> {
|
||||
fn multiple_from_syn(fields: &'a Fields) -> Result<Vec<Self>> {
|
||||
fn multiple_from_syn(fields: &'a Fields, span: Span) -> Result<Vec<Self>> {
|
||||
fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, field)| Field::from_syn(i, field))
|
||||
.map(|(i, field)| Field::from_syn(i, field, span))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn from_syn(i: usize, node: &'a syn::Field) -> Result<Self> {
|
||||
fn from_syn(i: usize, node: &'a syn::Field, span: Span) -> Result<Self> {
|
||||
Ok(Field {
|
||||
original: node,
|
||||
attrs: attr::get(&node.attrs)?,
|
||||
member: node
|
||||
.ident
|
||||
.clone()
|
||||
.map(Member::Named)
|
||||
.unwrap_or_else(|| Member::Unnamed(Index::from(i))),
|
||||
member: node.ident.clone().map(Member::Named).unwrap_or_else(|| {
|
||||
Member::Unnamed(Index {
|
||||
index: i as u32,
|
||||
span,
|
||||
})
|
||||
}),
|
||||
ty: &node.ty,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Attrs<'_> {
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
if let Some(display) = &self.display {
|
||||
Some(display.fmt.span())
|
||||
} else if let Some(transparent) = &self.transparent {
|
||||
Some(transparent.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
|
||||
use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use std::iter::FromIterator;
|
||||
use syn::parse::{Nothing, ParseStream};
|
||||
|
@ -12,7 +12,7 @@ pub struct Attrs<'a> {
|
|||
pub source: Option<&'a Attribute>,
|
||||
pub backtrace: Option<&'a Attribute>,
|
||||
pub from: Option<&'a Attribute>,
|
||||
pub transparent: Option<&'a Attribute>,
|
||||
pub transparent: Option<Transparent<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -23,6 +23,12 @@ pub struct Display<'a> {
|
|||
pub has_bonus_display: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Transparent<'a> {
|
||||
pub original: &'a Attribute,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
pub fn get(input: &[Attribute]) -> Result<Attrs> {
|
||||
let mut attrs = Attrs {
|
||||
display: None,
|
||||
|
@ -66,14 +72,17 @@ fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Resu
|
|||
syn::custom_keyword!(transparent);
|
||||
|
||||
attr.parse_args_with(|input: ParseStream| {
|
||||
if input.parse::<Option<transparent>>()?.is_some() {
|
||||
if let Some(kw) = input.parse::<Option<transparent>>()? {
|
||||
if attrs.transparent.is_some() {
|
||||
return Err(Error::new_spanned(
|
||||
attr,
|
||||
"duplicate #[error(transparent)] attribute",
|
||||
));
|
||||
}
|
||||
attrs.transparent = Some(attr);
|
||||
attrs.transparent = Some(Transparent {
|
||||
original: attr,
|
||||
span: kw.span,
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ impl Struct<'_> {
|
|||
if let Some(transparent) = self.attrs.transparent {
|
||||
if self.fields.len() != 1 {
|
||||
return Err(Error::new_spanned(
|
||||
transparent,
|
||||
transparent.original,
|
||||
"#[error(transparent)] requires exactly one field",
|
||||
));
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> {
|
|||
}
|
||||
if let Some(transparent) = field.attrs.transparent {
|
||||
return Err(Error::new_spanned(
|
||||
transparent,
|
||||
transparent.original,
|
||||
"#[error(transparent)] needs to go outside the enum or struct, not on an individual field",
|
||||
));
|
||||
}
|
||||
|
|
|
@ -198,3 +198,29 @@ fn test_field() {
|
|||
|
||||
assert("0", Error(Inner { data: 0 }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_rules() {
|
||||
// Regression test for https://github.com/dtolnay/thiserror/issues/86
|
||||
|
||||
macro_rules! decl_error {
|
||||
($variant:ident($value:ident)) => {
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error0 {
|
||||
#[error("{0:?}")]
|
||||
$variant($value),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("{0:?}")]
|
||||
pub enum Error1 {
|
||||
$variant($value),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
decl_error!(Repro(u8));
|
||||
|
||||
assert("0", Error0::Repro(0));
|
||||
assert("0", Error1::Repro(0));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue