return statements are sometimes required, even when they shouldn't be
My repro is unfortunately private code, so sorry...if it's not obvious what's going on I'll work up something else for you. But if you do something like this:
#[auto_enum(Iterator)]
fn foo() -> Impl Iterator<Item=whatever> {
if a {
return std::iter::empty();
}
// Some more of those
// And then return without an explicit return statement...
std::iter::from_fn(|| stuff)
}
Then it only picks up the variants that explicitly had a return statement, and ignores the final expression in the function.
I believe this is because we do not currently handle last expressions that are not considered branches. https://github.com/taiki-e/auto_enums/blob/5c3ffbef7d132990fa4c8726f55afd156b0c6978/src/auto_enum/expr.rs#L21-L37
My guess is that you will have to special case the last expression in a function because afaik that's the only place this edge case appears.
It's also probably worth noting that returning () is something a function might want to do even if it doesn't make sense in most cases, so there might be a holistic solution.
I can look at a PR if you don't have the time. I ended up not using this at work for other reasons unrelated to the quality of the crate. Yay code simplification, heh.
My guess is that you will have to special case the last expression in a function because afaik that's the only place this edge case appears.
Yeah, the code I linked to is a function (child_expr) that handles that case, but even if the last expression itself is not considered to have a branch, it would indeed need to handle the last expression as one of the branches if a return is found in another expression.
https://github.com/taiki-e/auto_enums/blob/5c3ffbef7d132990fa4c8726f55afd156b0c6978/src/auto_enum/mod.rs#L206-L207
I can look at a PR if you don't have the time.
I don't have the bandwidth to work on the fix this right now, so if you could take a look that would be great!
I'll try to at least poke at it this weekend.
I couldn't untangle the many nested visitors and such to figure out how to funnel the data around generically, which is problematic because the same bug applies to closures. That said, here is a diff that shows some broken test cases:
diff --git a/tests/auto_enum.rs b/tests/auto_enum.rs
index 3c1f583..514997b 100644
--- a/tests/auto_enum.rs
+++ b/tests/auto_enum.rs
@@ -340,6 +340,22 @@ fn stable() {
}
assert_eq!(try_operator3(10).unwrap().sum::<i32>(), 54);
+ #[auto_enum(Iterator)]
+ fn trailing_expr_as_return(x: usize) -> impl Iterator<Item = i32> {
+ if x == 0 {
+ return 1..8;
+ }
+
+ if x > 3 {
+ return 2..=10;
+ }
+
+ (0..2).map(|x| x + 1)
+ }
+ for (i, x) in ANS.iter().enumerate() {
+ assert_eq!(trailing_expr_as_return(i).sum::<i32>(), *x);
+ }
+
#[auto_enum]
fn closure() {
#[auto_enum(Iterator)]
@@ -360,6 +376,25 @@ fn stable() {
assert_eq!(f(i).sum::<i32>(), *x);
}
+ // Same as above, but the final else is a trailing return expression.
+ #[auto_enum(Iterator)]
+ let f = |x| {
+ if x > 10 {
+ return (0..x as _).map(|x| x - 1);
+ }
+ if x == 0 {
+ return 1..8;
+ } else if x > 3 {
+ return 2..=10;
+ }
+
+ (0..2).map(|x| x + 1)
+ };
Hopefully that helps you or whoever else comes after figure this out quickly. Unfortunately without familiarity, this is taking much longer than I can justify spending on it at the moment. I will circle back if this ever becomes a blocker, but since I'm not currently using the crate this bug is low priority for me at the moment.