diff --git a/derive_builder/tests/compile-fail/build_fn_error.stderr b/derive_builder/tests/compile-fail/build_fn_error.stderr index f7b32ba..d7ad106 100644 --- a/derive_builder/tests/compile-fail/build_fn_error.stderr +++ b/derive_builder/tests/compile-fail/build_fn_error.stderr @@ -1,31 +1,23 @@ -error: Unknown field: `path` +error: unrecognized derive_builder attribute --> tests/compile-fail/build_fn_error.rs:4:52 | 4 | #[builder(build_fn(error(validation_error = false, path = "hello")))] | ^^^^ -error: Cannot set `error(validation_error = false)` when using `validate` - --> tests/compile-fail/build_fn_error.rs:10:45 +error: `error(validation_error = false)` and `validate` cannot be used together + --> tests/compile-fail/build_fn_error.rs:10:53 | 10 | #[builder(build_fn(error(validation_error = false), validate = "hello"))] - | ^^^^^ + | ^^^^^^^^^^^^^^^^^^ -error: Unknown field: `path` +error: unrecognized derive_builder attribute --> tests/compile-fail/build_fn_error.rs:16:26 | 16 | #[builder(build_fn(error(path = "hello")))] | ^^^^ -error: Missing field `validation_error` at build_fn/error - --> tests/compile-fail/build_fn_error.rs:15:10 - | -15 | #[derive(Builder)] - | ^^^^^^^ - | - = note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: Missing field `validation_error` - --> tests/compile-fail/build_fn_error.rs:22:20 +error: unexpected end of input, expected nested attribute + --> tests/compile-fail/build_fn_error.rs:22:26 | 22 | #[builder(build_fn(error()))] - | ^^^^^ + | ^ diff --git a/derive_builder/tests/compile-fail/builder_field_custom.stderr b/derive_builder/tests/compile-fail/builder_field_custom.stderr index b9737a9..24f34b2 100644 --- a/derive_builder/tests/compile-fail/builder_field_custom.stderr +++ b/derive_builder/tests/compile-fail/builder_field_custom.stderr @@ -1,23 +1,23 @@ error: #[builder(default)] and #[builder(field(build="..."))] cannot be used together - --> tests/compile-fail/builder_field_custom.rs:8:19 + --> tests/compile-fail/builder_field_custom.rs:9:9 | -8 | default = "1", - | ^^^ +9 | field(build = "self.ipsum.map(|v| v + 42).unwrap_or(100)") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: #[builder(default)] and #[builder(field(ty="..."))] cannot be used together - --> tests/compile-fail/builder_field_custom.rs:14:25 + --> tests/compile-fail/builder_field_custom.rs:14:30 | 14 | #[builder(default = "2", field(ty = "usize"))] - | ^^^ + | ^^^^^^^^^^^^^^^^^^^ error: #[builder(default)] and #[builder(field(build="..."))] cannot be used together - --> tests/compile-fail/builder_field_custom.rs:18:25 + --> tests/compile-fail/builder_field_custom.rs:18:30 | 18 | #[builder(default = "3", field(ty = "usize", build = "self.ipsum + 42"))] - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: #[builder(default)] and #[builder(field(ty="..."))] cannot be used together - --> tests/compile-fail/builder_field_custom.rs:18:25 + --> tests/compile-fail/builder_field_custom.rs:18:30 | 18 | #[builder(default = "3", field(ty = "usize", build = "self.ipsum + 42"))] - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/derive_builder/tests/compile-fail/deny_empty_default.stderr b/derive_builder/tests/compile-fail/deny_empty_default.stderr index df13194..68951ae 100644 --- a/derive_builder/tests/compile-fail/deny_empty_default.stderr +++ b/derive_builder/tests/compile-fail/deny_empty_default.stderr @@ -1,5 +1,5 @@ -error: Unknown literal value `` - --> $DIR/deny_empty_default.rs:8:25 +error: unexpected end of input, expected expression + --> tests/compile-fail/deny_empty_default.rs:8:25 | 8 | #[builder(default = "")] | ^^ diff --git a/derive_builder/tests/compile-fail/rename_setter_struct_level.stderr b/derive_builder/tests/compile-fail/rename_setter_struct_level.stderr index 5e2141b..555fbf9 100644 --- a/derive_builder/tests/compile-fail/rename_setter_struct_level.stderr +++ b/derive_builder/tests/compile-fail/rename_setter_struct_level.stderr @@ -1,4 +1,4 @@ -error: Unknown field: `name` +error: unrecognized derive_builder attribute --> tests/compile-fail/rename_setter_struct_level.rs:7:18 | 7 | #[builder(setter(name = "foo"))] diff --git a/derive_builder/tests/compile-fail/vis_conflict.stderr b/derive_builder/tests/compile-fail/vis_conflict.stderr index c8c2ed8..f892632 100644 --- a/derive_builder/tests/compile-fail/vis_conflict.stderr +++ b/derive_builder/tests/compile-fail/vis_conflict.stderr @@ -1,32 +1,32 @@ -error: `public` and `private` cannot be used together - --> tests/compile-fail/vis_conflict.rs:7:15 +error: this visibility conflicts with a `public` specified earlier + --> tests/compile-fail/vis_conflict.rs:5:19 | -7 | #[builder(public, private)] - | ^^^^^^ +5 | #[builder(public, vis = "pub(crate)")] + | ^^^^^^^^^^^^^^^^^^ -error: `vis="..."` cannot be used with `public` or `private` - --> tests/compile-fail/vis_conflict.rs:5:25 +error: this visibility conflicts with a `public` specified earlier + --> tests/compile-fail/vis_conflict.rs:7:23 | -5 | #[builder(public, vis = "pub(crate)")] - | ^^^^^^^^^^^^ +7 | #[builder(public, private)] + | ^^^^^^^ + +error: this visibility conflicts with a `public` specified earlier + --> tests/compile-fail/vis_conflict.rs:12:19 + | +12 | #[builder(public, vis = "pub(crate)", build_fn(private, public))] + | ^^^^^^^^^^^^^^^^^^ -error: `public` and `private` cannot be used together +error: this visibility conflicts with a `private` specified earlier --> tests/compile-fail/vis_conflict.rs:12:57 | 12 | #[builder(public, vis = "pub(crate)", build_fn(private, public))] | ^^^^^^ -error: `vis="..."` cannot be used with `public` or `private` - --> tests/compile-fail/vis_conflict.rs:14:30 +error: this visibility conflicts with a `private` specified earlier + --> tests/compile-fail/vis_conflict.rs:14:24 | 14 | #[builder(private, vis = "pub")] - | ^^^^^ - -error: `vis="..."` cannot be used with `public` or `private` - --> tests/compile-fail/vis_conflict.rs:12:25 - | -12 | #[builder(public, vis = "pub(crate)", build_fn(private, public))] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^ error[E0433]: failed to resolve: use of undeclared type `ExampleBuilder` --> tests/compile-fail/vis_conflict.rs:19:5 diff --git a/derive_builder_core/Cargo.toml b/derive_builder_core/Cargo.toml index ec9cf2a..0e00b24 100644 --- a/derive_builder_core/Cargo.toml +++ b/derive_builder_core/Cargo.toml @@ -21,10 +21,9 @@ clippy = [] lib_has_std = [] [dependencies] -darling = "0.20.6" proc-macro2 = "1.0.37" quote = "1.0.35" -syn = { version = "2.0.15", features = ["full", "extra-traits"] } +syn = { version = "2.0.49", features = ["full", "extra-traits"] } [dev-dependencies] pretty_assertions = "1.2.1" diff --git a/derive_builder_core/src/block.rs b/derive_builder_core/src/block.rs index 0d53cba..a86836e 100644 --- a/derive_builder_core/src/block.rs +++ b/derive_builder_core/src/block.rs @@ -1,8 +1,9 @@ -use std::convert::TryFrom; - -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use quote::ToTokens; -use syn::{spanned::Spanned, Block, LitStr}; +use syn::{ + meta::ParseNestedMeta, parse::ParseStream, spanned::Spanned, token, Block, Expr, ExprLit, Lit, + Stmt, +}; /// A wrapper for expressions/blocks which automatically adds the start and end /// braces. @@ -13,72 +14,57 @@ use syn::{spanned::Spanned, Block, LitStr}; pub struct BlockContents(Block); impl BlockContents { - pub fn is_empty(&self) -> bool { - self.0.stmts.is_empty() - } - - pub fn span(&self) -> Span { - self.0.span() + #[cfg(test)] + pub(crate) fn new(block: Block) -> Self { + Self(block) } -} -impl ToTokens for BlockContents { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens) + pub(crate) fn parse_nested_meta(meta: &ParseNestedMeta) -> syn::Result { + let expr: Expr = meta.value()?.parse()?; + if let Expr::Lit(ExprLit { + lit: Lit::Str(lit), .. + }) = expr + { + Ok(Self(Block { + brace_token: token::Brace(lit.span()), + stmts: lit.parse_with(parse_nonempty_block)?, + })) + } else { + Ok(Self(Block { + brace_token: token::Brace(expr.span()), + stmts: vec![Stmt::Expr(expr, None)], + })) + } } } -impl TryFrom<&'_ LitStr> for BlockContents { - type Error = syn::Error; - - fn try_from(s: &LitStr) -> Result { - let mut block_str = s.value(); - block_str.insert(0, '{'); - block_str.push('}'); - LitStr::new(&block_str, s.span()).parse().map(Self) +fn parse_nonempty_block(input: ParseStream) -> syn::Result> { + if input.is_empty() { + Err(input.error("expected expression")) + } else { + Block::parse_within(input) } } -impl From for BlockContents { - fn from(v: syn::Expr) -> Self { - Self(Block { - brace_token: syn::token::Brace(v.span()), - stmts: vec![syn::Stmt::Expr(v, None)], - }) - } -} - -impl darling::FromMeta for BlockContents { - fn from_value(value: &syn::Lit) -> darling::Result { - if let syn::Lit::Str(s) = value { - let contents = BlockContents::try_from(s)?; - if contents.is_empty() { - Err(darling::Error::unknown_value("").with_span(s)) - } else { - Ok(contents) - } - } else { - Err(darling::Error::unexpected_lit_type(value)) - } - } - - fn from_expr(expr: &syn::Expr) -> darling::Result { - if let syn::Expr::Lit(lit) = expr { - Self::from_value(&lit.lit) - } else { - Ok(Self::from(expr.clone())) - } +impl ToTokens for BlockContents { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens) } } #[cfg(test)] mod test { - use std::convert::TryInto; - use super::*; + use syn::MetaList; fn parse(s: &str) -> Result { - (&LitStr::new(s, Span::call_site())).try_into() + let mut block_contents = None; + let attr: MetaList = parse_quote!(field(build = #s)); + attr.parse_nested_meta(|meta| { + block_contents = Some(BlockContents::parse_nested_meta(&meta)?); + Ok(()) + }) + .map(|()| block_contents.unwrap()) } #[test] diff --git a/derive_builder_core/src/build_method.rs b/derive_builder_core/src/build_method.rs index 55150b4..58c30ec 100644 --- a/derive_builder_core/src/build_method.rs +++ b/derive_builder_core/src/build_method.rs @@ -159,8 +159,8 @@ macro_rules! default_build_method { #[cfg(test)] mod tests { - #[allow(unused_imports)] use super::*; + use crate::BlockContents; #[test] fn std() { @@ -184,7 +184,7 @@ mod tests { fn default_struct() { let mut build_method = default_build_method!(); let alt_default = - DefaultExpression::explicit::(parse_quote!(Default::default())); + DefaultExpression::Explicit(BlockContents::new(parse_quote!({ Default::default() }))); build_method.default_struct = Some(&alt_default); #[rustfmt::skip] diff --git a/derive_builder_core/src/default_expression.rs b/derive_builder_core/src/default_expression.rs index b4a7208..734d946 100644 --- a/derive_builder_core/src/default_expression.rs +++ b/derive_builder_core/src/default_expression.rs @@ -1,6 +1,6 @@ use crate::BlockContents; -use proc_macro2::Span; use quote::ToTokens; +use syn::{meta::ParseNestedMeta, Token}; /// A `DefaultExpression` can be either explicit or refer to the canonical trait. #[derive(Debug, Clone)] @@ -10,6 +10,15 @@ pub enum DefaultExpression { } impl DefaultExpression { + pub(crate) fn parse_nested_meta(meta: &ParseNestedMeta) -> syn::Result { + if meta.input.peek(Token![=]) { + let block_contents = BlockContents::parse_nested_meta(meta)?; + Ok(DefaultExpression::Explicit(block_contents)) + } else { + Ok(DefaultExpression::Trait) + } + } + /// Add the crate root path so the default expression can be emitted /// to a `TokenStream`. /// @@ -22,28 +31,6 @@ impl DefaultExpression { expr: self, } } - - pub fn span(&self) -> Span { - match self { - DefaultExpression::Explicit(block) => block.span(), - DefaultExpression::Trait => Span::call_site(), - } - } - - #[cfg(test)] - pub fn explicit>(content: I) -> Self { - DefaultExpression::Explicit(content.into()) - } -} - -impl darling::FromMeta for DefaultExpression { - fn from_word() -> darling::Result { - Ok(DefaultExpression::Trait) - } - - fn from_value(value: &syn::Lit) -> darling::Result { - Ok(Self::Explicit(BlockContents::from_value(value)?)) - } } /// Wrapper for `DefaultExpression` diff --git a/derive_builder_core/src/initializer.rs b/derive_builder_core/src/initializer.rs index d609c15..4a5973f 100644 --- a/derive_builder_core/src/initializer.rs +++ b/derive_builder_core/src/initializer.rs @@ -319,7 +319,7 @@ mod tests { #[test] fn default_value() { let mut initializer = default_initializer!(); - let default_value = DefaultExpression::explicit::(parse_quote!(42)); + let default_value = DefaultExpression::Explicit(BlockContents::new(parse_quote!({ 42 }))); initializer.default_value = Some(&default_value); assert_eq!( diff --git a/derive_builder_core/src/lib.rs b/derive_builder_core/src/lib.rs index da5737d..ec186e2 100644 --- a/derive_builder_core/src/lib.rs +++ b/derive_builder_core/src/lib.rs @@ -16,10 +16,6 @@ //! [`derive_builder_core`]: https://!crates.io/crates/derive_builder_core #![deny(warnings, missing_docs)] -#![cfg_attr(test, recursion_limit = "100")] - -#[macro_use] -extern crate darling; extern crate proc_macro; extern crate proc_macro2; @@ -49,7 +45,6 @@ pub(crate) use build_method::BuildMethod; pub(crate) use builder::Builder; pub(crate) use builder_field::{BuilderField, BuilderFieldType}; pub(crate) use change_span::change_span; -use darling::FromDeriveInput; pub(crate) use default_expression::DefaultExpression; pub(crate) use deprecation_notes::DeprecationNotes; pub(crate) use doc_comment::doc_comment_from; @@ -63,9 +58,7 @@ const DEFAULT_STRUCT_NAME: &str = "__default"; pub fn builder_for_struct(ast: syn::DeriveInput) -> proc_macro2::TokenStream { let opts = match macro_options::Options::from_derive_input(&ast) { Ok(val) => val, - Err(err) => { - return err.write_errors(); - } + Err(err) => return err.into_compile_error(), }; let mut builder = opts.as_builder(); diff --git a/derive_builder_core/src/macro_options/darling_opts.rs b/derive_builder_core/src/macro_options/darling_opts.rs index 062e8e5..f92d1ae 100644 --- a/derive_builder_core/src/macro_options/darling_opts.rs +++ b/derive_builder_core/src/macro_options/darling_opts.rs @@ -1,11 +1,13 @@ -use std::{borrow::Cow, vec::IntoIter}; +use std::{borrow::Cow, slice}; +use crate::macro_options::{parse_optional_bool, set, Diagnostic}; use crate::BuildMethod; -use darling::util::{Flag, PathList, SpannedValue}; -use darling::{Error, FromMeta}; use proc_macro2::Span; -use syn::{spanned::Spanned, Attribute, Generics, Ident, Meta, Path}; +use syn::{ + meta::ParseNestedMeta, spanned::Spanned, token, Attribute, Data, Generics, Ident, LitBool, + LitStr, Meta, Path, Visibility, +}; use crate::{ BlockContents, Builder, BuilderField, BuilderFieldType, BuilderPattern, DefaultExpression, @@ -13,74 +15,120 @@ use crate::{ }; /// `derive_builder` uses separate sibling keywords to represent -/// mutually-exclusive visibility states. This trait requires implementers to -/// expose those property values and provides a method to compute any explicit visibility -/// bounds. -trait Visibility { - fn public(&self) -> &Flag; - fn private(&self) -> &Flag; - fn explicit(&self) -> Option<&syn::Visibility>; +/// mutually-exclusive visibility states. +#[derive(Debug)] +enum VisibilityAttr { + /// `public` + Public, + /// `private` + Private, + /// `vis = "pub(crate)"` + Explicit(Visibility), + None, +} - /// Get the explicitly-expressed visibility preference from the attribute. - /// This returns `None` if the input didn't include either keyword. - /// - /// # Panics - /// This method panics if the input specifies both `public` and `private`. - fn as_expressed_vis(&self) -> Option> { - let declares_public = self.public().is_present(); - let declares_private = self.private().is_present(); - let declares_explicit = self.explicit().is_some(); - - if declares_private { - assert!(!declares_public && !declares_explicit); - Some(Cow::Owned(syn::Visibility::Inherited)) - } else if let Some(vis) = self.explicit() { - assert!(!declares_public); - Some(Cow::Borrowed(vis)) - } else if declares_public { - Some(Cow::Owned(syn::parse_quote!(pub))) - } else { - None - } +impl Default for VisibilityAttr { + fn default() -> Self { + Self::None } } -fn no_visibility_conflict(v: &T) -> darling::Result<()> { - let declares_public = v.public().is_present(); - let declares_private = v.private().is_present(); - if let Some(vis) = v.explicit() { - if declares_public || declares_private { - Err( - Error::custom(r#"`vis="..."` cannot be used with `public` or `private`"#) - .with_span(vis), - ) +impl VisibilityAttr { + fn parse_nested_meta( + &mut self, + meta: &ParseNestedMeta, + diag: &mut Diagnostic, + ) -> syn::Result { + if meta.path.is_ident("public") { + self.report_conflict(meta, diag); + *self = Self::Public; + Ok(true) + } else if meta.path.is_ident("private") { + self.report_conflict(meta, diag); + *self = Self::Private; + Ok(true) + } else if meta.path.is_ident("vis") { + let vis: Visibility = meta.value()?.parse::()?.parse()?; + self.report_conflict(meta, diag); + *self = Self::Explicit(vis); + Ok(true) } else { - Ok(()) + Ok(false) + } + } + + fn report_conflict(&self, meta: &ParseNestedMeta, diag: &mut Diagnostic) { + match self { + Self::Public => { + let msg = "this visibility conflicts with a `public` specified earlier"; + diag.push(meta.error(msg)); + } + Self::Private => { + let msg = "this visibility conflicts with a `private` specified earlier"; + diag.push(meta.error(msg)); + } + Self::Explicit(_) => { + let msg = r#"this visibility conflicts with a `vis = "..."` specified earlier"#; + diag.push(meta.error(msg)); + } + Self::None => {} + } + } + + /// Get the explicitly-expressed visibility preference from the attribute. + /// This returns `None` if the input didn't include either keyword. + fn as_expressed_vis(&self) -> Option> { + match self { + Self::Public => Some(Cow::Owned(parse_quote!(pub))), + Self::Private => Some(Cow::Owned(Visibility::Inherited)), + Self::Explicit(vis) => Some(Cow::Borrowed(vis)), + Self::None => None, } - } else if declares_public && declares_private { - Err( - Error::custom(r#"`public` and `private` cannot be used together"#) - .with_span(&v.public().span()), - ) - } else { - Ok(()) } } -#[derive(Debug, Clone, FromMeta)] +#[derive(Debug)] struct BuildFnErrorGenerated { /// Indicates whether or not the generated error should have /// a validation variant that takes a `String` as its contents. - validation_error: SpannedValue, + validation_error: bool, } -#[derive(Debug, Clone)] +#[derive(Debug)] enum BuildFnError { Existing(Path), Generated(BuildFnErrorGenerated), } impl BuildFnError { + fn parse_nested_meta(meta: &ParseNestedMeta, diag: &mut Diagnostic) -> syn::Result { + let lookahead = meta.input.lookahead1(); + if lookahead.peek(Token![=]) { + let path: Path = meta.value()?.parse::()?.parse()?; + return Ok(Self::Existing(path)); + } else if !lookahead.peek(token::Paren) { + return Err(lookahead.error()); + } + + let mut validation_error = None; + + meta.parse_nested_meta(|meta| { + if meta.path.is_ident("validation_error") { + let lit: LitBool = meta.value()?.parse()?; + set(&meta, &mut validation_error, lit.value, diag); + } else { + return Err(meta.error("unrecognized derive_builder attribute")); + } + Ok(()) + })?; + + Ok(Self::Generated(BuildFnErrorGenerated { + validation_error: validation_error.ok_or_else(|| { + syn::Error::new_spanned(&meta.path, "missing attribute `validation_error`") + })?, + })) + } + fn as_existing(&self) -> Option<&Path> { match self { BuildFnError::Existing(p) => Some(p), @@ -96,28 +144,15 @@ impl BuildFnError { } } -impl FromMeta for BuildFnError { - fn from_meta(item: &Meta) -> darling::Result { - match item { - Meta::Path(_) => Err(Error::unsupported_format("word").with_span(item)), - Meta::List(_) => BuildFnErrorGenerated::from_meta(item).map(Self::Generated), - Meta::NameValue(i) => Path::from_expr(&i.value).map(Self::Existing), - } - } -} - /// Options for the `build_fn` property in struct-level builder options. /// There is no inheritance for these settings from struct-level to field-level, /// so we don't bother using `Option` for values in this struct. -#[derive(Debug, Clone, FromMeta)] -#[darling(default, and_then = Self::validation_needs_error)] +#[derive(Debug)] pub struct BuildFn { skip: bool, name: Ident, validate: Option, - public: Flag, - private: Flag, - vis: Option, + vis: VisibilityAttr, /// Either the path to an existing error type that the build method should return or a meta /// list of options to modify the generated error. /// @@ -144,22 +179,58 @@ pub struct BuildFn { } impl BuildFn { - fn validation_needs_error(self) -> darling::Result { - let mut acc = Error::accumulator(); - if self.validate.is_some() { - if let Some(BuildFnError::Generated(e)) = &self.error { - if !*e.validation_error { - acc.push( - Error::custom( - "Cannot set `error(validation_error = false)` when using `validate`", - ) - .with_span(&e.validation_error.span()), - ) + fn parse_nested_meta(meta: &ParseNestedMeta, diag: &mut Diagnostic) -> syn::Result { + let mut skip = None; + let mut name = None; + let mut validate = None; + let mut vis = VisibilityAttr::None; + let mut build_fn_error = None; + + meta.parse_nested_meta(|meta| { + if meta.path.is_ident("skip") { + let value = parse_optional_bool(&meta)?; + set(&meta, &mut skip, value, diag); + } else if meta.path.is_ident("name") { + let value: Ident = meta.value()?.parse::()?.parse()?; + set(&meta, &mut name, value, diag); + } else if meta.path.is_ident("validate") { + let value: Path = meta.value()?.parse::()?.parse()?; + set(&meta, &mut validate, value, diag); + Self::check_validation(&meta, &validate, &build_fn_error, diag); + } else if meta.path.is_ident("error") { + let value = BuildFnError::parse_nested_meta(&meta, diag)?; + set(&meta, &mut build_fn_error, value, diag); + Self::check_validation(&meta, &validate, &build_fn_error, diag); + } else if !vis.parse_nested_meta(&meta, diag)? { + return Err(meta.error("unrecognized derive_builder attribute")); + } + Ok(()) + })?; + + Ok(BuildFn { + skip: skip.unwrap_or(false), + name: name.unwrap_or_else(|| Ident::new("build", Span::call_site())), + validate, + vis, + error: build_fn_error, + }) + } + + fn check_validation( + meta: &ParseNestedMeta, + validate: &Option, + build_fn_error: &Option, + diag: &mut Diagnostic, + ) { + if validate.is_some() { + if let Some(BuildFnError::Generated(e)) = build_fn_error { + if !e.validation_error { + diag.push(meta.error( + "`error(validation_error = false)` and `validate` cannot be used together", + )); } } } - - acc.finish_with(self) } } @@ -169,82 +240,74 @@ impl Default for BuildFn { skip: false, name: Ident::new("build", Span::call_site()), validate: None, - public: Default::default(), - private: Default::default(), - vis: None, + vis: VisibilityAttr::None, error: None, } } } -impl Visibility for BuildFn { - fn public(&self) -> &Flag { - &self.public - } - - fn private(&self) -> &Flag { - &self.private - } - - fn explicit(&self) -> Option<&syn::Visibility> { - self.vis.as_ref() - } -} - /// Contents of the `field` meta in `builder` attributes at the struct level. -#[derive(Debug, Clone, Default, FromMeta)] +#[derive(Debug, Default)] pub struct StructLevelFieldMeta { - public: Flag, - private: Flag, - vis: Option, + vis: VisibilityAttr, } -impl Visibility for StructLevelFieldMeta { - fn public(&self) -> &Flag { - &self.public - } +impl StructLevelFieldMeta { + fn parse_nested_meta(meta: &ParseNestedMeta, diag: &mut Diagnostic) -> syn::Result { + let mut vis = VisibilityAttr::None; - fn private(&self) -> &Flag { - &self.private - } + meta.parse_nested_meta(|meta| { + if !vis.parse_nested_meta(&meta, diag)? { + return Err(meta.error("unrecognized derive_builder attribute")); + } + Ok(()) + })?; - fn explicit(&self) -> Option<&syn::Visibility> { - self.vis.as_ref() + Ok(StructLevelFieldMeta { vis }) } } /// Contents of the `field` meta in `builder` attributes at the field level. // // This is a superset of the attributes permitted in `field` at the struct level. -// Perhaps in the future we will be able to use `#[darling(flatten)]`, but -// that does not exist right now: https://github.com/TedDriggs/darling/issues/146 -#[derive(Debug, Clone, Default, FromMeta)] +// Perhaps the data structures can be refactored to share common parts. +#[derive(Debug, Default)] pub struct FieldLevelFieldMeta { - public: Flag, - private: Flag, - vis: Option, + vis: VisibilityAttr, /// Custom builder field type - #[darling(rename = "ty")] builder_type: Option, /// Custom builder field method, for making target struct field value build: Option, } -impl Visibility for FieldLevelFieldMeta { - fn public(&self) -> &Flag { - &self.public - } - - fn private(&self) -> &Flag { - &self.private - } +impl FieldLevelFieldMeta { + fn parse_nested_meta(meta: &ParseNestedMeta, diag: &mut Diagnostic) -> syn::Result { + let mut vis = VisibilityAttr::None; + let mut builder_type = None; + let mut build = None; + + meta.parse_nested_meta(|meta| { + if meta.path.is_ident("ty") || meta.path.is_ident("type") { + let value: syn::Type = meta.value()?.parse::()?.parse()?; + set(&meta, &mut builder_type, value, diag); + } else if meta.path.is_ident("build") { + let value = BlockContents::parse_nested_meta(&meta)?; + set(&meta, &mut build, value, diag); + } else if !vis.parse_nested_meta(&meta, diag)? { + return Err(meta.error("unrecognized derive_builder attribute")); + } + Ok(()) + })?; - fn explicit(&self) -> Option<&syn::Visibility> { - self.vis.as_ref() + Ok(FieldLevelFieldMeta { + vis, + builder_type, + build, + }) } } -#[derive(Debug, Clone, Default, FromMeta)] +#[derive(Debug, Default)] pub struct StructLevelSetter { prefix: Option, into: Option, @@ -253,6 +316,39 @@ pub struct StructLevelSetter { } impl StructLevelSetter { + fn parse_nested_meta(meta: &ParseNestedMeta, diag: &mut Diagnostic) -> syn::Result { + let mut prefix = None; + let mut into = None; + let mut strip_option = None; + let mut skip = None; + + meta.parse_nested_meta(|meta| { + if meta.path.is_ident("prefix") { + let value = meta.value()?.parse::()?.parse()?; + set(&meta, &mut prefix, value, diag); + } else if meta.path.is_ident("into") { + let value = parse_optional_bool(&meta)?; + set(&meta, &mut into, value, diag); + } else if meta.path.is_ident("strip_option") { + let value = parse_optional_bool(&meta)?; + set(&meta, &mut strip_option, value, diag); + } else if meta.path.is_ident("skip") { + let value = parse_optional_bool(&meta)?; + set(&meta, &mut skip, value, diag); + } else { + return Err(meta.error("unrecognized derive_builder attribute")); + } + Ok(()) + })?; + + Ok(StructLevelSetter { + prefix, + into, + strip_option, + skip, + }) + } + /// Check if setters are explicitly enabled or disabled at /// the struct level. pub fn enabled(&self) -> Option { @@ -260,27 +356,10 @@ impl StructLevelSetter { } } -/// Create `Each` from an attribute's `Meta`. -/// -/// Two formats are supported: -/// -/// * `each = "..."`, which provides the name of the `each` setter and otherwise uses default values -/// * `each(name = "...")`, which allows setting additional options on the `each` setter -fn parse_each(meta: &Meta) -> darling::Result> { - if let Meta::NameValue(mnv) = meta { - Ident::from_meta(meta) - .map(Each::from) - .map(Some) - .map_err(|e| e.with_span(&mnv.value)) - } else { - Each::from_meta(meta).map(Some) - } -} - /// The `setter` meta item on fields in the input type. /// Unlike the `setter` meta item at the struct level, this allows specific /// name overrides. -#[derive(Debug, Clone, Default, FromMeta)] +#[derive(Debug, Default)] pub struct FieldLevelSetter { prefix: Option, name: Option, @@ -288,11 +367,68 @@ pub struct FieldLevelSetter { strip_option: Option, skip: Option, custom: Option, - #[darling(with = parse_each)] each: Option, } impl FieldLevelSetter { + fn parse_nested_meta(meta: &ParseNestedMeta, diag: &mut Diagnostic) -> syn::Result { + if !meta.input.peek(token::Paren) { + // `setter` as a word is equivalent to `setter(skip = false)`. This + // is useful for re-enabling setter for one field when they've been + // disabled at the struct level. + return Ok(FieldLevelSetter { + skip: Some(false), + ..Default::default() + }); + } + + let mut prefix = None; + let mut name = None; + let mut into = None; + let mut strip_option = None; + let mut skip = None; + let mut custom = None; + let mut each = None; + + meta.parse_nested_meta(|meta| { + if meta.path.is_ident("prefix") { + let value: Ident = meta.value()?.parse::()?.parse()?; + set(&meta, &mut prefix, value, diag); + } else if meta.path.is_ident("name") { + let value: Ident = meta.value()?.parse::()?.parse()?; + set(&meta, &mut name, value, diag); + } else if meta.path.is_ident("into") { + let value = parse_optional_bool(&meta)?; + set(&meta, &mut into, value, diag); + } else if meta.path.is_ident("strip_option") { + let value = parse_optional_bool(&meta)?; + set(&meta, &mut strip_option, value, diag); + } else if meta.path.is_ident("skip") { + let value = parse_optional_bool(&meta)?; + set(&meta, &mut skip, value, diag); + } else if meta.path.is_ident("custom") { + let value = parse_optional_bool(&meta)?; + set(&meta, &mut custom, value, diag); + } else if meta.path.is_ident("each") { + let value = Each::parse_nested_meta(&meta, diag)?; + set(&meta, &mut each, value, diag); + } else { + return Err(meta.error("unrecognized derive_builder attribute")); + } + Ok(()) + })?; + + Ok(FieldLevelSetter { + prefix, + name, + into, + strip_option, + skip, + custom, + each, + }) + } + /// Get whether the setter should be emitted. The rules are the same as /// for `field_enabled`, except we only skip the setter if `setter(custom)` is present. pub fn setter_enabled(&self) -> Option { @@ -325,47 +461,18 @@ impl FieldLevelSetter { } } -/// `derive_builder` allows the calling code to use `setter` as a word to enable -/// setters when they've been disabled at the struct level. -fn field_setter(meta: &Meta) -> darling::Result { - // it doesn't matter what the path is; the fact that this function - // has been called means that a valueless path is the shorthand case. - if let Meta::Path(_) = meta { - Ok(FieldLevelSetter { - skip: Some(false), - ..Default::default() - }) - } else { - FieldLevelSetter::from_meta(meta) - } -} - /// Data extracted from the fields of the input struct. -#[derive(Debug, Clone, FromField)] -#[darling( - attributes(builder), - forward_attrs(doc, cfg, allow, builder_field_attr, builder_setter_attr), - and_then = "Self::resolve" -)] +#[derive(Debug)] pub struct Field { ident: Option, - /// Raw input attributes, for consumption by Field::unnest_attrs. Do not use elsewhere. - attrs: Vec, ty: syn::Type, /// Field-level override for builder pattern. /// Note that setting this may force the builder to derive `Clone`. pattern: Option, - public: Flag, - private: Flag, /// Declared visibility for the field in the builder, e.g. `#[builder(vis = "...")]`. - /// - /// This cannot be named `vis` or `darling` would put the deriving field's visibility into the - /// field instead. - #[darling(rename = "vis")] - visibility: Option, - // See the documentation for `FieldSetterMeta` to understand how `darling` - // is interpreting this field. - #[darling(default, with = field_setter)] + vis: VisibilityAttr, + /// `derive_builder` allows the calling code to use `setter` as a word to enable + /// setters when they've been disabled at the struct level. setter: FieldLevelSetter, /// The value for this field if the setter is never invoked. /// @@ -378,203 +485,134 @@ pub struct Field { /// /// This property only captures the first two, the third is computed in `FieldWithDefaults`. default: Option, - try_setter: Flag, - #[darling(default)] + try_setter: bool, field: FieldLevelFieldMeta, - #[darling(skip)] field_attrs: Vec, - #[darling(skip)] setter_attrs: Vec, } impl Field { - fn no_visibility_conflicts(&self) -> darling::Result<()> { - let mut errors = Error::accumulator(); - errors.handle(no_visibility_conflict(&self.field)); - errors.handle(no_visibility_conflict(self)); - errors.finish() - } + fn from_field(ast: &syn::Field, diag: &mut Diagnostic) -> syn::Result { + let mut pattern = None; + let mut vis = VisibilityAttr::None; + let mut setter = None; + let mut default = None; + let mut try_setter = None; + let mut field = None; + let mut field_attrs = Vec::new(); + let mut setter_attrs = Vec::new(); + + for attr in &ast.attrs { + if attr.path().is_ident("builder") { + if let Err(err) = attr.parse_nested_meta(|meta| { + if meta.path.is_ident("pattern") { + let value = BuilderPattern::parse_nested_meta(&meta, diag)?; + set(&meta, &mut pattern, value, diag); + } else if meta.path.is_ident("setter") { + let value = FieldLevelSetter::parse_nested_meta(&meta, diag)?; + set(&meta, &mut setter, value, diag) + } else if meta.path.is_ident("default") { + let value = DefaultExpression::parse_nested_meta(&meta)?; + set(&meta, &mut default, value, diag); + Self::check_field_vs_default(&meta, &field, &default, diag); + } else if meta.path.is_ident("try_setter") { + set(&meta, &mut try_setter, true, diag); + } else if meta.path.is_ident("field") { + let value = FieldLevelFieldMeta::parse_nested_meta(&meta, diag)?; + set(&meta, &mut field, value, diag); + Self::check_field_vs_default(&meta, &field, &default, diag); + } else if !vis.parse_nested_meta(&meta, diag)? { + return Err(meta.error("unrecognized derive_builder attribute")); + } + Ok(()) + }) { + diag.push(err); + } + } else if attr.path().is_ident("doc") + || attr.path().is_ident("cfg") + || attr.path().is_ident("allow") + { + field_attrs.push(attr.clone()); + setter_attrs.push(attr.clone()); + } else if attr.path().is_ident("builder_field_attr") { + unnest_attr(attr, &mut field_attrs, diag); + } else if attr.path().is_ident("builder_setter_attr") { + unnest_attr(attr, &mut setter_attrs, diag); + } + } - /// Resolve and check (post-parsing) options which come from multiple darling options - /// - /// * Check that we don't have a custom field type or builder *and* a default value - /// * Populate `self.field_attrs` and `self.setter_attrs` by draining `self.attrs` - fn resolve(mut self) -> darling::Result { - let mut errors = darling::Error::accumulator(); + Ok(Field { + ident: ast.ident.clone(), + ty: ast.ty.clone(), + pattern, + vis, + setter: setter.unwrap_or_default(), + default, + try_setter: try_setter.unwrap_or(false), + field: field.unwrap_or_default(), + field_attrs, + setter_attrs, + }) + } + /// Check that we don't have a custom field type or builder *and* a default value. + fn check_field_vs_default( + meta: &ParseNestedMeta, + field: &Option, + default: &Option, + diag: &mut Diagnostic, + ) { // `default` can be preempted by properties in `field`. Silently ignoring a // `default` could cause the direct user of `derive_builder` to see unexpected // behavior from the builder, so instead we require that the deriving struct // not pass any ignored instructions. - if let Field { - default: Some(field_default), - .. - } = &self - { + if let (Some(field), Some(_default)) = (field, default) { // `field.build` is stronger than `default`, as it contains both instructions on how to // deal with a missing value and conversions to do on the value during target type // construction. - if self.field.build.is_some() { - errors.push( - darling::Error::custom( - r#"#[builder(default)] and #[builder(field(build="..."))] cannot be used together"#, - ) - .with_span(&field_default.span()), - ); + if field.build.is_some() { + diag.push(meta.error( + r#"#[builder(default)] and #[builder(field(build="..."))] cannot be used together"#, + )); } // `field.ty` being set means `default` will not be used, since we don't know how // to check a custom field type for the absence of a value and therefore we'll never // know that we should use the `default` value. - if self.field.builder_type.is_some() { - errors.push( - darling::Error::custom( - r#"#[builder(default)] and #[builder(field(ty="..."))] cannot be used together"#, - ) - .with_span(&field_default.span()) - ) - } - }; - - errors.handle(distribute_and_unnest_attrs( - &mut self.attrs, - &mut [ - ("builder_field_attr", &mut self.field_attrs), - ("builder_setter_attr", &mut self.setter_attrs), - ], - )); - - errors.finish_with(self) - } -} - -/// Divide a list of attributes into multiple partially-overlapping output lists. -/// -/// Some attributes from the macro input will be added to the output in multiple places; -/// for example, a `cfg` attribute must be replicated to both the struct and its impl block or -/// the resulting code will not compile. -/// -/// Other attributes are scoped to a specific output by their path, e.g. `builder_field_attr`. -/// These attributes will only appear in one output list, but need that outer path removed. -/// -/// For performance reasons, we want to do this in one pass through the list instead of -/// first distributing and then iterating through each of the output lists. -/// -/// Each item in `outputs` contains the attribute name unique to that output, and the `Vec` where all attributes for that output should be inserted. -/// Attributes whose path matches any value in `outputs` will be added only to the first matching one, and will be "unnested". -/// Other attributes are not unnested, and simply copied for each decoratee. -fn distribute_and_unnest_attrs( - input: &mut Vec, - outputs: &mut [(&'static str, &mut Vec)], -) -> darling::Result<()> { - let mut errors = vec![]; - - for (name, list) in &*outputs { - assert!(list.is_empty(), "Output Vec for '{}' was not empty", name); - } - - for attr in input.drain(..) { - let destination = outputs - .iter_mut() - .find(|(ptattr, _)| attr.path().is_ident(ptattr)); - - if let Some((_, destination)) = destination { - match unnest_from_one_attribute(attr) { - Ok(n) => destination.push(n), - Err(e) => errors.push(e), - } - } else { - for (_, output) in outputs.iter_mut() { - output.push(attr.clone()); + if field.builder_type.is_some() { + diag.push(meta.error( + r#"#[builder(default)] and #[builder(field(ty="..."))] cannot be used together"#, + )); } } } - - if !errors.is_empty() { - return Err(darling::Error::multiple(errors)); - } - - Ok(()) } -fn unnest_from_one_attribute(attr: syn::Attribute) -> darling::Result { - match &attr.style { - syn::AttrStyle::Outer => (), - syn::AttrStyle::Inner(bang) => { - return Err(darling::Error::unsupported_format(&format!( - "{} must be an outer attribute", - attr.path() - .get_ident() - .map(Ident::to_string) - .unwrap_or_else(|| "Attribute".to_string()) - )) - .with_span(bang)); - } - }; - - let original_span = attr.span(); - - let pound = attr.pound_token; - let meta = attr.meta; - - match meta { - Meta::Path(_) => Err(Error::unsupported_format("word").with_span(&meta)), - Meta::NameValue(_) => Err(Error::unsupported_format("name-value").with_span(&meta)), - Meta::List(list) => { - let inner = list.tokens; - Ok(parse_quote_spanned!(original_span=> #pound [ #inner ])) - } +/// Convert an attribute like `#[builder_struct_attr(doc(hidden))]` into `#[doc(hidden)]`. +fn unnest_attr(attr: &Attribute, out: &mut Vec, diag: &mut Diagnostic) { + match attr.parse_args() { + Ok(meta) => out.push(Attribute { + pound_token: attr.pound_token, + style: attr.style, + bracket_token: attr.bracket_token, + meta, + }), + Err(err) => diag.push(err), } } -impl Visibility for Field { - fn public(&self) -> &Flag { - &self.public - } - - fn private(&self) -> &Flag { - &self.private - } - - fn explicit(&self) -> Option<&syn::Visibility> { - self.visibility.as_ref() - } -} - -fn default_crate_root() -> Path { - parse_quote!(::derive_builder) -} - -fn default_create_empty() -> Ident { - Ident::new("create_empty", Span::call_site()) -} - -#[derive(Debug, Clone, FromDeriveInput)] -#[darling( - attributes(builder), - forward_attrs(cfg, allow, builder_struct_attr, builder_impl_attr), - supports(struct_named), - and_then = Self::unnest_attrs -)] +#[derive(Debug)] pub struct Options { ident: Ident, - /// DO NOT USE. - /// - /// Initial receiver for forwarded attributes from the struct; these are split - /// into `Options::struct_attrs` and `Options::impl_attrs` before `FromDeriveInput` - /// returns. - attrs: Vec, - - #[darling(skip)] struct_attrs: Vec, - #[darling(skip)] impl_attrs: Vec, - /// The visibility of the deriving struct. Do not confuse this with `#[builder(vis = "...")]`, - /// which is received by `Options::visibility`. - vis: syn::Visibility, + /// The visibility of the deriving struct. + /// + /// Do not confuse this with `builder_vis` which is the visibility received by `#[builder(vis = "...")]`, + struct_vis: Visibility, generics: Generics, @@ -583,97 +621,163 @@ pub struct Options { /// The path to the root of the derive_builder crate used in generated /// code. - #[darling(rename = "crate", default = default_crate_root)] crate_root: Path, - #[darling(default)] pattern: BuilderPattern, - #[darling(default)] build_fn: BuildFn, /// Additional traits to derive on the builder. - #[darling(default)] - derive: PathList, + derive: Vec, - custom_constructor: Flag, + custom_constructor: bool, /// The ident of the inherent method which takes no arguments and returns /// an instance of the builder with all fields empty. - #[darling(default = default_create_empty)] create_empty: Ident, /// Setter options applied to all field setters in the struct. - #[darling(default)] setter: StructLevelSetter, /// Struct-level value to use in place of any unfilled fields default: Option, - public: Flag, - - private: Flag, - /// Desired visibility of the builder struct. /// - /// Do not confuse this with `Options::vis`, which is the visibility of the deriving struct. - #[darling(rename = "vis")] - visibility: Option, + /// Do not confuse this with `struct_vis`, which is the visibility of the deriving struct. + builder_vis: VisibilityAttr, /// The parsed body of the derived struct. - data: darling::ast::Data, + data: Vec, - no_std: Flag, + no_std: bool, /// When present, emit additional fallible setters alongside each regular /// setter. - try_setter: Flag, + try_setter: bool, - #[darling(default)] field: StructLevelFieldMeta, - #[darling(skip, default)] deprecation_notes: DeprecationNotes, } -impl Visibility for Options { - fn public(&self) -> &Flag { - &self.public - } - - fn private(&self) -> &Flag { - &self.private - } +impl Options { + pub(crate) fn from_derive_input(ast: &syn::DeriveInput) -> syn::Result { + let diag = &mut Diagnostic::new(); + + let mut struct_attrs = Vec::new(); + let mut impl_attrs = Vec::new(); + let mut name = None; + let mut crate_root = None; + let mut pattern = None; + let mut build_fn = None; + let mut derive = None; + let mut custom_constructor = None; + let mut create_empty = None; + let mut setter = None; + let mut default = None; + let mut builder_vis = VisibilityAttr::None; + let mut data = Vec::new(); + let mut no_std = None; + let mut try_setter = None; + let mut field = None; + + for attr in &ast.attrs { + if attr.path().is_ident("builder") { + if let Meta::Path(_) = attr.meta { + continue; // Ignore empty #[builder], which would otherwise be an error. + } + if let Err(err) = attr.parse_nested_meta(|meta| { + if meta.path.is_ident("name") { + let value: Ident = meta.value()?.parse::()?.parse()?; + set(&meta, &mut name, value, diag); + } else if meta.path.is_ident("crate") { + let value: Path = meta.value()?.parse::()?.parse()?; + set(&meta, &mut crate_root, value, diag); + } else if meta.path.is_ident("pattern") { + let value = BuilderPattern::parse_nested_meta(&meta, diag)?; + set(&meta, &mut pattern, value, diag); + } else if meta.path.is_ident("build_fn") { + let value = BuildFn::parse_nested_meta(&meta, diag)?; + set(&meta, &mut build_fn, value, diag); + } else if meta.path.is_ident("derive") { + let mut value = Vec::new(); + meta.parse_nested_meta(|meta| { + value.push(meta.path); + Ok(()) + })?; + set(&meta, &mut derive, value, diag); + } else if meta.path.is_ident("custom_constructor") { + set(&meta, &mut custom_constructor, true, diag); + } else if meta.path.is_ident("create_empty") { + let value: Ident = meta.value()?.parse::()?.parse()?; + set(&meta, &mut create_empty, value, diag); + } else if meta.path.is_ident("setter") { + let value = StructLevelSetter::parse_nested_meta(&meta, diag)?; + set(&meta, &mut setter, value, diag); + } else if meta.path.is_ident("default") { + let value = DefaultExpression::parse_nested_meta(&meta)?; + set(&meta, &mut default, value, diag); + } else if meta.path.is_ident("no_std") { + set(&meta, &mut no_std, true, diag); + } else if meta.path.is_ident("try_setter") { + set(&meta, &mut try_setter, true, diag); + } else if meta.path.is_ident("field") { + let value = StructLevelFieldMeta::parse_nested_meta(&meta, diag)?; + set(&meta, &mut field, value, diag); + } else if !builder_vis.parse_nested_meta(&meta, diag)? { + return Err(meta.error("unrecognized derive_builder attribute")); + } + Ok(()) + }) { + diag.push(err); + } + } else if attr.path().is_ident("cfg") || attr.path().is_ident("allow") { + struct_attrs.push(attr.clone()); + impl_attrs.push(attr.clone()); + } else if attr.path().is_ident("builder_struct_attr") { + unnest_attr(attr, &mut struct_attrs, diag); + } else if attr.path().is_ident("builder_impl_attr") { + unnest_attr(attr, &mut impl_attrs, diag); + } + } - fn explicit(&self) -> Option<&syn::Visibility> { - self.visibility.as_ref() - } -} + if let Data::Struct(ast) = &ast.data { + for field in &ast.fields { + let field = Field::from_field(field, diag)?; + data.push(field); + } + } else { + let msg = "this derive macro requires a struct with named fields"; + diag.push(syn::Error::new(Span::call_site(), msg)); + } -impl Options { - /// Populate `self.struct_attrs` and `self.impl_attrs` by draining `self.attrs` - fn unnest_attrs(mut self) -> darling::Result { - let mut errors = Error::accumulator(); - - errors.handle(distribute_and_unnest_attrs( - &mut self.attrs, - &mut [ - ("builder_struct_attr", &mut self.struct_attrs), - ("builder_impl_attr", &mut self.impl_attrs), - ], - )); - - // Check for conflicting visibility declarations. These cannot be pushed - // down into `FieldMeta` et al because of the call to `no_visibility_conflict(&self)`, - // as all sub-fields must be valid for this `Options` function to run. - errors.handle(no_visibility_conflict(&self.field)); - errors.handle(no_visibility_conflict(&self.build_fn)); - self.data - .as_ref() - .map_struct_fields(|f| errors.handle(f.no_visibility_conflicts())); - errors.handle(no_visibility_conflict(&self)); + if let Some(error) = diag.take() { + return Err(error); + } - errors.finish_with(self) + Ok(Options { + ident: ast.ident.clone(), + struct_attrs, + impl_attrs, + struct_vis: ast.vis.clone(), + generics: ast.generics.clone(), + name, + crate_root: crate_root.unwrap_or_else(|| parse_quote!(::derive_builder)), + pattern: pattern.unwrap_or_default(), + build_fn: build_fn.unwrap_or_default(), + derive: derive.unwrap_or_default(), + custom_constructor: custom_constructor.unwrap_or(false), + create_empty: create_empty.unwrap_or_else(|| parse_quote!(create_empty)), + setter: setter.unwrap_or_default(), + default, + builder_vis, + data, + no_std: no_std.unwrap_or(false), + try_setter: try_setter.unwrap_or(false), + field: field.unwrap_or_default(), + deprecation_notes: DeprecationNotes::default(), + }) } } @@ -700,26 +804,21 @@ impl Options { /// The visibility of the builder struct. /// If a visibility was declared in attributes, that will be used; /// otherwise the struct's own visibility will be used. - pub fn builder_vis(&self) -> Cow { - self.as_expressed_vis().unwrap_or(Cow::Borrowed(&self.vis)) + pub fn builder_vis(&self) -> Cow { + self.builder_vis + .as_expressed_vis() + .unwrap_or(Cow::Borrowed(&self.struct_vis)) } /// Get the visibility of the emitted `build` method. /// This defaults to the visibility of the parent builder, but can be overridden. - pub fn build_method_vis(&self) -> Cow { + pub fn build_method_vis(&self) -> Cow { self.build_fn + .vis .as_expressed_vis() .unwrap_or_else(|| self.builder_vis()) } - pub fn raw_fields(&self) -> Vec<&Field> { - self.data - .as_ref() - .take_struct() - .expect("Only structs supported") - .fields - } - /// A builder requires `Clone` to be derived if its build method or any of its setters /// use the mutable or immutable pattern. pub fn requires_clone(&self) -> bool { @@ -729,11 +828,11 @@ impl Options { /// Get an iterator over the input struct's fields which pulls fallback /// values from struct-level settings. pub fn fields(&self) -> FieldIter { - FieldIter(self, self.raw_fields().into_iter()) + FieldIter(self, self.data.iter()) } pub fn field_count(&self) -> usize { - self.raw_fields().len() + self.data.len() } } @@ -748,7 +847,7 @@ impl Options { derives: &self.derive, struct_attrs: &self.struct_attrs, impl_attrs: &self.impl_attrs, - impl_default: !self.custom_constructor.is_present(), + impl_default: !self.custom_constructor, create_empty: self.create_empty.clone(), generics: Some(&self.generics), visibility: self.builder_vis(), @@ -766,13 +865,13 @@ impl Options { .error .as_ref() .and_then(BuildFnError::as_generated) - .map(|e| *e.validation_error) + .map(|e| e.validation_error) .unwrap_or(true), no_alloc: cfg!(not(any(feature = "alloc", feature = "lib_has_std"))), must_derive_clone: self.requires_clone(), doc_comment: None, deprecation_notes: Default::default(), - std: !self.no_std.is_present(), + std: !self.no_std, } } @@ -825,7 +924,7 @@ impl<'a> FieldWithDefaults<'a> { /// Check if this field should emit a fallible setter. /// This depends on the `TryFrom` trait, which hasn't yet stabilized. pub fn try_setter(&self) -> bool { - self.field.try_setter.is_present() || self.parent.try_setter.is_present() + self.field.try_setter || self.parent.try_setter } /// Get the prefix that should be applied to the field name to produce @@ -874,10 +973,11 @@ impl<'a> FieldWithDefaults<'a> { } /// Get the visibility of the emitted setter, if there will be one. - pub fn setter_vis(&self) -> Cow { + pub fn setter_vis(&self) -> Cow { self.field + .vis .as_expressed_vis() - .or_else(|| self.parent.as_expressed_vis()) + .or_else(|| self.parent.builder_vis.as_expressed_vis()) .unwrap_or_else(|| Cow::Owned(syn::parse_quote!(pub))) } @@ -890,9 +990,10 @@ impl<'a> FieldWithDefaults<'a> { .expect("Tuple structs are not supported") } - pub fn field_vis(&self) -> Cow { + pub fn field_vis(&self) -> Cow { self.field .field + .vis .as_expressed_vis() .or_else( // Disabled fields become a PhantomData in the builder. We make that field @@ -902,12 +1003,12 @@ impl<'a> FieldWithDefaults<'a> { if self.field_enabled() { None } else { - Some(Cow::Owned(syn::Visibility::Inherited)) + Some(Cow::Owned(Visibility::Inherited)) } }, ) - .or_else(|| self.parent.field.as_expressed_vis()) - .unwrap_or(Cow::Owned(syn::Visibility::Inherited)) + .or_else(|| self.parent.field.vis.as_expressed_vis()) + .unwrap_or(Cow::Owned(Visibility::Inherited)) } pub fn field_type(&'a self) -> BuilderFieldType<'a> { @@ -996,7 +1097,7 @@ impl<'a> FieldWithDefaults<'a> { } } -pub struct FieldIter<'a>(&'a Options, IntoIter<&'a Field>); +pub struct FieldIter<'a>(&'a Options, slice::Iter<'a, Field>); impl<'a> Iterator for FieldIter<'a> { type Item = FieldWithDefaults<'a>; diff --git a/derive_builder_core/src/macro_options/diagnostic.rs b/derive_builder_core/src/macro_options/diagnostic.rs new file mode 100644 index 0000000..8465b4d --- /dev/null +++ b/derive_builder_core/src/macro_options/diagnostic.rs @@ -0,0 +1,21 @@ +pub struct Diagnostic { + err: Option, +} + +impl Diagnostic { + pub fn new() -> Self { + Diagnostic { err: None } + } + + pub fn push(&mut self, err: syn::Error) { + if let Some(prev) = &mut self.err { + prev.combine(err); + } else { + self.err = Some(err); + } + } + + pub fn take(&mut self) -> Option { + self.err.take() + } +} diff --git a/derive_builder_core/src/macro_options/mod.rs b/derive_builder_core/src/macro_options/mod.rs index 84e091c..bd5438d 100644 --- a/derive_builder_core/src/macro_options/mod.rs +++ b/derive_builder_core/src/macro_options/mod.rs @@ -12,5 +12,34 @@ //! `FieldOptions` instances. mod darling_opts; +mod diagnostic; + +use syn::meta::ParseNestedMeta; +use syn::{LitBool, LitStr}; pub use self::darling_opts::Options; +pub use self::diagnostic::Diagnostic; + +pub(crate) fn set(meta: &ParseNestedMeta, out: &mut Option, value: T, diag: &mut Diagnostic) { + if out.is_some() { + diag.push(meta.error("duplicate attribute")); + } else { + *out = Some(value); + } +} + +/// Parse boolean value for a nested attribute in one of the following forms: +/// `k = true`, `k = "true"`, or just `k` which means true. +pub(crate) fn parse_optional_bool(meta: &ParseNestedMeta) -> syn::Result { + if meta.input.peek(Token![=]) { + let value = meta.value()?; + let lit: LitBool = if value.peek(LitStr) { + value.parse::()?.parse()? + } else { + value.parse()? + }; + Ok(lit.value) + } else { + Ok(true) + } +} diff --git a/derive_builder_core/src/options.rs b/derive_builder_core/src/options.rs index c4d3ba9..bdc24a5 100644 --- a/derive_builder_core/src/options.rs +++ b/derive_builder_core/src/options.rs @@ -1,9 +1,13 @@ +use crate::macro_options::{parse_optional_bool, set, Diagnostic}; +use syn::meta::ParseNestedMeta; +use syn::{token, Ident, LitStr}; + /// Controls the signature of a setter method, /// more specifically how `self` is passed and returned. /// /// It can also be generalized to methods with different parameter sets and /// return types, e.g. the `build()` method. -#[derive(PartialEq, Eq, Debug, Clone, Copy, FromMeta)] +#[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum BuilderPattern { /// E.g. `fn bar(self, bar: Bar) -> Self`. Owned, @@ -20,6 +24,23 @@ pub enum BuilderPattern { } impl BuilderPattern { + pub(crate) fn parse_nested_meta( + meta: &ParseNestedMeta, + diag: &mut Diagnostic, + ) -> syn::Result { + let lit: LitStr = meta.value()?.parse()?; + Ok(match lit.value().as_str() { + "owned" => BuilderPattern::Owned, + "mutable" => BuilderPattern::Mutable, + "immutable" => BuilderPattern::Immutable, + unknown => { + let msg = format!("unknown literal value `{}`", unknown); + diag.push(syn::Error::new(lit.span(), msg)); + Self::default() + } + }) + } + /// Returns true if this style of builder needs to be able to clone its /// fields during the `build` method. pub fn requires_clone(&self) -> bool { @@ -34,15 +55,51 @@ impl Default for BuilderPattern { } } -#[derive(Debug, Clone, FromMeta)] +#[derive(Debug, Clone)] pub struct Each { pub name: syn::Ident, - #[darling(default)] pub into: bool, } -impl From for Each { - fn from(name: syn::Ident) -> Self { - Self { name, into: false } +impl Each { + /// Create `Each` from an attribute's `Meta`. + /// + /// Two formats are supported: + /// + /// * `each = "..."`, which provides the name of the `each` setter and otherwise uses default values + /// * `each(name = "...")`, which allows setting additional options on the `each` setter + pub(crate) fn parse_nested_meta( + meta: &ParseNestedMeta, + diag: &mut Diagnostic, + ) -> syn::Result { + let lookahead = meta.input.lookahead1(); + if lookahead.peek(Token![=]) { + let name: Ident = meta.value()?.parse::()?.parse()?; + return Ok(Each { name, into: false }); + } else if !lookahead.peek(token::Paren) { + return Err(lookahead.error()); + } + + let mut name: Option = None; + let mut into: Option = None; + + meta.parse_nested_meta(|meta| { + if meta.path.is_ident("name") { + let value: Ident = meta.value()?.parse::()?.parse()?; + set(&meta, &mut name, value, diag); + } else if meta.path.is_ident("into") { + let value = parse_optional_bool(&meta)?; + set(&meta, &mut into, value, diag); + } else { + return Err(meta.error("unrecognized derive_builder attribute")); + } + Ok(()) + })?; + + Ok(Each { + name: name + .ok_or_else(|| syn::Error::new_spanned(&meta.path, "missing attribute `name`"))?, + into: into.unwrap_or(false), + }) } } diff --git a/derive_builder_macro/Cargo.toml b/derive_builder_macro/Cargo.toml index d419fd3..34d9002 100644 --- a/derive_builder_macro/Cargo.toml +++ b/derive_builder_macro/Cargo.toml @@ -28,4 +28,4 @@ lib_has_std = ["derive_builder_core/lib_has_std"] [dependencies] derive_builder_core = { version = "=0.20.0", path = "../derive_builder_core" } -syn = { version = "2.0.15", features = ["full", "extra-traits"] } +syn = { version = "2.0.49", features = ["full", "extra-traits"] }