Skip to content

Commit 17f52ef

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 17f52ef

2 files changed

Lines changed: 96 additions & 0 deletions

File tree

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

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

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

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