Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
15 changes: 9 additions & 6 deletions bon-macros/src/builder/builder_gen/input_fn/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
);
}

Expand All @@ -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}"
);
}
}
Expand Down
45 changes: 40 additions & 5 deletions bon-macros/src/builder/item_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -188,3 +184,42 @@ fn conv_impl_item_fn_into_fn_item(func: syn::ImplItemFn) -> Result<syn::ItemFn>
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"
)
}
8 changes: 8 additions & 0 deletions bon-macros/src/util/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
29 changes: 29 additions & 0 deletions bon/tests/integration/ui/compile_fail/attr_bon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
89 changes: 89 additions & 0 deletions bon/tests/integration/ui/compile_fail/attr_bon.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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)