Reject display attribute on a field

This commit is contained in:
David Tolnay 2019-10-12 13:41:56 -07:00
parent 10d1f640da
commit aa123cfe0c
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
5 changed files with 40 additions and 4 deletions

View File

@ -8,12 +8,13 @@ use syn::{
}; };
pub struct Attrs<'a> { pub struct Attrs<'a> {
pub display: Option<Display>, pub display: Option<Display<'a>>,
pub source: Option<Source<'a>>, pub source: Option<Source<'a>>,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Display { pub struct Display<'a> {
pub original: &'a Attribute,
pub fmt: LitStr, pub fmt: LitStr,
pub args: TokenStream, pub args: TokenStream,
pub was_shorthand: bool, pub was_shorthand: bool,
@ -54,6 +55,7 @@ pub fn get(input: &[Attribute]) -> Result<Attrs> {
fn parse_display(attr: &Attribute) -> Result<Display> { fn parse_display(attr: &Attribute) -> Result<Display> {
attr.parse_args_with(|input: ParseStream| { attr.parse_args_with(|input: ParseStream| {
let mut display = Display { let mut display = Display {
original: attr,
fmt: input.parse()?, fmt: input.parse()?,
args: parse_token_expr(input, false)?, args: parse_token_expr(input, false)?,
was_shorthand: false, was_shorthand: false,
@ -116,7 +118,7 @@ fn parse_source(attr: &Attribute) -> Result<Source> {
Ok(Source { original: attr }) Ok(Source { original: attr })
} }
impl ToTokens for Display { impl ToTokens for Display<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let fmt = &self.fmt; let fmt = &self.fmt;
let args = &self.args; let args = &self.args;

View File

@ -3,7 +3,7 @@ use proc_macro2::TokenStream;
use quote::quote_spanned; use quote::quote_spanned;
use syn::{Ident, LitStr}; use syn::{Ident, LitStr};
impl Display { impl Display<'_> {
// Transform `"error {var}"` to `"error {}", var`. // Transform `"error {var}"` to `"error {}", var`.
pub fn expand_shorthand(&mut self) { pub fn expand_shorthand(&mut self) {
if !self.args.is_empty() { if !self.args.is_empty() {

View File

@ -17,6 +17,9 @@ impl Struct<'_> {
fn validate(&self) -> Result<()> { fn validate(&self) -> Result<()> {
check_no_source(&self.attrs)?; check_no_source(&self.attrs)?;
find_duplicate_source(&self.fields)?; find_duplicate_source(&self.fields)?;
for field in &self.fields {
field.validate()?;
}
Ok(()) Ok(())
} }
} }
@ -42,6 +45,21 @@ impl Variant<'_> {
fn validate(&self) -> Result<()> { fn validate(&self) -> Result<()> {
check_no_source(&self.attrs)?; check_no_source(&self.attrs)?;
find_duplicate_source(&self.fields)?; find_duplicate_source(&self.fields)?;
for field in &self.fields {
field.validate()?;
}
Ok(())
}
}
impl Field<'_> {
fn validate(&self) -> Result<()> {
if let Some(display) = &self.attrs.display {
return Err(Error::new_spanned(
display.original,
"not expected here; the #[error(...)] attribute belongs on top of a struct or an enum variant",
));
}
Ok(()) Ok(())
} }
} }

View File

@ -0,0 +1,11 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
What {
#[error("...")]
io: std::io::Error,
},
}
fn main() {}

View File

@ -0,0 +1,5 @@
error: not expected here; the #[error(...)] attribute belongs on top of a struct or an enum variant
--> $DIR/unexpected-field-fmt.rs:6:9
|
6 | #[error("...")]
| ^^^^^^^^^^^^^^^