Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconstruct span information lost by compiler #87

Merged
merged 4 commits into from May 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 31 additions & 13 deletions impl/src/ast.rs
@@ -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,
Expand Down Expand Up @@ -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);
}
Expand All @@ -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();
}
Expand All @@ -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
}
}
}
17 changes: 13 additions & 4 deletions impl/src/attr.rs
@@ -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};
Expand All @@ -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)]
Expand All @@ -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,
Expand Down Expand Up @@ -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(());
}

Expand Down
4 changes: 2 additions & 2 deletions impl/src/valid.rs
Expand Up @@ -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",
));
}
Expand Down Expand Up @@ -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",
));
}
Expand Down
26 changes: 26 additions & 0 deletions tests/test_display.rs
Expand Up @@ -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));
}