Fix various IDE features

As a side benefit, we got `let` guard support for `move_guard` for free.
This commit is contained in:
Chayim Refael Friedman 2022-01-24 00:37:59 +02:00
parent fe1e324694
commit 13ac5c3491
15 changed files with 95 additions and 93 deletions

View File

@ -811,10 +811,10 @@ pub fn from_call_site(call: &ast::MacroCall) -> ExpandTo {
MACRO_TYPE => ExpandTo::Type,
ARG_LIST | TRY_EXPR | TUPLE_EXPR | PAREN_EXPR | ARRAY_EXPR | FOR_EXPR | PATH_EXPR
| CLOSURE_EXPR | CONDITION | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM
| MATCH_GUARD | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR
| FIELD_EXPR | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR
| BIN_EXPR => ExpandTo::Expr,
| CLOSURE_EXPR | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM | MATCH_GUARD
| RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR | FIELD_EXPR
| AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR
| LET_EXPR => ExpandTo::Expr,
LET_STMT => {
// FIXME: Handle LHS Pattern
ExpandTo::Expr

View File

@ -55,6 +55,7 @@ fn test(x: &i32) {
139..140 'g': {unknown}
143..144 'e': {unknown}
157..204 'if let... }': ()
160..175 'let [val] = opt': bool
164..169 '[val]': [{unknown}]
165..168 'val': {unknown}
172..175 'opt': [{unknown}]
@ -62,6 +63,7 @@ fn test(x: &i32) {
190..191 'h': {unknown}
194..197 'val': {unknown}
210..236 'if let...rue {}': ()
213..233 'let x ... &true': bool
217..225 'x @ true': &bool
221..225 'true': bool
221..225 'true': bool
@ -111,36 +113,42 @@ fn test(x: &i32) {
37..38 'x': &i32
46..208 '{ ...) {} }': ()
52..75 'if let...y() {}': ()
55..72 'let "f... any()': bool
59..64 '"foo"': &str
59..64 '"foo"': &str
67..70 'any': fn any<&str>() -> &str
67..72 'any()': &str
73..75 '{}': ()
80..99 'if let...y() {}': ()
83..96 'let 1 = any()': bool
87..88 '1': i32
87..88 '1': i32
91..94 'any': fn any<i32>() -> i32
91..96 'any()': i32
97..99 '{}': ()
104..126 'if let...y() {}': ()
107..123 'let 1u... any()': bool
111..115 '1u32': u32
111..115 '1u32': u32
118..121 'any': fn any<u32>() -> u32
118..123 'any()': u32
124..126 '{}': ()
131..153 'if let...y() {}': ()
134..150 'let 1f... any()': bool
138..142 '1f32': f32
138..142 '1f32': f32
145..148 'any': fn any<f32>() -> f32
145..150 'any()': f32
151..153 '{}': ()
158..179 'if let...y() {}': ()
161..176 'let 1.0 = any()': bool
165..168 '1.0': f64
165..168 '1.0': f64
171..174 'any': fn any<f64>() -> f64
171..176 'any()': f64
177..179 '{}': ()
184..206 'if let...y() {}': ()
187..203 'let tr... any()': bool
191..195 'true': bool
191..195 'true': bool
198..201 'any': fn any<bool>() -> bool
@ -163,10 +171,12 @@ fn test(x: &i32) {
8..9 'x': &i32
17..75 '{ ...2 {} }': ()
23..45 'if let...u32 {}': ()
26..42 'let 1....= 2u32': bool
30..35 '1..76': u32
38..42 '2u32': u32
43..45 '{}': ()
50..73 'if let...u32 {}': ()
53..70 'let 1....= 2u32': bool
57..63 '1..=76': u32
66..70 '2u32': u32
71..73 '{}': ()

View File

@ -2248,6 +2248,7 @@ fn test() {
176..193 'Thing ...1i32 }': Thing<i32>
187..191 '1i32': i32
199..240 'if let... }': ()
202..221 'let Th... } = z': bool
206..217 'Thing { t }': Thing<i32>
214..215 't': i32
220..221 'z': Thing<i32>

View File

@ -18,7 +18,7 @@
algo, ast,
display::{fn_as_proc_macro_label, macro_label},
match_ast, AstNode, Direction,
SyntaxKind::{CONDITION, LET_STMT},
SyntaxKind::{LET_EXPR, LET_STMT},
SyntaxToken, T,
};
@ -484,7 +484,7 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
let let_kw = if ident
.syntax()
.parent()
.map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
.map_or(false, |p| p.kind() == LET_STMT || p.kind() == LET_EXPR)
{
"let "
} else {

View File

@ -243,7 +243,7 @@ fn is_named_constructor(
let expr = match_ast! {
match let_node {
ast::LetStmt(it) => it.initializer(),
ast::Condition(it) => it.expr(),
ast::LetExpr(it) => it.expr(),
_ => None,
}
}?;
@ -372,15 +372,10 @@ fn should_not_display_type_hint(
match node {
ast::LetStmt(it) => return it.ty().is_some(),
ast::Param(it) => return it.ty().is_some(),
ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty),
ast::IfExpr(it) => {
return it.condition().and_then(|condition| condition.pat()).is_some()
&& pat_is_enum_variant(db, bind_pat, pat_ty);
},
ast::WhileExpr(it) => {
return it.condition().and_then(|condition| condition.pat()).is_some()
&& pat_is_enum_variant(db, bind_pat, pat_ty);
},
ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
ast::IfExpr(_) => return false,
ast::WhileExpr(_) => return false,
ast::ForExpr(it) => {
// We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
// Type of expr should be iterable.

View File

@ -46,7 +46,6 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext) ->
}
let cond = expr.condition().filter(|cond| !cond.is_pattern_cond())?;
let cond = cond.expr()?;
let then = expr.then_branch()?;
let else_ = match expr.else_branch()? {
ast::ElseBranch::Block(b) => b,
@ -209,7 +208,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext) ->
_ => receiver,
};
let if_expr = make::expr_if(
make::condition(cond, None),
cond,
closure_body.reset_indent(),
Some(ast::ElseBranch::Block(make::block_expr(None, Some(none_path)))),
)

View File

@ -48,25 +48,28 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
let cond = if_expr.condition()?;
// Check if there is an IfLet that we can handle.
let if_let_pat = match cond.pat() {
None => None, // No IfLet, supported.
Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
let path = pat.path()?;
if path.qualifier().is_some() {
return None;
}
let (if_let_pat, cond_expr) = if cond.is_pattern_cond() {
let let_ = cond.single_let()?;
match let_.pat() {
Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
let path = pat.path()?;
if path.qualifier().is_some() {
return None;
}
let bound_ident = pat.fields().next().unwrap();
if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
return None;
}
let bound_ident = pat.fields().next().unwrap();
if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
return None;
}
Some((path, bound_ident))
(Some((path, bound_ident)), let_.expr()?)
}
_ => return None, // Unsupported IfLet.
}
Some(_) => return None, // Unsupported IfLet.
} else {
(None, cond)
};
let cond_expr = cond.expr()?;
let then_block = if_expr.then_branch()?;
let then_block = then_block.stmt_list()?;
@ -119,8 +122,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
let then_branch =
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
let cond = invert_boolean_expression(cond_expr);
make::expr_if(make::condition(cond, None), then_branch, None)
.indent(if_indent_level)
make::expr_if(cond, then_branch, None).indent(if_indent_level)
};
new_expr.syntax().clone_for_update()
}

View File

@ -42,7 +42,6 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O
let while_expr = while_kw.parent().and_then(ast::WhileExpr::cast)?;
let while_body = while_expr.loop_body()?;
let while_cond = while_expr.condition()?;
let while_cond_expr = while_cond.expr()?;
let target = while_expr.syntax().text_range();
acc.add(
@ -55,19 +54,15 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O
let break_block =
make::block_expr(once(make::expr_stmt(make::expr_break(None)).into()), None)
.indent(while_indent_level);
let block_expr = match while_cond.pat() {
Some(_) => {
let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
let stmts = once(make::expr_stmt(if_expr).into());
make::block_expr(stmts, None)
}
None => {
let if_cond = make::condition(invert_boolean_expression(while_cond_expr), None);
let if_expr = make::expr_if(if_cond, break_block, None);
let stmts =
once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
make::block_expr(stmts, while_body.tail_expr())
}
let block_expr = if while_cond.is_pattern_cond() {
let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
let stmts = once(make::expr_stmt(if_expr).into());
make::block_expr(stmts, None)
} else {
let if_cond = invert_boolean_expression(while_cond);
let if_expr = make::expr_if(if_cond, break_block, None);
let stmts = once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
make::block_expr(stmts, while_body.tail_expr())
};
let replacement = make::expr_loop(block_expr.indent(while_indent_level));

View File

@ -1219,28 +1219,26 @@ fn make_call_expr(&self, call_expr: ast::Expr) -> ast::Expr {
let stmt = make::expr_stmt(action);
let block = make::block_expr(iter::once(stmt.into()), None);
let controlflow_break_path = make::path_from_text("ControlFlow::Break");
let condition = make::condition(
let condition = make::expr_let(
make::tuple_struct_pat(
controlflow_break_path,
iter::once(make::wildcard_pat().into()),
)
.into(),
call_expr,
Some(
make::tuple_struct_pat(
controlflow_break_path,
iter::once(make::wildcard_pat().into()),
)
.into(),
),
);
make::expr_if(condition, block, None)
make::expr_if(condition.into(), block, None)
}
FlowHandler::IfOption { action } => {
let path = make::ext::ident_path("Some");
let value_pat = make::ext::simple_ident_pat(make::name("value"));
let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
let cond = make::condition(call_expr, Some(pattern.into()));
let cond = make::expr_let(pattern.into(), call_expr);
let value = make::expr_path(make::ext::ident_path("value"));
let action_expr = action.make_result_handler(Some(value));
let action_stmt = make::expr_stmt(action_expr);
let then = make::block_expr(iter::once(action_stmt.into()), None);
make::expr_if(cond, then, None)
make::expr_if(cond.into(), then, None)
}
FlowHandler::MatchOption { none } => {
let some_name = "value";

View File

@ -34,12 +34,12 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
return None;
}
let cond = expr.condition()?;
// This assist should not apply for if-let.
if expr.condition()?.is_pattern_cond() {
if cond.is_pattern_cond() {
return None;
}
let cond = expr.condition()?.expr()?;
let then_node = expr.then_branch()?.syntax().clone();
let else_block = match expr.else_branch()? {
ast::ElseBranch::Block(it) => it,

View File

@ -1,8 +1,5 @@
use syntax::{
ast::{
edit::AstNodeEdit, make, AstNode, BlockExpr, Condition, ElseBranch, Expr, IfExpr, MatchArm,
Pat,
},
ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat},
SyntaxKind::WHITESPACE,
};
@ -44,18 +41,11 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
}
let space_before_guard = guard.syntax().prev_sibling_or_token();
// FIXME: support `if let` guards too
if guard.let_token().is_some() {
return None;
}
let guard_condition = guard.expr()?;
let guard_condition = guard.condition()?;
let arm_expr = match_arm.expr()?;
let if_expr = make::expr_if(
make::condition(guard_condition, None),
make::block_expr(None, Some(arm_expr.clone())),
None,
)
.indent(arm_expr.indent_level());
let if_expr =
make::expr_if(guard_condition, make::block_expr(None, Some(arm_expr.clone())), None)
.indent(arm_expr.indent_level());
let target = guard.syntax().text_range();
acc.add(
@ -193,17 +183,13 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
)
}
// Parses an if-else-if chain to get the conditons and the then branches until we encounter an else
// Parses an if-else-if chain to get the conditions and the then branches until we encounter an else
// branch or the end.
fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Condition, BlockExpr)>, Option<BlockExpr>)> {
fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option<BlockExpr>)> {
let mut conds_blocks = Vec::new();
let mut curr_if = if_expr;
let tail = loop {
let cond = curr_if.condition()?;
// Not support moving if let to arm guard
if cond.is_pattern_cond() {
return None;
}
conds_blocks.push((cond, curr_if.then_branch()?));
match curr_if.else_branch() {
Some(ElseBranch::IfExpr(e)) => {

View File

@ -60,15 +60,22 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
None
}
});
let scrutinee_to_be_expr = if_expr.condition()?.expr()?;
let scrutinee_to_be_expr = if_expr.condition()?;
let scrutinee_to_be_expr = match scrutinee_to_be_expr.single_let() {
Some(cond) => cond.expr()?,
None => scrutinee_to_be_expr,
};
let mut pat_seen = false;
let mut cond_bodies = Vec::new();
for if_expr in if_exprs {
let cond = if_expr.condition()?;
let expr = cond.expr()?;
let cond = match cond.pat() {
Some(pat) => {
let cond = match cond.single_let() {
Some(let_) => {
let pat = let_.pat()?;
let expr = let_.expr()?;
// FIXME: If one `let` is wrapped in parentheses and the second is not,
// we'll exit here.
if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
// Only if all condition expressions are equal we can merge them into a match
return None;
@ -76,7 +83,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
pat_seen = true;
Either::Left(pat)
}
None => Either::Right(expr),
// Multiple `let`, unsupported.
None if cond.is_pattern_cond() => return None,
None => Either::Right(cond),
};
let body = if_expr.then_branch()?;
cond_bodies.push((cond, body));
@ -217,11 +226,11 @@ fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr {
}
}
let condition = make::condition(scrutinee, Some(if_let_pat));
let condition = make::expr_let(if_let_pat, scrutinee);
let then_block = make_block_expr(then_expr.reset_indent());
let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
let if_let_expr = make::expr_if(
condition,
condition.into(),
then_block,
else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
)

View File

@ -62,7 +62,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
let block =
make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax()));
let if_ = make::expr_if(make::condition(init, Some(pat)), block, None);
let if_ = make::expr_if(make::expr_let(pat, init).into(), block, None);
let stmt = make::expr_stmt(if_);
edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));

View File

@ -575,6 +575,14 @@ fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
(ty, name)
},
ast::LetExpr(it) => {
cov_mark::hit!(expected_type_if_let_without_leading_char);
let ty = it.pat()
.and_then(|pat| self.sema.type_of_pat(&pat))
.or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it)))
.map(TypeInfo::original);
(ty, None)
},
ast::ArgList(_) => {
cov_mark::hit!(expected_type_fn_param);
ActiveParameter::at_token(
@ -641,9 +649,7 @@ fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
(ty, None)
},
ast::IfExpr(it) => {
cov_mark::hit!(expected_type_if_let_without_leading_char);
let ty = it.condition()
.and_then(|cond| cond.expr())
.and_then(|e| self.sema.type_of_expr(&e))
.map(TypeInfo::original);
(ty, None)
@ -939,7 +945,7 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
return (PatternRefutability::Irrefutable, has_type_ascription)
},
ast::MatchArm(_) => PatternRefutability::Refutable,
ast::Condition(_) => PatternRefutability::Refutable,
ast::LetExpr(_) => PatternRefutability::Refutable,
ast::ForExpr(_) => PatternRefutability::Irrefutable,
_ => PatternRefutability::Irrefutable,
}

View File

@ -226,6 +226,7 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
| ast::Expr::TryExpr(_)
| ast::Expr::TupleExpr(_)
| ast::Expr::WhileExpr(_)
| ast::Expr::LetExpr(_)
| ast::Expr::YieldExpr(_) => cb(expr),
}
}