Skip to content

Commit

Permalink
Take a useful span for constructed idents
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed May 15, 2020
1 parent 7dc7219 commit 3891d98
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 17 deletions.
27 changes: 16 additions & 11 deletions impl/src/ast.rs
Original file line number Diff line number Diff line change
@@ -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();
let fields = Field::multiple_from_syn(&data.fields, span)?;
if let Some(display) = &mut attrs.display {
display.expand_shorthand(&fields);
}
Expand Down Expand Up @@ -100,33 +102,36 @@ impl<'a> Enum<'a> {

impl<'a> Variant<'a> {
fn from_syn(node: &'a syn::Variant) -> Result<Self> {
let attrs = attr::get(&node.attrs)?;
let span = attrs.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,
})
}
Expand Down
29 changes: 25 additions & 4 deletions impl/src/attr.rs
Original file line number Diff line number Diff line change
@@ -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 Expand Up @@ -179,3 +188,15 @@ impl ToTokens for Display<'_> {
});
}
}

impl Attrs<'_> {
pub fn span(&self) -> Span {
if let Some(display) = &self.display {
display.fmt.span()
} else if let Some(transparent) = &self.transparent {
transparent.span
} else {
Span::call_site()
}
}
}
4 changes: 2 additions & 2 deletions impl/src/valid.rs
Original file line number Diff line number Diff line change
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

0 comments on commit 3891d98

Please sign in to comment.