Merge pull request #20913 from A4-Tacks/if-let-chain-for-is-method-with-if-let

Fix not applicable on let-chain for replace_is_method_with_if_let_method
This commit is contained in:
Shoyu Vanilla (Flint) 2025-10-26 14:48:02 +00:00 committed by GitHub
commit 803ab10db4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 81 additions and 4 deletions

View file

@ -1,7 +1,7 @@
use ide_db::syntax_helpers::suggest_name;
use syntax::ast::{self, AstNode, syntax_factory::SyntaxFactory};
use crate::{AssistContext, AssistId, Assists};
use crate::{AssistContext, AssistId, Assists, utils::cover_let_chain};
// Assist: replace_is_some_with_if_let_some
//
@ -27,13 +27,11 @@ pub(crate) fn replace_is_method_with_if_let_method(
let if_expr = ctx.find_node_at_offset::<ast::IfExpr>()?;
let cond = if_expr.condition()?;
let cond = cover_let_chain(cond, ctx.selection_trimmed())?;
let call_expr = match cond {
ast::Expr::MethodCallExpr(call) => call,
_ => return None,
};
if ctx.offset() > if_expr.then_branch()?.stmt_list()?.l_curly_token()?.text_range().end() {
return None;
}
let name_ref = call_expr.name_ref()?;
match name_ref.text().as_str() {
@ -195,6 +193,63 @@ fn main() {
);
}
#[test]
fn replace_is_some_with_if_let_some_in_let_chain() {
check_assist(
replace_is_method_with_if_let_method,
r#"
fn main() {
let x = Some(1);
let cond = true;
if cond && x.is_som$0e() {}
}
"#,
r#"
fn main() {
let x = Some(1);
let cond = true;
if cond && let Some(${0:x1}) = x {}
}
"#,
);
check_assist(
replace_is_method_with_if_let_method,
r#"
fn main() {
let x = Some(1);
let cond = true;
if x.is_som$0e() && cond {}
}
"#,
r#"
fn main() {
let x = Some(1);
let cond = true;
if let Some(${0:x1}) = x && cond {}
}
"#,
);
check_assist(
replace_is_method_with_if_let_method,
r#"
fn main() {
let x = Some(1);
let cond = true;
if cond && x.is_som$0e() && cond {}
}
"#,
r#"
fn main() {
let x = Some(1);
let cond = true;
if cond && let Some(${0:x1}) = x && cond {}
}
"#,
);
}
#[test]
fn replace_is_some_with_if_let_some_not_applicable_after_l_curly() {
check_assist_not_applicable(

View file

@ -1133,6 +1133,28 @@ pub(crate) fn tt_from_syntax(node: SyntaxNode) -> Vec<NodeOrToken<ast::TokenTree
tt_stack.pop().expect("parent token tree was closed before it was completed").1
}
pub(crate) fn cover_let_chain(mut expr: ast::Expr, range: TextRange) -> Option<ast::Expr> {
if !expr.syntax().text_range().contains_range(range) {
return None;
}
loop {
let (chain_expr, rest) = if let ast::Expr::BinExpr(bin_expr) = &expr
&& bin_expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And))
{
(bin_expr.rhs(), bin_expr.lhs())
} else {
(Some(expr), None)
};
if let Some(chain_expr) = chain_expr
&& chain_expr.syntax().text_range().contains_range(range)
{
break Some(chain_expr);
}
expr = rest?;
}
}
pub fn is_body_const(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> bool {
let mut is_const = true;
preorder_expr(expr, &mut |ev| {