From 3ecf0b73da73debdb941d1693f2f6725bcf51ea0 Mon Sep 17 00:00:00 2001 From: Niklas Dewally Date: Tue, 26 Nov 2024 18:36:01 +0000 Subject: [PATCH] fix: Biplate> for Expr Fixes bug where `Biplate> for Expr` returns [] instead of the expected vector. This was due to `derive_iter` not handling the `Biplate> for Iter` case correctly. We already handled the special case of `Biplate for Iter`, but not the identity/ same type case (`Biplate for T`) found in all other Biplates. This was causing `Biplate> for Iter` to return its `Iter` children (using the `Biplate for Iter` case) instead of returning itself as expected. Fixes: https://github.com/conjure-cp/uniplate/issues/16 --- uniplate/src/lib.rs | 25 +++++++++- .../issue-16-biplate-to-vec-expr.rs | 46 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 uniplate/tests/derive-pass/issue-16-biplate-to-vec-expr.rs diff --git a/uniplate/src/lib.rs b/uniplate/src/lib.rs index eb7d4e9..92e691c 100644 --- a/uniplate/src/lib.rs +++ b/uniplate/src/lib.rs @@ -78,8 +78,7 @@ macro_rules! derive_iter { return (::uniplate::Tree::Zero, Box::new(move |_| val.clone())); } - // this is an example of the special biplate case discussed in the paper. - // T == F: return all types F in the Vector. + // T == F: return all types F in the iterator. if std::any::TypeId::of::() == std::any::TypeId::of::() { unsafe { // need to cast from F to T @@ -113,6 +112,28 @@ macro_rules! derive_iter { return (children, ctx); } } + // Identity / same type case: Biplate for Iter + else if std::any::TypeId::of::() == std::any::TypeId::of::<$iter_ty>() { + unsafe { + // need to cast from Iter to T + let val: T = std::mem::transmute::<&$iter_ty, &T>(&self).clone(); + + let children: ::uniplate::Tree = ::uniplate::Tree::One(val); + + let ctx: Box) -> $iter_ty> = + Box::new(move |new_tree: ::uniplate::Tree| { + let ::uniplate::Tree::One(x) = new_tree else { + todo!(); + }; + // need to cast from T to Iter + let val: $iter_ty = + std::mem::transmute::<&T, &$iter_ty>(&x).clone(); + val + }); + + return (children, ctx); + } + } // T != F: return all type T's contained in the type F's in the vector let mut child_trees: im::Vector<::uniplate::Tree> = im::Vector::new(); diff --git a/uniplate/tests/derive-pass/issue-16-biplate-to-vec-expr.rs b/uniplate/tests/derive-pass/issue-16-biplate-to-vec-expr.rs new file mode 100644 index 0000000..120798d --- /dev/null +++ b/uniplate/tests/derive-pass/issue-16-biplate-to-vec-expr.rs @@ -0,0 +1,46 @@ +//! Test case for issue #16. + +#![allow(dead_code)] + +use uniplate::derive::Uniplate; +use uniplate::Biplate; + +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +#[uniplate()] +#[biplate(to=Vec,walk_into=[Expr])] +enum Stmt { + Nothing, + Assign(String, Expr), + Sequence(Vec), + If(Expr, Box, Box), + While(Expr, Box), + Return(String), +} + +#[derive(Eq, PartialEq, Clone, Debug, Uniplate)] +#[uniplate()] +#[biplate(to=Vec)] +enum Expr { + Add(Box, Box), + Sub(Box, Box), + Mul(Box, Box), + Div(Box, Box), + Val(i32), + Var(String), + Neg(Box), +} + +pub fn main() { + use Expr::*; + use Stmt::*; + + let inner_stmts = vec![Assign("x".into(),Val(1)), Return("x".into())]; + let stmt = Sequence(inner_stmts.clone()); + + let result = <_ as Biplate>>::children_bi(&stmt).into_iter().collect::>(); + + assert_eq!(result.len(),1); + assert_eq!(result[0],inner_stmts); + assert_eq!( as Biplate>>::children_bi(&inner_stmts).len(),1); + assert_eq!( as Biplate>>::children_bi(&inner_stmts)[0],inner_stmts); +}