fix: don't use control flow when extracted fn contains tail expr of original fn

This commit is contained in:
Ryo Yoshida 2023-07-10 21:50:03 +09:00
parent 2f6d545535
commit 595d9e6ebb
No known key found for this signature in database
GPG key ID: E25698A930586171

View file

@ -1385,31 +1385,30 @@ enum FlowHandler {
impl FlowHandler { impl FlowHandler {
fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler { fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler {
match &fun.control_flow.kind { if fun.contains_tail_expr {
None => FlowHandler::None, return FlowHandler::None;
Some(flow_kind) => { }
let action = flow_kind.clone(); let Some(action) = fun.control_flow.kind.clone() else {
if let FunType::Unit = ret_ty { return FlowHandler::None;
match flow_kind { };
FlowKind::Return(None)
| FlowKind::Break(_, None) if let FunType::Unit = ret_ty {
| FlowKind::Continue(_) => FlowHandler::If { action }, match action {
FlowKind::Return(_) | FlowKind::Break(_, _) => { FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
FlowHandler::IfOption { action } FlowHandler::If { action }
}
FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
}
} else {
match flow_kind {
FlowKind::Return(None)
| FlowKind::Break(_, None)
| FlowKind::Continue(_) => FlowHandler::MatchOption { none: action },
FlowKind::Return(_) | FlowKind::Break(_, _) => {
FlowHandler::MatchResult { err: action }
}
FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
}
} }
FlowKind::Return(_) | FlowKind::Break(_, _) => FlowHandler::IfOption { action },
FlowKind::Try { kind } => FlowHandler::Try { kind },
}
} else {
match action {
FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
FlowHandler::MatchOption { none: action }
}
FlowKind::Return(_) | FlowKind::Break(_, _) => {
FlowHandler::MatchResult { err: action }
}
FlowKind::Try { kind } => FlowHandler::Try { kind },
} }
} }
} }
@ -1654,11 +1653,7 @@ impl Function {
fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> { fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
let fun_ty = self.return_type(ctx); let fun_ty = self.return_type(ctx);
let handler = if self.contains_tail_expr { let handler = FlowHandler::from_ret_ty(self, &fun_ty);
FlowHandler::None
} else {
FlowHandler::from_ret_ty(self, &fun_ty)
};
let ret_ty = match &handler { let ret_ty = match &handler {
FlowHandler::None => { FlowHandler::None => {
if matches!(fun_ty, FunType::Unit) { if matches!(fun_ty, FunType::Unit) {
@ -1728,11 +1723,7 @@ fn make_body(
fun: &Function, fun: &Function,
) -> ast::BlockExpr { ) -> ast::BlockExpr {
let ret_ty = fun.return_type(ctx); let ret_ty = fun.return_type(ctx);
let handler = if fun.contains_tail_expr { let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
FlowHandler::None
} else {
FlowHandler::from_ret_ty(fun, &ret_ty)
};
let block = match &fun.body { let block = match &fun.body {
FunctionBody::Expr(expr) => { FunctionBody::Expr(expr) => {
@ -4471,7 +4462,7 @@ async fn foo() -> Result<(), ()> {
"#, "#,
r#" r#"
async fn foo() -> Result<(), ()> { async fn foo() -> Result<(), ()> {
fun_name().await? fun_name().await
} }
async fn $0fun_name() -> Result<(), ()> { async fn $0fun_name() -> Result<(), ()> {
@ -4690,7 +4681,7 @@ fn $0fun_name() {
check_assist( check_assist(
extract_function, extract_function,
r#" r#"
//- minicore: result //- minicore: result, try
fn foo() -> Result<(), i64> { fn foo() -> Result<(), i64> {
$0Result::<i32, i64>::Ok(0)?; $0Result::<i32, i64>::Ok(0)?;
Ok(())$0 Ok(())$0
@ -4698,7 +4689,7 @@ fn foo() -> Result<(), i64> {
"#, "#,
r#" r#"
fn foo() -> Result<(), i64> { fn foo() -> Result<(), i64> {
fun_name()? fun_name()
} }
fn $0fun_name() -> Result<(), i64> { fn $0fun_name() -> Result<(), i64> {
@ -5753,6 +5744,34 @@ fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
); );
} }
#[test]
fn tail_expr_no_extra_control_flow() {
check_assist(
extract_function,
r#"
//- minicore: result
fn fallible() -> Result<(), ()> {
$0if true {
return Err(());
}
Ok(())$0
}
"#,
r#"
fn fallible() -> Result<(), ()> {
fun_name()
}
fn $0fun_name() -> Result<(), ()> {
if true {
return Err(());
}
Ok(())
}
"#,
);
}
#[test] #[test]
fn non_tail_expr_of_tail_expr_loop() { fn non_tail_expr_of_tail_expr_loop() {
check_assist( check_assist(
@ -5800,12 +5819,6 @@ fn $0fun_name() -> ControlFlow<()> {
extract_function, extract_function,
r#" r#"
//- minicore: option, try //- minicore: option, try
impl<T> core::ops::Try for Option<T> {
type Output = T;
type Residual = Option<!>;
}
impl<T> core::ops::FromResidual for Option<T> {}
fn f() -> Option<()> { fn f() -> Option<()> {
if true { if true {
let a = $0if true { let a = $0if true {
@ -5820,12 +5833,6 @@ fn f() -> Option<()> {
} }
"#, "#,
r#" r#"
impl<T> core::ops::Try for Option<T> {
type Output = T;
type Residual = Option<!>;
}
impl<T> core::ops::FromResidual for Option<T> {}
fn f() -> Option<()> { fn f() -> Option<()> {
if true { if true {
let a = fun_name()?;; let a = fun_name()?;;
@ -5852,12 +5859,6 @@ fn $0fun_name() -> Option<()> {
extract_function, extract_function,
r#" r#"
//- minicore: option, try //- minicore: option, try
impl<T> core::ops::Try for Option<T> {
type Output = T;
type Residual = Option<!>;
}
impl<T> core::ops::FromResidual for Option<T> {}
fn f() -> Option<()> { fn f() -> Option<()> {
if true { if true {
$0{ $0{
@ -5874,15 +5875,9 @@ fn f() -> Option<()> {
} }
"#, "#,
r#" r#"
impl<T> core::ops::Try for Option<T> {
type Output = T;
type Residual = Option<!>;
}
impl<T> core::ops::FromResidual for Option<T> {}
fn f() -> Option<()> { fn f() -> Option<()> {
if true { if true {
fun_name()? fun_name()
} else { } else {
None None
} }