diff --git a/core/src/auto_enum/context.rs b/core/src/auto_enum/context.rs index 1ce8d25f..496b6485 100644 --- a/core/src/auto_enum/context.rs +++ b/core/src/auto_enum/context.rs @@ -13,7 +13,7 @@ use syn::{ use crate::utils::{expr_call, path, replace_expr, unit, VisitedNode}; -use super::visitor::{Dummy, FindTry, Visitor}; +use super::visitor::{Dummy, FindNested, FindTry, Visitor}; // ================================================================================================= // Context @@ -230,6 +230,12 @@ impl Context { node.visited(&mut Dummy::new(self)); } + pub(super) fn find_nested(&mut self, node: &mut impl VisitedNode) -> bool { + let mut visitor = FindNested::new(); + node.visited(&mut visitor); + visitor.has + } + pub(super) fn find_try(&mut self, node: &mut impl VisitedNode) { let mut visitor = FindTry::new(self); node.visited(&mut visitor); diff --git a/core/src/auto_enum/expr.rs b/core/src/auto_enum/expr.rs index cc55a2be..8bba86ff 100644 --- a/core/src/auto_enum/expr.rs +++ b/core/src/auto_enum/expr.rs @@ -1,237 +1,162 @@ -use std::ops::{Deref, DerefMut}; - use syn::{ visit_mut::{self, VisitMut}, *, }; -use crate::utils::{expr_block, replace_block, replace_expr}; +use crate::utils::{expr_block, replace_block, replace_expr, Attrs}; -use super::{Attrs, NAME, NESTED, NEVER}; +use super::{Context, NAME, NESTED, NEVER}; /// Visits last expression. /// /// Note that do not use this after `cx.visitor()`. -pub(super) fn child_expr(expr: &mut Expr, cx: &mut super::Context) -> Result<()> { - fn child_expr_inner(expr: &mut Expr, cx: &mut Context<'_>) -> Result<()> { - cx.last_expr_mut( - expr, - (), - |expr, cx| { - if expr.any_empty_attr(NESTED) { - cx.nested = true; - } - !cx.is_unreachable(expr) - }, - |expr, cx| match expr { - Expr::Match(expr) => cx.visit_last_expr_match(expr), - Expr::If(expr) => cx.visit_last_expr_if(expr), - Expr::Loop(expr) => cx.visit_last_expr_loop(expr), - - // Search recursively - Expr::MethodCall(ExprMethodCall { receiver: expr, .. }) - | Expr::Paren(ExprParen { expr, .. }) - | Expr::Type(ExprType { expr, .. }) => child_expr_inner(expr, cx), - - _ => Ok(()), - }, - ) +pub(super) fn child_expr(expr: &mut Expr, cx: &mut Context) -> Result<()> { + if !cx.visit_last() || is_unreachable(cx, expr) { + return Ok(()); } - if cx.visit_last() { child_expr_inner(expr, &mut Context::from(cx)) } else { Ok(()) } -} - -// ================================================================================================= -// Context + match expr { + Expr::Block(ExprBlock { block, .. }) | Expr::Unsafe(ExprUnsafe { block, .. }) => { + match block.stmts.last_mut() { + Some(Stmt::Expr(expr)) => return child_expr(expr, cx), + Some(Stmt::Item(_)) => unreachable!(), + _ => {} + } + } + _ => {} + } -struct Context<'a> { - cx: &'a mut super::Context, - nested: bool, -} + match expr { + Expr::Match(expr) => visit_last_expr_match(cx, expr), + Expr::If(expr) => visit_last_expr_if(cx, expr), + Expr::Loop(expr) => visit_last_expr_loop(cx, expr), -// To avoid `cx.cx` -impl Deref for Context<'_> { - type Target = super::Context; + // Search recursively + Expr::MethodCall(ExprMethodCall { receiver: expr, .. }) + | Expr::Paren(ExprParen { expr, .. }) + | Expr::Type(ExprType { expr, .. }) => child_expr(expr, cx), - fn deref(&self) -> &Self::Target { - self.cx + _ => Ok(()), } } -impl DerefMut for Context<'_> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.cx - } -} +pub(super) fn is_unreachable(cx: &Context, expr: &Expr) -> bool { + const UNREACHABLE_MACROS: &[&str] = &["unreachable", "panic"]; -impl<'a> From<&'a mut super::Context> for Context<'a> { - fn from(cx: &'a mut super::Context) -> Self { - Self { cx, nested: false } + if expr.any_empty_attr(NEVER) || expr.any_attr(NAME) { + return true; } -} - -impl Context<'_> { - fn last_expr_mut( - &mut self, - expr: &mut Expr, - success: T, - mut filter: impl FnMut(&Expr, &mut Self) -> bool, - f: impl FnOnce(&mut Expr, &mut Self) -> Result, - ) -> Result { - if !filter(expr, self) { - return Ok(success); - } - match expr { - Expr::Block(ExprBlock { block, .. }) | Expr::Unsafe(ExprUnsafe { block, .. }) => { - match block.stmts.last_mut() { - Some(Stmt::Expr(expr)) => return self.last_expr_mut(expr, success, filter, f), - Some(Stmt::Semi(expr, _)) => { - if !filter(expr, self) { - return Ok(success); - } - } - Some(_) => return Ok(success), - None => {} - } - } - _ => {} + match expr { + Expr::Block(ExprBlock { block, .. }) | Expr::Unsafe(ExprUnsafe { block, .. }) => { + is_unreachable_stmt(cx, block.stmts.last()) } - f(expr, self) - } - - fn is_unreachable(&self, expr: &Expr) -> bool { - const UNREACHABLE_MACROS: &[&str] = &["unreachable", "panic"]; - - fn filter(expr: &Expr) -> bool { - !expr.any_empty_attr(NEVER) && !expr.any_attr(NAME) - } + Expr::Break(_) | Expr::Continue(_) | Expr::Return(_) => true, - if !filter(expr) { - return true; + // `unreachable!`, `panic!` or an expression level marker (`marker!` macro). + Expr::Macro(ExprMacro { mac, .. }) => { + UNREACHABLE_MACROS.iter().any(|i| mac.path.is_ident(i)) || cx.is_marker_macro(mac) } - match expr { - Expr::Block(ExprBlock { block, .. }) | Expr::Unsafe(ExprUnsafe { block, .. }) => { - match block.stmts.last() { - Some(Stmt::Expr(expr)) => return self.is_unreachable(expr), - Some(Stmt::Semi(expr, _)) if !filter(expr) => return true, - Some(_) => return true, - None => {} - } - } - _ => {} + Expr::Match(ExprMatch { arms, .. }) => { + arms.iter().all(|arm| arm.any_empty_attr(NEVER) || is_unreachable(cx, &*arm.body)) } - match expr { - Expr::Break(_) | Expr::Continue(_) | Expr::Return(_) => true, - - // `unreachable!`, `panic!` or an expression level marker (`marker!` macro). - Expr::Macro(ExprMacro { mac, .. }) => { - UNREACHABLE_MACROS.iter().any(|i| mac.path.is_ident(i)) || self.is_marker_macro(mac) - } - - Expr::Match(ExprMatch { arms, .. }) => { - arms.iter().all(|arm| arm.any_empty_attr(NEVER) || self.is_unreachable(&*arm.body)) - } - - // `Err(expr)?` or `None?`. - Expr::Try(ExprTry { expr, .. }) => match &**expr { - Expr::Path(ExprPath { path, qself: None, .. }) => path.is_ident("None"), - Expr::Call(ExprCall { args, func, .. }) if args.len() == 1 => match &**func { - Expr::Path(ExprPath { path, qself: None, .. }) => path.is_ident("Err"), - _ => false, - }, + // `Err(expr)?` or `None?`. + Expr::Try(ExprTry { expr, .. }) => match &**expr { + Expr::Path(ExprPath { path, qself: None, .. }) => path.is_ident("None"), + Expr::Call(ExprCall { args, func, .. }) if args.len() == 1 => match &**func { + Expr::Path(ExprPath { path, qself: None, .. }) => path.is_ident("Err"), _ => false, }, _ => false, - } - } + }, - fn visit_last_expr(&mut self, expr: &mut Expr) -> Result { - self.last_expr_mut( - expr, - true, - |expr, cx| !cx.is_unreachable(expr), - |expr, cx| match expr { - Expr::Match(expr) => cx.visit_last_expr_match(expr).map(|()| true), - Expr::If(expr) => cx.visit_last_expr_if(expr).map(|()| true), - Expr::Loop(expr) => cx.visit_last_expr_loop(expr).map(|()| true), - _ => Ok(false), - }, - ) + // Search recursively + Expr::MethodCall(ExprMethodCall { receiver: expr, .. }) + | Expr::Paren(ExprParen { expr, .. }) + | Expr::Type(ExprType { expr, .. }) => is_unreachable(cx, expr), + + _ => false, } +} - fn visit_last_expr_match(&mut self, expr: &mut ExprMatch) -> Result<()> { - fn skip(arm: &mut Arm, cx: &mut Context<'_>) -> Result { - Ok(arm.any_empty_attr(NEVER) - || cx.is_unreachable(&*arm.body) - || ((arm.any_empty_attr(NESTED) || cx.nested) - && cx.visit_last_expr(&mut arm.body)?)) +fn is_unreachable_stmt(cx: &Context, stmt: Option<&Stmt>) -> bool { + match stmt { + Some(Stmt::Expr(expr)) | Some(Stmt::Semi(expr, _)) => is_unreachable(cx, expr), + Some(Stmt::Local(local)) => { + local.init.as_ref().map_or(false, |(_, expr)| is_unreachable(cx, expr)) } + Some(Stmt::Item(_)) => true, + None => false, + } +} - expr.arms.iter_mut().try_for_each(|arm| { - if !skip(arm, self)? { - arm.comma = Some(token::Comma::default()); - replace_expr(&mut *arm.body, |x| self.next_expr(x)); - } - Ok(()) - }) +fn visit_last_expr_match(cx: &mut Context, expr: &mut ExprMatch) -> Result<()> { + fn skip(arm: &mut Arm, cx: &mut Context) -> bool { + arm.any_empty_attr(NEVER) + || arm.any_empty_attr(NESTED) + || is_unreachable(cx, &*arm.body) + || cx.find_nested(arm) } - fn visit_last_expr_if(&mut self, expr: &mut ExprIf) -> Result<()> { - #[allow(clippy::needless_pass_by_value)] - fn skip(last: Option<&mut Stmt>, cx: &mut Context<'_>) -> Result { - Ok(match &last { - Some(Stmt::Expr(expr)) | Some(Stmt::Semi(expr, _)) => cx.is_unreachable(expr), - _ => true, - } || match last { - Some(Stmt::Expr(expr)) => { - (expr.any_empty_attr(NESTED) || cx.nested) && cx.visit_last_expr(expr)? - } - _ => true, - }) + expr.arms.iter_mut().try_for_each(|arm| { + if !skip(arm, cx) { + arm.comma = Some(token::Comma::default()); + replace_expr(&mut *arm.body, |x| cx.next_expr(x)); } + Ok(()) + }) +} - if !skip(expr.then_branch.stmts.last_mut(), self)? { - replace_block(&mut expr.then_branch, |b| self.next_expr(expr_block(b))); +fn visit_last_expr_if(cx: &mut Context, expr: &mut ExprIf) -> Result<()> { + fn skip(block: &mut Block, cx: &mut Context) -> bool { + match block.stmts.last_mut() { + Some(Stmt::Expr(expr)) => { + expr.any_empty_attr(NESTED) || is_unreachable(cx, expr) || cx.find_nested(block) + } + _ => is_unreachable_stmt(cx, block.stmts.last()), } + } - match expr.else_branch.as_mut().map(|(_, expr)| &mut **expr) { - Some(Expr::Block(expr)) => { - if !skip(expr.block.stmts.last_mut(), self)? { - replace_block(&mut expr.block, |b| self.next_expr(expr_block(b))); - } - Ok(()) + if !skip(&mut expr.then_branch, cx) { + replace_block(&mut expr.then_branch, |b| cx.next_expr(expr_block(b))); + } + + match expr.else_branch.as_mut().map(|(_, expr)| &mut **expr) { + Some(Expr::Block(expr)) => { + if !skip(&mut expr.block, cx) { + replace_block(&mut expr.block, |b| cx.next_expr(expr_block(b))); } - Some(Expr::If(expr)) => self.visit_last_expr_if(expr), + Ok(()) + } + Some(Expr::If(expr)) => visit_last_expr_if(cx, expr), - // TODO: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html#method.join - // `expr.span().join(expr.then_branch.span()).unwrap_or_else(|| expr.span())`` - None => Err(error!(expr.if_token, "`if` expression missing an else clause")), + // TODO: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html#method.join + // `expr.span().join(expr.then_branch.span()).unwrap_or_else(|| expr.span())`` + None => Err(error!(expr.if_token, "`if` expression missing an else clause")), - Some(_) => unreachable!("wrong_if"), - } + Some(_) => unreachable!("wrong_if"), } +} - fn visit_last_expr_loop(&mut self, expr: &mut ExprLoop) -> Result<()> { - LoopVisitor::new(expr, self).visit_block_mut(&mut expr.body); - Ok(()) - } +fn visit_last_expr_loop(cx: &mut Context, expr: &mut ExprLoop) -> Result<()> { + LoopVisitor::new(expr, cx).visit_block_mut(&mut expr.body); + Ok(()) } // ================================================================================================= // LoopVisitor struct LoopVisitor<'a> { - cx: &'a mut super::Context, + cx: &'a mut Context, label: Option, nested: bool, } impl<'a> LoopVisitor<'a> { - fn new(expr: &ExprLoop, cx: &'a mut super::Context) -> Self { + fn new(expr: &ExprLoop, cx: &'a mut Context) -> Self { Self { cx, label: expr.label.as_ref().map(|l| l.name.clone()), nested: false } } diff --git a/core/src/auto_enum/mod.rs b/core/src/auto_enum/mod.rs index f9eb3798..312eb1bd 100644 --- a/core/src/auto_enum/mod.rs +++ b/core/src/auto_enum/mod.rs @@ -22,8 +22,6 @@ const NAME: &str = "auto_enum"; const NESTED: &str = "nested"; /// The annotation for skipping branch. const NEVER: &str = "never"; -/// The annotations used by `#[auto_enum]`. -const EMPTY_ATTRS: &[&str] = &[NEVER, NESTED]; /// The old annotation replaced by `#[nested]`. const NESTED_OLD: &str = "rec"; diff --git a/core/src/auto_enum/visitor.rs b/core/src/auto_enum/visitor.rs index 035eb4dc..32dc68ef 100644 --- a/core/src/auto_enum/visitor.rs +++ b/core/src/auto_enum/visitor.rs @@ -10,7 +10,7 @@ use syn::{ use crate::utils::expr_call; use crate::utils::{expr_unimplemented, replace_expr, Attrs, AttrsMut}; -use super::{Context, VisitMode, DEFAULT_MARKER, NAME, NEVER}; +use super::{Context, VisitMode, DEFAULT_MARKER, NAME, NESTED, NEVER}; // ================================================================================================= // Visitor @@ -37,13 +37,11 @@ impl<'a> Visitor<'a> { fn find_remove_attrs(&mut self, attrs: &mut impl AttrsMut) { if !self.scope.foreign { - super::EMPTY_ATTRS.iter().for_each(|ident| { - if let Some(attr) = attrs.find_remove_attr(ident) { - if let Err(e) = syn::parse2::(attr.tokens) { - self.cx.diagnostic.error(e); - } + if let Some(attr) = attrs.find_remove_attr(NEVER) { + if let Err(e) = syn::parse2::(attr.tokens) { + self.cx.diagnostic.error(e); } - }); + } if let Some(old) = attrs.find_remove_attr(super::NESTED_OLD) { self.cx.diagnostic.error(error!( @@ -133,6 +131,17 @@ impl<'a> Visitor<'a> { } } + /// `#[nested]` + fn visit_nested(&mut self, node: &mut Expr, attr: Attribute) { + debug_assert!(!self.scope.foreign); + + if let Err(e) = + syn::parse2::(attr.tokens).and_then(|_| super::expr::child_expr(node, self.cx)) + { + self.cx.diagnostic.error(e); + } + } + /// Expression level marker (`marker!` macro) fn visit_marker_macro(&mut self, node: &mut Expr) { debug_assert!(!self.scope.foreign || self.cx.marker != DEFAULT_MARKER); @@ -178,6 +187,12 @@ impl VisitMut for Visitor<'_> { VisitMode::Default => {} } + if !self.scope.foreign { + if let Some(attr) = node.find_remove_attr(NESTED) { + self.visit_nested(node, attr); + } + } + visit_mut::visit_expr_mut(self, node); if !self.scope.foreign || self.cx.marker != DEFAULT_MARKER { @@ -189,14 +204,30 @@ impl VisitMut for Visitor<'_> { fn visit_arm_mut(&mut self, node: &mut Arm) { if !self.cx.failed() { + if !self.scope.foreign { + if let Some(attr) = node.find_remove_attr(NESTED) { + self.visit_nested(&mut *node.body, attr); + } + } + visit_mut::visit_arm_mut(self, node); + self.find_remove_attrs(node); } } fn visit_local_mut(&mut self, node: &mut Local) { if !self.cx.failed() { + if !self.scope.foreign { + if let Some(attr) = node.find_remove_attr(NESTED) { + if let Some((_, expr)) = &mut node.init { + self.visit_nested(&mut **expr, attr); + } + } + } + visit_mut::visit_local_mut(self, node); + self.find_remove_attrs(node); } } @@ -302,6 +333,54 @@ fn visit_stmt(visitor: &mut impl VisitStmt, node: &mut Stmt) { } } +// ================================================================================================= +// FindNested + +/// Find `#[nested]` attribute. +pub(super) struct FindNested { + pub(super) has: bool, +} + +impl FindNested { + pub(super) fn new() -> Self { + Self { has: false } + } +} + +impl VisitMut for FindNested { + fn visit_expr_mut(&mut self, node: &mut Expr) { + if !node.any_attr(NAME) { + if node.any_empty_attr(NESTED) { + self.has = true; + } else { + visit_mut::visit_expr_mut(self, node); + } + } + } + + fn visit_arm_mut(&mut self, node: &mut Arm) { + if node.any_empty_attr(NESTED) { + self.has = true; + } else { + visit_mut::visit_arm_mut(self, node); + } + } + + fn visit_local_mut(&mut self, node: &mut Local) { + if !node.any_attr(NAME) { + if node.any_empty_attr(NESTED) { + self.has = true; + } else { + visit_mut::visit_local_mut(self, node); + } + } + } + + fn visit_item_mut(&mut self, _: &mut Item) { + // Do not recurse into nested items. + } +} + // ================================================================================================= // FindTry diff --git a/src/lib.rs b/src/lib.rs index 75ceb2c9..eda4cec8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,6 +124,9 @@ //! # fn main() { let _ = foo(0); } //! ``` //! +//! `#[nested]` can be used basically in the same place as `#[auto_enum]`, +//! except that `#[nested]` cannot be used in functions. +//! //! ### Positions where `#[auto_enum]` can be used. //! //! `#[auto_enum]` can be used in the following three places. However, since @@ -449,6 +452,9 @@ //! * Expression level marker (`marker!` macro). //! * An item definition. //! +//! Also, if the branch contains `#[nested]`, it is interpreted as returning +//! an anonymous enum generated by `#[auto_enum]`, not a value. +//! //! ```rust //! # #![cfg_attr(feature = "try_trait", feature(try_trait))] //! # use auto_enums::auto_enum; diff --git a/tests/auto_enum.rs b/tests/auto_enum.rs index 0d7a8996..d035e4d4 100644 --- a/tests/auto_enum.rs +++ b/tests/auto_enum.rs @@ -10,10 +10,7 @@ #![cfg_attr(feature = "read_initializer", feature(read_initializer))] #![cfg_attr(not(feature = "std"), no_std)] #![warn(rust_2018_idioms)] - -#[allow(unused_imports)] -#[macro_use] -extern crate alloc; +#![allow(dead_code)] mod stable { use auto_enums::auto_enum; @@ -138,6 +135,15 @@ mod stable { assert_eq!(no_return4(i).sum::(), *x); } + #[auto_enum(fmt::Debug)] + fn no_return5(x: usize) -> impl core::fmt::Debug { + match x { + 0 => 1..8, + 3 => {} + _ => 0..=2, + } + } + #[auto_enum(Iterator)] fn return1(x: usize) -> impl Iterator { if x > 10 { @@ -334,111 +340,6 @@ mod stable { assert_eq!(transpose(0).unwrap().sum::(), 28); } - #[test] - fn nested() { - #[auto_enum(Iterator)] - fn match_in_match(x: usize) -> impl Iterator { - match x { - 0 => 1..8, - #[nested] - n if n > 3 => match x { - 2..=10 => (1..x as _).map(|x| x - 1), - _ => 2..=10, - }, - _ => (0..2).map(|x| x + 1), - } - } - for (i, x) in ANS.iter().enumerate() { - assert_eq!(match_in_match(i).sum::(), *x); - } - - #[rustfmt::skip] - #[allow(unused_unsafe)] - #[auto_enum(Iterator)] - fn in_block(x: usize) -> impl Iterator { - {{{ unsafe {{{ unsafe { unsafe {{ - match x { - 0 => 1..8, - #[nested] - n if n > 3 => {{{ unsafe {{ - if x > 10 { - (-10..=x as _).map(|x| x - 4) - } else { - (1..=4).map(|x| x - 4) - } - }}}}} - _ => (0..2).map(|x| x + 1), - } - }}}}}}}}} - } - for (i, x) in ANS.iter().enumerate() { - assert_eq!(in_block(i).sum::(), *x); - } - - #[auto_enum(Iterator)] - fn match_in_if(x: usize) -> impl Iterator { - if x == 0 { - 1..8 - } else if x > 3 { - #[nested] - match x { - 1..=4 => 2..=10, - _ => (11..20).map(|x| x - 1), - } - } else { - (0..2).map(|x| x + 1) - } - } - for (i, x) in ANS.iter().enumerate() { - assert_eq!(match_in_if(i).sum::(), *x); - } - - #[auto_enum(Iterator)] - fn if_in_if(x: usize) -> impl Iterator { - if x == 0 { - 1..8 - } else if x > 3 { - #[nested] - { - if x > 4 { 2..=10 } else { (11..20).map(|x| x - 1) } - } - } else { - (0..2).map(|x| x + 1) - } - } - for (i, x) in ANS.iter().enumerate() { - assert_eq!(if_in_if(i).sum::(), *x); - } - - #[auto_enum(Iterator)] - fn nop(x: usize) -> impl Iterator { - if x == 0 { - 1..8 - } else if x > 3 { - #[nested] - 2..=10 - } else { - (0..2).map(|x| x + 1) - } - } - for (i, x) in ANS.iter().enumerate() { - assert_eq!(nop(i).sum::(), *x); - } - - #[auto_enum(Iterator)] - fn no_return(x: usize) -> impl Iterator { - match x { - 0 => 1..8, - #[nested] - 3 => panic!(), - _ => (0..2).map(|x| x + 1), - } - } - for (i, x) in ANS.iter().enumerate() { - assert_eq!(no_return(i).sum::(), *x); - } - } - #[test] fn marker() { #[auto_enum(Iterator)] @@ -773,27 +674,6 @@ mod nightly { }; } - #[test] - fn nested() { - // nested - for (i, x) in ANS.iter().enumerate() { - #[auto_enum(Iterator)] - let iter = if i > 3 { - #[nested] - match i { - 1..=10 => (1..3).map(|x| x + 1), - 11..=20 => 4..=5, - _ => (5..10).map(|x| x - 1), - } - } else if i == 0 { - 1..8 - } else { - vec![1, 2, 0].into_iter() - }; - assert_eq!(iter.sum::(), *x); - } - } - #[test] fn marker() { fn marker1(x: usize) -> impl Iterator + Clone { diff --git a/tests/nested.rs b/tests/nested.rs new file mode 100644 index 00000000..5b1ff2a8 --- /dev/null +++ b/tests/nested.rs @@ -0,0 +1,219 @@ +#![cfg_attr(feature = "try_trait", feature(try_trait))] +#![warn(rust_2018_idioms)] +#![allow(dead_code)] + +use auto_enums::auto_enum; + +#[test] +fn nested() { + #[auto_enum(Iterator)] + fn match_in_match(x: usize) -> impl Iterator { + match x { + 0 => 1..8, + #[nested] + n if n > 3 => match x { + 2..=10 => (1..x as _).map(|x| x - 1), + _ => 2..=10, + }, + _ => (0..2).map(|x| x + 1), + } + } + for (i, x) in [28, 3].iter().enumerate() { + assert_eq!(match_in_match(i).sum::(), *x); + } + + #[auto_enum(Iterator)] + fn match_in_match_2(x: usize) -> impl Iterator { + match x { + 0 => 1..8, + #[nested] + n if n > 3 => match x { + 2..=10 => + { + #[nested] + match n { + 4 => (1..x as _).map(|x| x - 1), + _ => (1..x as _).map(|x| x + 1), + } + } + _ => 2..=10, + }, + _ => (0..2).map(|x| x + 1), + } + } + for (i, x) in [28, 3].iter().enumerate() { + assert_eq!(match_in_match(i).sum::(), *x); + } + + #[rustfmt::skip] + #[allow(unused_unsafe)] + #[auto_enum(Iterator)] + fn in_block(x: usize) -> impl Iterator { + {{{ unsafe {{{ unsafe { unsafe {{ + match x { + 0 => 1..8, + #[nested] + n if n > 3 => {{{ unsafe {{ + if x > 10 { + (-10..=x as _).map(|x| x - 4) + } else { + (1..=4).map(|x| x - 4) + } + }}}}} + _ => (0..2).map(|x| x + 1), + } + }}}}}}}}} + } + for (i, x) in [28, 3].iter().enumerate() { + assert_eq!(in_block(i).sum::(), *x); + } + + #[auto_enum(Iterator)] + fn match_in_if(x: usize) -> impl Iterator { + if x == 0 { + 1..8 + } else if x > 3 { + #[nested] + match x { + 1..=4 => 2..=10, + _ => (11..20).map(|x| x - 1), + } + } else { + (0..2).map(|x| x + 1) + } + } + for (i, x) in [28, 3].iter().enumerate() { + assert_eq!(match_in_if(i).sum::(), *x); + } + + #[auto_enum(Iterator)] + fn if_in_block_if(x: usize) -> impl Iterator { + if x == 0 { + 1..8 + } else if x > 3 { + #[nested] + { + if x > 4 { 2..=10 } else { (11..20).map(|x| x - 1) } + } + } else { + (0..2).map(|x| x + 1) + } + } + for (i, x) in [28, 3].iter().enumerate() { + assert_eq!(if_in_block_if(i).sum::(), *x); + } + + #[auto_enum(Iterator)] + fn match_in_let_match(x: usize) -> impl Iterator { + match x { + 0 => 1..8, + 3 => { + #[nested] + let x = match x { + 4..=10 => 2..=10, + _ => (11..20).map(|x| x - 1), + }; + x + } + _ => (0..2).map(|x| x + 1), + } + } + for (i, x) in [28, 3].iter().enumerate() { + assert_eq!(match_in_let_match(i).sum::(), *x); + } + + #[auto_enum(Iterator)] + fn match_in_let_if(x: usize) -> impl Iterator { + match x { + 0 => 1..8, + 3 => { + #[nested] + let x = if x > 4 { 2..=10 } else { (11..20).map(|x| x - 1) }; + x + } + _ => (0..2).map(|x| x + 1), + } + } + for (i, x) in [28, 3].iter().enumerate() { + assert_eq!(match_in_let_if(i).sum::(), *x); + } + + #[auto_enum(Iterator)] + fn if_in_let_if(x: usize) -> impl Iterator { + if x == 0 { + 1..8 + } else if x > 3 { + #[nested] + let x = if x > 4 { 2..=10 } else { (11..20).map(|x| x - 1) }; + x + } else { + (0..2).map(|x| x + 1) + } + } + for (i, x) in [28, 3].iter().enumerate() { + assert_eq!(if_in_let_if(i).sum::(), *x); + } + + #[auto_enum(Iterator)] + fn match_in_let_if_nop(x: usize) -> impl Iterator { + match x { + 0 => 1..8, + 3 => { + #[nested] + let x = if x > 4 { 2..=10 } else { (11..20).map(|x| x - 1) }; + #[nested] // no-op + let _y = 111..120; + x + } + _ => (0..2).map(|x| x + 1), + } + } + + #[auto_enum(Iterator)] + fn if_in_let_if_nop(x: usize) -> impl Iterator { + if x == 0 { + 1..8 + } else if x > 3 { + #[nested] + let x = if x > 4 { 2..=10 } else { (11..20).map(|x| x - 1) }; + #[nested] // no-op + x + } else { + (0..2).map(|x| x + 1) + } + } + + #[auto_enum(Iterator)] + fn match_nop(x: usize) -> impl Iterator { + match x { + 0 => 1..8, + 3 => { + // #[nested] // no-op, E0308 error will occur if you uncomment this + 2..=10 + } + _ => (0..2).map(|x| x + 1), + } + } + + #[auto_enum(Iterator)] + fn if_nop(x: usize) -> impl Iterator { + if x == 0 { + 1..8 + } else if x > 3 { + // #[nested] // no-op, E0308 error will occur if you uncomment this + 2..=10 + } else { + (0..2).map(|x| x + 1) + } + } + + #[auto_enum(Iterator)] + fn no_return(x: usize) -> impl Iterator { + match x { + 0 => 1..8, + #[nested] + 3 => panic!(), + _ => (0..2).map(|x| x + 1), + } + } +}