Skip to content

Commit e13e5d0

Browse files
authored
Add support for propagating #[track_caller] and future special attributes (#282)
1 parent 1d33d4e commit e13e5d0

File tree

9 files changed

+96
-15
lines changed

9 files changed

+96
-15
lines changed

benchmarks/runtime/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
clippy::let_and_return,
1010
clippy::map_unwrap_or,
1111
clippy::must_use_candidate,
12+
clippy::similar_names,
1213
rustdoc::missing_crate_level_docs
1314
)]
1415

bon-macros/src/builder/builder_gen/finish_fn.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ impl super::BuilderGenCtx {
149149
let body = &self.finish_fn.body.generate(self);
150150
let asyncness = &self.finish_fn.asyncness;
151151
let unsafety = &self.finish_fn.unsafety;
152-
let must_use = &self.finish_fn.must_use;
152+
let special_attrs = &self.finish_fn.special_attrs;
153153
let attrs = &self.finish_fn.attrs;
154154
let finish_fn_vis = &self.finish_fn.vis;
155155
let finish_fn_ident = &self.finish_fn.ident;
@@ -171,7 +171,7 @@ impl super::BuilderGenCtx {
171171
clippy::future_not_send,
172172
clippy::missing_const_for_fn,
173173
)]
174-
#must_use
174+
#(#special_attrs)*
175175
#finish_fn_vis #const_ #asyncness #unsafety fn #finish_fn_ident(
176176
self,
177177
#(#finish_fn_params,)*

bon-macros/src/builder/builder_gen/input_fn/mod.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,6 @@ impl<'a> FnInputCtx<'a> {
297297
let members = Member::from_raw(&self.config, MemberOrigin::FnArg, members)?;
298298

299299
let generics = self.generics();
300-
301300
let mut adapted_fn_sig = self.adapted_fn()?.sig;
302301

303302
if self.config.start_fn.name.is_none() {
@@ -342,7 +341,7 @@ impl<'a> FnInputCtx<'a> {
342341
vis: finish_fn_vis.map(SpannedKey::into_value),
343342
unsafety: self.fn_item.norm.sig.unsafety,
344343
asyncness: self.fn_item.norm.sig.asyncness,
345-
must_use: get_must_use_attribute(&self.fn_item.norm.attrs)?,
344+
special_attrs: get_propagated_attrs(&self.fn_item.norm.attrs)?,
346345
body: Box::new(finish_fn_body),
347346
output: self.fn_item.norm.sig.output,
348347
attrs: finish_fn_docs,
@@ -486,26 +485,38 @@ fn merge_generic_params(
486485
.collect()
487486
}
488487

489-
fn get_must_use_attribute(attrs: &[syn::Attribute]) -> Result<Option<syn::Attribute>> {
488+
const PROPAGATED_ATTRIBUTES: &[&str] = &["must_use", "track_caller"];
489+
490+
fn get_propagated_attrs(attrs: &[syn::Attribute]) -> Result<Vec<syn::Attribute>> {
491+
PROPAGATED_ATTRIBUTES
492+
.iter()
493+
.copied()
494+
.filter_map(|needle| find_propagated_attr(attrs, needle).transpose())
495+
.collect()
496+
}
497+
498+
fn find_propagated_attr(attrs: &[syn::Attribute], needle: &str) -> Result<Option<syn::Attribute>> {
490499
let mut iter = attrs
491500
.iter()
492-
.filter(|attr| attr.meta.path().is_ident("must_use"));
501+
.filter(|attr| attr.meta.path().is_ident(needle));
493502

494503
let result = iter.next();
495504

496505
if let Some(second) = iter.next() {
497506
bail!(
498507
second,
499-
"found multiple #[must_use], but bon only works with exactly one or zero."
508+
"found multiple #[{}], but bon only works with exactly one or zero.",
509+
needle
500510
);
501511
}
502512

503513
if let Some(attr) = result {
504514
if let syn::AttrStyle::Inner(_) = attr.style {
505515
bail!(
506516
attr,
507-
"#[must_use] attribute must be placed on the function itself, \
508-
not inside it."
517+
"#[{}] attribute must be placed on the function itself, \
518+
not inside it.",
519+
needle
509520
);
510521
}
511522
}

bon-macros/src/builder/builder_gen/input_struct.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@ impl StructInputCtx {
157157
vis: finish_fn_vis.map(SpannedKey::into_value),
158158
unsafety: None,
159159
asyncness: None,
160-
must_use: Some(syn::parse_quote! {
160+
special_attrs: vec![syn::parse_quote! {
161161
#[must_use = "building a struct without using it is likely a bug"]
162-
}),
162+
}],
163163
body: Box::new(finish_fn_body),
164164
output: syn::parse_quote!(-> #struct_ty),
165165
attrs: finish_fn_docs

bon-macros/src/builder/builder_gen/models.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ pub(super) struct FinishFn {
5656

5757
pub(super) unsafety: Option<syn::Token![unsafe]>,
5858
pub(super) asyncness: Option<syn::Token![async]>,
59-
/// <https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute>
60-
pub(super) must_use: Option<syn::Attribute>,
59+
/// Special attributes to propagate, such as [`must_use`](<https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute>)
60+
/// and [`track_caller`](<https://doc.rust-lang.org/reference/attributes/codegen.html#r-attributes.codegen.track_caller>)
61+
pub(super) special_attrs: Vec<syn::Attribute>,
6162
pub(super) body: Box<dyn FinishFnBody>,
6263
pub(super) output: syn::ReturnType,
6364
}
@@ -71,7 +72,7 @@ pub(super) struct FinishFnParams {
7172
pub(super) attrs: Vec<syn::Attribute>,
7273
pub(super) unsafety: Option<syn::Token![unsafe]>,
7374
pub(super) asyncness: Option<syn::Token![async]>,
74-
pub(super) must_use: Option<syn::Attribute>,
75+
pub(super) special_attrs: Vec<syn::Attribute>,
7576
pub(super) body: Box<dyn FinishFnBody>,
7677
pub(super) output: syn::ReturnType,
7778
}
@@ -316,7 +317,7 @@ impl BuilderGenCtx {
316317
attrs: finish_fn.attrs,
317318
unsafety: finish_fn.unsafety,
318319
asyncness: finish_fn.asyncness,
319-
must_use: finish_fn.must_use,
320+
special_attrs: finish_fn.special_attrs,
320321
body: finish_fn.body,
321322
output: finish_fn.output,
322323
};

bon/tests/integration/builder/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod orig_fn_naming;
2525
mod positional_members;
2626
mod raw_idents;
2727
mod smoke;
28+
mod track_caller;
2829

2930
use crate::prelude::*;
3031

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use crate::prelude::*;
2+
use core::panic::Location;
3+
4+
#[derive(Debug)]
5+
#[allow(dead_code)]
6+
struct LineCol {
7+
line: u32,
8+
col: u32,
9+
}
10+
11+
impl From<&'_ Location<'_>> for LineCol {
12+
fn from(value: &'_ Location<'_>) -> Self {
13+
Self {
14+
line: value.line(),
15+
col: value.column(),
16+
}
17+
}
18+
}
19+
20+
#[test]
21+
fn track_caller_fn() {
22+
#[builder]
23+
#[track_caller]
24+
fn dont_brick(_x: u32) -> LineCol {
25+
Location::caller().into()
26+
}
27+
28+
let location = dont_brick().x(10).call();
29+
assert_debug_eq(location, expect!["LineCol { line: 28, col: 39 }"]);
30+
31+
#[track_caller]
32+
#[builder]
33+
fn dont_brick_2(_x: u32) -> LineCol {
34+
Location::caller().into()
35+
}
36+
37+
let location = dont_brick_2().x(10).call();
38+
assert_debug_eq(location, expect!["LineCol { line: 37, col: 41 }"]);
39+
}
40+
41+
#[test]
42+
fn track_caller_impl_block() {
43+
struct Brick;
44+
struct Senti;
45+
#[bon]
46+
impl Senti {
47+
#[builder(finish_fn = yatta)]
48+
#[track_caller]
49+
fn new(brick: Brick) -> LineCol {
50+
let Brick = brick;
51+
Location::caller().into()
52+
}
53+
}
54+
let yatta = Senti::builder().brick(Brick).yatta();
55+
assert_debug_eq(yatta, expect!["LineCol { line: 54, col: 47 }"]);
56+
}

bon/tests/integration/ui/compile_fail/attr_builder.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,8 @@ fn destructuring1((x, y): (u32, u32)) {
6767

6868
#[builder]
6969
fn destructuring2((_, _): (u32, u32)) {}
70+
71+
#[builder]
72+
#[track_caller]
73+
#[track_caller]
74+
fn double_track_caller() {}

bon/tests/integration/ui/compile_fail/attr_builder.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,9 @@ error: use a simple `identifier: type` syntax for the function argument; destruc
8383
|
8484
69 | fn destructuring2((_, _): (u32, u32)) {}
8585
| ^^^^^^
86+
87+
error: found multiple #[track_caller], but bon only works with exactly one or zero.
88+
--> tests/integration/ui/compile_fail/attr_builder.rs:73:1
89+
|
90+
73 | #[track_caller]
91+
| ^

0 commit comments

Comments
 (0)