Merge pull request #20526 from A4-Tacks/postfix-let-in-let-chain

Fix .let completion not work for let-chain
This commit is contained in:
Shoyu Vanilla (Flint) 2025-10-10 10:07:31 +00:00 committed by GitHub
commit 986bb35e75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 33 deletions

View file

@ -11,12 +11,12 @@ use ide_db::{
text_edit::TextEdit,
ty_filter::TryEnum,
};
use itertools::Either;
use stdx::never;
use syntax::{
SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
TextRange, TextSize,
T, TextRange, TextSize,
ast::{self, AstNode, AstToken},
match_ast,
};
use crate::{
@ -113,12 +113,8 @@ pub(crate) fn complete_postfix(
if let Some(parent) = dot_receiver_including_refs.syntax().parent()
&& let Some(second_ancestor) = parent.parent()
{
let sec_ancestor_kind = second_ancestor.kind();
if let Some(expr) = <Either<ast::IfExpr, ast::WhileExpr>>::cast(second_ancestor) {
is_in_cond = match expr {
Either::Left(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent),
Either::Right(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent),
}
if let Some(parent_expr) = ast::Expr::cast(parent) {
is_in_cond = is_in_condition(&parent_expr);
}
match &try_enum {
Some(try_enum) if is_in_cond => match try_enum {
@ -147,7 +143,7 @@ pub(crate) fn complete_postfix(
.add_to(acc, ctx.db);
}
},
_ if matches!(sec_ancestor_kind, STMT_LIST | EXPR_STMT) => {
_ if matches!(second_ancestor.kind(), STMT_LIST | EXPR_STMT) => {
postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
.add_to(acc, ctx.db);
postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};"))
@ -454,6 +450,22 @@ fn add_custom_postfix_completions(
None
}
pub(crate) fn is_in_condition(it: &ast::Expr) -> bool {
it.syntax()
.parent()
.and_then(|parent| {
Some(match_ast! { match parent {
ast::IfExpr(expr) => expr.condition()? == *it,
ast::WhileExpr(expr) => expr.condition()? == *it,
ast::MatchGuard(guard) => guard.condition()? == *it,
ast::BinExpr(bin_expr) => (bin_expr.op_token()?.kind() == T![&&])
.then(|| is_in_condition(&bin_expr.into()))?,
_ => return None,
} })
})
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use expect_test::expect;
@ -648,6 +660,38 @@ fn main() {
let bar = Some(true);
if let Some($0) = bar
}
"#,
);
check_edit(
"let",
r#"
//- minicore: option
fn main() {
let bar = Some(true);
if true && bar.$0
}
"#,
r#"
fn main() {
let bar = Some(true);
if true && let Some($0) = bar
}
"#,
);
check_edit(
"let",
r#"
//- minicore: option
fn main() {
let bar = Some(true);
if true && true && bar.$0
}
"#,
r#"
fn main() {
let bar = Some(true);
if true && true && let Some($0) = bar
}
"#,
);
}

View file

@ -19,12 +19,15 @@ use syntax::{
match_ast,
};
use crate::context::{
AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx,
DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind,
NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx, PathKind,
PatternContext, PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget,
TypeLocation,
use crate::{
completions::postfix::is_in_condition,
context::{
AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx,
DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind,
NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx,
PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx,
TypeAscriptionTarget, TypeLocation,
},
};
#[derive(Debug)]
@ -1223,24 +1226,6 @@ fn classify_name_ref<'db>(
Some(res)
};
fn is_in_condition(it: &ast::Expr) -> bool {
(|| {
let parent = it.syntax().parent()?;
if let Some(expr) = ast::WhileExpr::cast(parent.clone()) {
Some(expr.condition()? == *it)
} else if let Some(expr) = ast::IfExpr::cast(parent.clone()) {
Some(expr.condition()? == *it)
} else if let Some(expr) = ast::BinExpr::cast(parent)
&& expr.op_token()?.kind() == T![&&]
{
Some(is_in_condition(&expr.into()))
} else {
None
}
})()
.unwrap_or(false)
}
let make_path_kind_expr = |expr: ast::Expr| {
let it = expr.syntax();
let in_block_expr = is_in_block(it);