From aa123cfe0cafca605eac78ba35ece9e9e656a9ec Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 12 Oct 2019 13:41:56 -0700 Subject: [PATCH] Reject display attribute on a field --- impl/src/attr.rs | 8 +++++--- impl/src/fmt.rs | 2 +- impl/src/valid.rs | 18 ++++++++++++++++++ tests/ui/unexpected-field-fmt.rs | 11 +++++++++++ tests/ui/unexpected-field-fmt.stderr | 5 +++++ 5 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 tests/ui/unexpected-field-fmt.rs create mode 100644 tests/ui/unexpected-field-fmt.stderr diff --git a/impl/src/attr.rs b/impl/src/attr.rs index 93e95dd..00fb965 100644 --- a/impl/src/attr.rs +++ b/impl/src/attr.rs @@ -8,12 +8,13 @@ use syn::{ }; pub struct Attrs<'a> { - pub display: Option, + pub display: Option>, pub source: Option>, } #[derive(Clone)] -pub struct Display { +pub struct Display<'a> { + pub original: &'a Attribute, pub fmt: LitStr, pub args: TokenStream, pub was_shorthand: bool, @@ -54,6 +55,7 @@ pub fn get(input: &[Attribute]) -> Result { fn parse_display(attr: &Attribute) -> Result { attr.parse_args_with(|input: ParseStream| { let mut display = Display { + original: attr, fmt: input.parse()?, args: parse_token_expr(input, false)?, was_shorthand: false, @@ -116,7 +118,7 @@ fn parse_source(attr: &Attribute) -> Result { Ok(Source { original: attr }) } -impl ToTokens for Display { +impl ToTokens for Display<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let fmt = &self.fmt; let args = &self.args; diff --git a/impl/src/fmt.rs b/impl/src/fmt.rs index a96de59..be897e5 100644 --- a/impl/src/fmt.rs +++ b/impl/src/fmt.rs @@ -3,7 +3,7 @@ use proc_macro2::TokenStream; use quote::quote_spanned; use syn::{Ident, LitStr}; -impl Display { +impl Display<'_> { // Transform `"error {var}"` to `"error {}", var`. pub fn expand_shorthand(&mut self) { if !self.args.is_empty() { diff --git a/impl/src/valid.rs b/impl/src/valid.rs index fca3063..b4be8f2 100644 --- a/impl/src/valid.rs +++ b/impl/src/valid.rs @@ -17,6 +17,9 @@ impl Struct<'_> { fn validate(&self) -> Result<()> { check_no_source(&self.attrs)?; find_duplicate_source(&self.fields)?; + for field in &self.fields { + field.validate()?; + } Ok(()) } } @@ -42,6 +45,21 @@ impl Variant<'_> { fn validate(&self) -> Result<()> { check_no_source(&self.attrs)?; 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(()) } } diff --git a/tests/ui/unexpected-field-fmt.rs b/tests/ui/unexpected-field-fmt.rs new file mode 100644 index 0000000..7c439d9 --- /dev/null +++ b/tests/ui/unexpected-field-fmt.rs @@ -0,0 +1,11 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + What { + #[error("...")] + io: std::io::Error, + }, +} + +fn main() {} diff --git a/tests/ui/unexpected-field-fmt.stderr b/tests/ui/unexpected-field-fmt.stderr new file mode 100644 index 0000000..42d80db --- /dev/null +++ b/tests/ui/unexpected-field-fmt.stderr @@ -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("...")] + | ^^^^^^^^^^^^^^^