From e18c7bc0e58a5dc29fe0ccbf91840962424dea52 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Fri, 30 May 2025 11:21:25 +0000 Subject: [PATCH 1/2] Improve error reporting for using the #[bon::builder] fully qualified attribute inside of impl blocks --- .../builder_gen/input_fn/validation.rs | 15 ++-- bon-macros/src/builder/item_impl.rs | 45 ++++++++-- bon-macros/src/util/path.rs | 8 ++ .../integration/ui/compile_fail/attr_bon.rs | 29 ++++++ .../ui/compile_fail/attr_bon.stderr | 89 +++++++++++++++++++ 5 files changed, 175 insertions(+), 11 deletions(-) diff --git a/bon-macros/src/builder/builder_gen/input_fn/validation.rs b/bon-macros/src/builder/builder_gen/input_fn/validation.rs index 0383ace0..8effae15 100644 --- a/bon-macros/src/builder/builder_gen/input_fn/validation.rs +++ b/bon-macros/src/builder/builder_gen/input_fn/validation.rs @@ -5,15 +5,18 @@ impl super::FnInputCtx<'_> { pub(super) fn validate(&self) -> Result { if self.impl_ctx.is_none() { let explanation = "\ - but #[bon] attribute is absent on top of the impl block; this \ - additional #[bon] attribute on the impl block is required for \ - the macro to see the type of `Self` and properly generate \ - the builder struct definition adjacently to the impl block."; + which likely means the builder attribute was used inside of an \ + impl block; the impl block needs to be annotated with the #[bon] \ + attribute and the builder attribute must be spelled as #[builder] \ + without any additional path prefix, since it's used as a simple \ + inert config attribute for #[bon] in impl blocks; more info on \ + inert vs active attributes: \ + https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes"; if let Some(receiver) = &self.fn_item.orig.sig.receiver() { bail!( &receiver.self_token, - "function contains a `self` parameter {explanation}" + "this function contains a `self` parameter {explanation}" ); } @@ -22,7 +25,7 @@ impl super::FnInputCtx<'_> { if let Some(self_span) = ctx.self_span { bail!( &self_span, - "function contains a `Self` type reference {explanation}" + "this function contains a `Self` type reference {explanation}" ); } } diff --git a/bon-macros/src/builder/item_impl.rs b/bon-macros/src/builder/item_impl.rs index 9b1dcd51..8a592561 100644 --- a/bon-macros/src/builder/item_impl.rs +++ b/bon-macros/src/builder/item_impl.rs @@ -43,11 +43,7 @@ pub(crate) fn generate( }); if builder_fns.is_empty() { - bail!( - &Span::call_site(), - "there are no #[builder] functions in the impl block, so there is no \ - need for a #[bon] attribute here" - ); + return Err(no_builder_attrs_error(&other_items)); } orig_impl_block.items = builder_fns; @@ -188,3 +184,42 @@ fn conv_impl_item_fn_into_fn_item(func: syn::ImplItemFn) -> Result block: Box::new(block), }) } + +fn no_builder_attrs_error(other_items: &[syn::ImplItem]) -> Error { + let builder_like_err = other_items.iter().find_map(|item| { + let item = match item { + syn::ImplItem::Fn(fn_item) => fn_item, + _ => return None, + }; + + let builder_like = item + .attrs + .iter() + .find(|attr| attr.path().ends_with_segment("builder"))?; + + let builder_like_str = darling::util::path_to_string(builder_like.path()); + let builder_like_prefix = builder_like_str + .strip_suffix("builder") + .unwrap_or(&builder_like_str); + + Some(err!( + &builder_like.path(), + "#[bon] macro found no #[builder] attributes in the impl block, but \ + it looks like this attribute was meant for #[bon]; note that #[bon] \ + expects a bare #[builder] attribute without the `{builder_like_prefix}` \ + prefix; #[builder] acts as a simple config attribute for the active \ + #[bon] attribute in impl blocks; more info on inert vs active attributes: \ + https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes" + )) + }); + + if let Some(err) = builder_like_err { + return err; + } + + err!( + &Span::call_site(), + "there are no #[builder] functions in the impl block, so there is no \ + need for a #[bon] attribute here" + ) +} diff --git a/bon-macros/src/util/path.rs b/bon-macros/src/util/path.rs index 525bbb8b..ec219d4f 100644 --- a/bon-macros/src/util/path.rs +++ b/bon-macros/src/util/path.rs @@ -2,6 +2,7 @@ use crate::util::prelude::*; pub(crate) trait PathExt { fn starts_with_segment(&self, desired_segment: &str) -> bool; + fn ends_with_segment(&self, desired_segment: &str) -> bool; /// Returns an error if this path has some generic arguments. fn require_mod_style(&self) -> Result; @@ -15,6 +16,13 @@ impl PathExt for syn::Path { .unwrap_or(false) } + fn ends_with_segment(&self, desired_segment: &str) -> bool { + self.segments + .last() + .map(|first| first.ident == desired_segment) + .unwrap_or(false) + } + fn require_mod_style(&self) -> Result { if self .segments diff --git a/bon/tests/integration/ui/compile_fail/attr_bon.rs b/bon/tests/integration/ui/compile_fail/attr_bon.rs index e810dc8d..32ea72bd 100644 --- a/bon/tests/integration/ui/compile_fail/attr_bon.rs +++ b/bon/tests/integration/ui/compile_fail/attr_bon.rs @@ -26,5 +26,34 @@ impl NoBuilderMethods { const NOT_BUILDER: () = (); } +struct BuilderLikeMethodAttribute; + +#[bon] +impl BuilderLikeMethodAttribute { + fn not_builder() {} + + #[::foo::builder] + fn builder_like() {} +} + +struct ActiveBuilderInsideImplWithSelf; + +#[bon] +impl ActiveBuilderInsideImplWithSelf { + #[bon::builder] + fn active_bon_with_self(&self) {} +} + +struct ActiveBuilderInsideImplClueless; + +#[bon] +impl ActiveBuilderInsideImplClueless { + // The `builder` attribute is "clueless" here because nothing tells it that + // it is inside of an impl block, so it silently "succeeds" to generate + // a builder method, but it produces a bunch of errors that items can't + // be defined inside of an impl block. + #[bon::builder] + fn active_bon_clueless() {} +} fn main() {} diff --git a/bon/tests/integration/ui/compile_fail/attr_bon.stderr b/bon/tests/integration/ui/compile_fail/attr_bon.stderr index 2d9962db..e5b1be7f 100644 --- a/bon/tests/integration/ui/compile_fail/attr_bon.stderr +++ b/bon/tests/integration/ui/compile_fail/attr_bon.stderr @@ -17,3 +17,92 @@ error: there are no #[builder] functions in the impl block, so there is no need | ^^^^^^ | = note: this error originates in the attribute macro `bon` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: #[bon] macro found no #[builder] attributes in the impl block, but it looks like this attribute was meant for #[bon]; note that #[bon] expects a bare #[builder] attribute without the `foo::` prefix; #[builder] acts as a simple config attribute for the active #[bon] attribute in impl blocks; more info on inert vs active attributes: https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes + --> tests/integration/ui/compile_fail/attr_bon.rs:35:7 + | +35 | #[::foo::builder] + | ^ + +error: #[bon] macro found no #[builder] attributes in the impl block, but it looks like this attribute was meant for #[bon]; note that #[bon] expects a bare #[builder] attribute without the `bon::` prefix; #[builder] acts as a simple config attribute for the active #[bon] attribute in impl blocks; more info on inert vs active attributes: https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes + --> tests/integration/ui/compile_fail/attr_bon.rs:43:7 + | +43 | #[bon::builder] + | ^^^ + +error: this function contains a `self` parameter which likely means the builder attribute was used inside of an impl block; the impl block needs to be annotated with the #[bon] attribute and the builder attribute must be spelled as #[builder] without any additional path prefix, since it's used as a simple inert config attribute for #[bon] in impl blocks; more info on inert vs active attributes: https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes + --> tests/integration/ui/compile_fail/attr_bon.rs:44:30 + | +44 | fn active_bon_with_self(&self) {} + | ^^^^ + +error: #[bon] macro found no #[builder] attributes in the impl block, but it looks like this attribute was meant for #[bon]; note that #[bon] expects a bare #[builder] attribute without the `bon::` prefix; #[builder] acts as a simple config attribute for the active #[bon] attribute in impl blocks; more info on inert vs active attributes: https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes + --> tests/integration/ui/compile_fail/attr_bon.rs:55:7 + | +55 | #[bon::builder] + | ^^^ + +error: struct is not supported in `trait`s or `impl`s + --> tests/integration/ui/compile_fail/attr_bon.rs:49:1 + | +49 | #[bon] + | ^^^^^^ + | + = help: consider moving the struct out to a nearby module scope + = note: this error originates in the attribute macro `bon::builder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: implementation is not supported in `trait`s or `impl`s + --> tests/integration/ui/compile_fail/attr_bon.rs:49:1 + | +49 | #[bon] + | ^^^^^^ + | + = help: consider moving the implementation out to a nearby module scope + = note: this error originates in the attribute macro `bon::builder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: module is not supported in `trait`s or `impl`s + --> tests/integration/ui/compile_fail/attr_bon.rs:49:1 + | +49 | #[bon] + | ^^^^^^ + | + = help: consider moving the module out to a nearby module scope + = note: this error originates in the attribute macro `bon::builder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `const` items in this context need a name + --> tests/integration/ui/compile_fail/attr_bon.rs:41:1 + | +41 | #[bon] + | ^^^^^^ `_` is not a valid name for this `const` item + | + = note: this error originates in the attribute macro `bon::builder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `const` items in this context need a name + --> tests/integration/ui/compile_fail/attr_bon.rs:49:1 + | +49 | #[bon] + | ^^^^^^ `_` is not a valid name for this `const` item + | + = note: this error originates in the attribute macro `bon::builder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0433]: failed to resolve: could not find `foo` in the list of imported crates + --> tests/integration/ui/compile_fail/attr_bon.rs:35:9 + | +35 | #[::foo::builder] + | ^^^ could not find `foo` in the list of imported crates + +error[E0412]: cannot find type `ActiveBonCluelessBuilder` in this scope + --> tests/integration/ui/compile_fail/attr_bon.rs:49:1 + | +49 | #[bon] + | ^^^^^^ not found in this scope + | + = note: this error originates in the attribute macro `bon::builder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0422]: cannot find struct, variant or union type `ActiveBonCluelessBuilder` in this scope + --> tests/integration/ui/compile_fail/attr_bon.rs:49:1 + | +49 | #[bon] + | ^^^^^^ not found in this scope + | + = note: this error originates in the attribute macro `bon::builder` (in Nightly builds, run with -Z macro-backtrace for more info) From 8b8264d5247ae522ad55f157ed10e95453c1ae5f Mon Sep 17 00:00:00 2001 From: Veetaha Date: Fri, 30 May 2025 11:33:20 +0000 Subject: [PATCH 2/2] Add apt-get update --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a98849bb..7f736f53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions-rust-lang/setup-rust-toolchain@v1 - - run: sudo apt-get install -y valgrind + - run: sudo apt-get update && sudo apt-get install -y valgrind - run: cd ./benchmarks/runtime && ./run.sh ${{ matrix.benchmark }} compilation-benchmarks: