Skip to content

Commit 958d16a

Browse files
committed
Fix bsn! enum variant subexpression hoisting
Hoist native subexpressions in process_enum_field the same way process_field already does, so SceneFunction closures stay static.
1 parent 81127b3 commit 958d16a

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

crates/bevy_scene/macros/src/bsn/codegen.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,15 @@ impl BsnType {
633633
return Ok(quote! {#(#type_assigns)*});
634634
}
635635

636+
if let Some(value @ (BsnValue::Ident(_)
637+
| BsnValue::Expr(_)
638+
| BsnValue::Closure(_)
639+
| BsnValue::Tuple(_))) = value
640+
{
641+
let ident = ctx.hoisted_expressions.hoist(value);
642+
return Ok(quote! { *#bind_name = #ident; });
643+
}
644+
636645
// NOTE: It is very important to still produce outputs for None field values. This is what
637646
// enables field autocomplete in Rust Analyzer
638647
value
@@ -995,6 +1004,52 @@ mod tests {
9951004
.contains("Duplicate field `x` found in BSN enum variant"));
9961005
}
9971006

1007+
#[test]
1008+
fn enum_variant_expr_is_hoisted() {
1009+
let mut refs = EntityRefs::default();
1010+
let paths = TestPaths::new();
1011+
let mut exprs = HoistedExpressions::default();
1012+
let mut ctx = paths.ctx(&mut refs, &mut exprs);
1013+
let mut assignments = vec![];
1014+
let handle = BsnType {
1015+
path: parse_quote!(FontSourceTemplate),
1016+
enum_variant: Some(parse_quote!(Handle)),
1017+
fields: BsnFields::Tuple(vec![BsnUnnamedField {
1018+
value: BsnValue::Expr(quote!(some_borrow.clone())),
1019+
}]),
1020+
};
1021+
1022+
let res = handle.push_enum_patch(
1023+
&mut ctx,
1024+
&parse_quote!(Handle),
1025+
&mut assignments,
1026+
PatchTarget {
1027+
path: &[],
1028+
is_ref: false,
1029+
},
1030+
);
1031+
1032+
assert!(res.is_ok());
1033+
assert_eq!(ctx.errors.len(), 0);
1034+
assert_eq!(exprs.expressions.len(), 1);
1035+
assert_eq!(
1036+
exprs.expressions[0].to_string(),
1037+
"let _expr0 = { some_borrow . clone () } . into () ;"
1038+
);
1039+
let assignment_output: String = assignments
1040+
.iter()
1041+
.map(|t| t.to_string())
1042+
.collect();
1043+
assert!(
1044+
assignment_output.contains("_expr0"),
1045+
"expected hoisted ident in assignment output: {assignment_output}"
1046+
);
1047+
assert!(
1048+
!assignment_output.contains("some_borrow"),
1049+
"borrow should not appear inline in assignment output: {assignment_output}"
1050+
);
1051+
}
1052+
9981053
#[test]
9991054
fn bsn_root_preserves_inference_on_error() {
10001055
// Arrange

crates/bevy_scene/src/lib.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2881,6 +2881,48 @@ mod tests {
28812881
assert_eq!(entity.get::<TextFont>().unwrap().font_size, FontSize(24));
28822882
}
28832883

2884+
#[test]
2885+
fn enum_variant_subexpressions_are_hoisted() {
2886+
#[derive(Component, FromTemplate, PartialEq, Eq, Debug, Clone)]
2887+
enum FontSource {
2888+
#[default]
2889+
Handle { value: String },
2890+
}
2891+
2892+
struct Config {
2893+
value: String,
2894+
}
2895+
2896+
fn make_scene(config: &Config) -> impl Scene {
2897+
bsn! {
2898+
Children [
2899+
(FontSource::Handle { value: { config.value.clone() } }),
2900+
(FontSource::Handle { value: { config.value.clone() } }),
2901+
]
2902+
}
2903+
}
2904+
2905+
let mut app = test_app();
2906+
let world = app.world_mut();
2907+
let config = Config {
2908+
value: "test".to_string(),
2909+
};
2910+
let entity = world.spawn_scene(make_scene(&config)).unwrap().id();
2911+
let children = world.entity(entity).get::<Children>().unwrap();
2912+
assert_eq!(
2913+
world.entity(children[0]).get::<FontSource>().unwrap(),
2914+
&FontSource::Handle {
2915+
value: "test".to_string(),
2916+
}
2917+
);
2918+
assert_eq!(
2919+
world.entity(children[1]).get::<FontSource>().unwrap(),
2920+
&FontSource::Handle {
2921+
value: "test".to_string(),
2922+
}
2923+
);
2924+
}
2925+
28842926
#[test]
28852927
fn field_name_shorthand() {
28862928
let mut app = test_app();

0 commit comments

Comments
 (0)