M3: an effects key naming no effect's field binds nothing (#2055)

Review follow-up: `effects/x/radius`, `effects/0` and `effects` name no
effect's field, but `list_binding` doesn't parse them, so the layer's
check took them for some other field and kept an inert binding. A key
under `effects` that names no effect's field is now refused as well.
This commit is contained in:
Oleksii Sukholita committed 2026-11-06 20:18:10 +01:00
1 parent 104aa3deb1
commit d23cebb12f
2 files changed
+8 -4

No files matched your search

+5 -2
View File
@@ -134,12 +134,15 @@ fn a_layer_binds_only_fields_its_effects_have() {
let (mut s, r) = setup();
ex(&mut s, "effect.add", json!({"ids": [r], "type": "dropShadow"}));
ex(&mut s, "effect.add", json!({"ids": [r], "type": "glass"}));
// As an effect style does: a drop shadow has no refraction, glass no spread, and there is no
// effect 5. Nothing is bound.
// As an effect style does: a drop shadow has no refraction, glass no spread, there is no
// effect 5, and a key that names no effect's field names nothing. Nothing is bound.
for (field, var, says) in [
("effects/0/refraction", "large", "no field"),
("effects/1/spread", "large", "no field"),
("effects/5/radius", "large", "no field"),
("effects/x/radius", "large", "no field"),
("effects/0", "large", "no field"),
("effects", "large", "no field"),
("effects/1/lightAngle", "brand", "doesn't take a color variable"),
("effects/0/color", "large", "doesn't take a number variable"),
] {
+3 -2
View File
@@ -661,11 +661,12 @@ fn effect_field(doc: &Document, node: NodeId, i: usize, field: &str) -> Option<V
}
/// Whether `node` has the field a binding key names. Only effect fields are checked: a layer has
/// `effects/<i>/<field>` when its effect `i` has that field (a drop shadow has no `refraction`).
/// `effects/<i>/<field>` when its effect `i` has that field (a drop shadow has no `refraction`),
/// and an `effects…` key naming no effect's field (`effects/x/radius`, `effects/0`) has none.
pub fn has_field(doc: &Document, node: NodeId, field: &str) -> bool {
match super::lists::list_binding(field) {
Some((BoundList::Effects, i, f)) => effect_field(doc, node, i, f).is_some(),
_ => true,
_ => field.split('/').next() != Some(BoundList::Effects.prefix()),
}
}