Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
119 changes: 57 additions & 62 deletions crates/ide-assists/src/handlers/extract_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1385,31 +1385,30 @@ enum FlowHandler {

impl FlowHandler {
fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler {
match &fun.control_flow.kind {
None => FlowHandler::None,
Some(flow_kind) => {
let action = flow_kind.clone();
if let FunType::Unit = ret_ty {
match flow_kind {
FlowKind::Return(None)
| FlowKind::Break(_, None)
| FlowKind::Continue(_) => FlowHandler::If { action },
FlowKind::Return(_) | FlowKind::Break(_, _) => {
FlowHandler::IfOption { 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() },
}
if fun.contains_tail_expr {
return FlowHandler::None;
}
let Some(action) = fun.control_flow.kind.clone() else {
return FlowHandler::None;
};

if let FunType::Unit = ret_ty {
match action {
FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
FlowHandler::If { action }
}
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 },
}
}
}
Expand Down Expand Up @@ -1654,11 +1653,7 @@ impl Function {

fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
let fun_ty = self.return_type(ctx);
let handler = if self.contains_tail_expr {
FlowHandler::None
} else {
FlowHandler::from_ret_ty(self, &fun_ty)
};
let handler = FlowHandler::from_ret_ty(self, &fun_ty);
let ret_ty = match &handler {
FlowHandler::None => {
if matches!(fun_ty, FunType::Unit) {
Expand Down Expand Up @@ -1728,11 +1723,7 @@ fn make_body(
fun: &Function,
) -> ast::BlockExpr {
let ret_ty = fun.return_type(ctx);
let handler = if fun.contains_tail_expr {
FlowHandler::None
} else {
FlowHandler::from_ret_ty(fun, &ret_ty)
};
let handler = FlowHandler::from_ret_ty(fun, &ret_ty);

let block = match &fun.body {
FunctionBody::Expr(expr) => {
Expand Down Expand Up @@ -4471,7 +4462,7 @@ async fn foo() -> Result<(), ()> {
"#,
r#"
async fn foo() -> Result<(), ()> {
fun_name().await?
fun_name().await
}

async fn $0fun_name() -> Result<(), ()> {
Expand Down Expand Up @@ -4690,15 +4681,15 @@ fn $0fun_name() {
check_assist(
extract_function,
r#"
//- minicore: result
//- minicore: result, try
fn foo() -> Result<(), i64> {
$0Result::<i32, i64>::Ok(0)?;
Ok(())$0
}
"#,
r#"
fn foo() -> Result<(), i64> {
fun_name()?
fun_name()
}

fn $0fun_name() -> Result<(), i64> {
Expand Down Expand Up @@ -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]
fn non_tail_expr_of_tail_expr_loop() {
check_assist(
Expand Down Expand Up @@ -5800,12 +5819,6 @@ fn $0fun_name() -> ControlFlow<()> {
extract_function,
r#"
//- 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<()> {
if true {
let a = $0if true {
Expand All @@ -5820,12 +5833,6 @@ fn f() -> Option<()> {
}
"#,
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<()> {
if true {
let a = fun_name()?;;
Expand All @@ -5852,12 +5859,6 @@ fn $0fun_name() -> Option<()> {
extract_function,
r#"
//- 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<()> {
if true {
$0{
Expand All @@ -5874,15 +5875,9 @@ fn f() -> Option<()> {
}
"#,
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<()> {
if true {
fun_name()?
fun_name()
} else {
None
}
Expand Down