Fix various IDE features
As a side benefit, we got `let` guard support for `move_guard` for free.
This commit is contained in:
parent
fe1e324694
commit
13ac5c3491
@ -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
|
||||
|
@ -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 '{}': ()
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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)))),
|
||||
)
|
||||
|
@ -48,8 +48,9 @@ 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.
|
||||
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() {
|
||||
@ -61,12 +62,14 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((path, bound_ident))
|
||||
(Some((path, bound_ident)), let_.expr()?)
|
||||
}
|
||||
Some(_) => return None, // Unsupported IfLet.
|
||||
_ => 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()
|
||||
}
|
||||
|
@ -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 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)
|
||||
}
|
||||
None => {
|
||||
let if_cond = make::condition(invert_boolean_expression(while_cond_expr), 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());
|
||||
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));
|
||||
|
@ -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(
|
||||
call_expr,
|
||||
Some(
|
||||
let condition = make::expr_let(
|
||||
make::tuple_struct_pat(
|
||||
controlflow_break_path,
|
||||
iter::once(make::wildcard_pat().into()),
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
call_expr,
|
||||
);
|
||||
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";
|
||||
|
@ -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,
|
||||
|
@ -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,17 +41,10 @@ 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,
|
||||
)
|
||||
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();
|
||||
@ -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)) => {
|
||||
|
@ -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),
|
||||
)
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user