1660: implement while let desugaring r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-08-07 13:16:06 +00:00
commit c5b44975b8
4 changed files with 109 additions and 51 deletions

View File

@ -11,17 +11,16 @@ use ra_syntax::{
}, },
AstNode, AstPtr, SyntaxNodePtr, AstNode, AstPtr, SyntaxNodePtr,
}; };
use test_utils::tested_by;
use crate::{ use crate::{
name::{AsName, SELF_PARAM}, name::{AsName, SELF_PARAM},
path::GenericArgs,
ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy},
type_ref::{Mutability, TypeRef}, type_ref::{Mutability, TypeRef},
DefWithBody, Either, HasSource, HirDatabase, HirFileId, MacroCallLoc, MacroFileKind, Name, DefWithBody, Either, HasSource, HirDatabase, HirFileId, MacroCallLoc, MacroFileKind, Name,
Path, Resolver, Path, Resolver,
}; };
use crate::{
path::GenericArgs,
ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy},
};
pub use self::scope::ExprScopes; pub use self::scope::ExprScopes;
@ -558,37 +557,40 @@ where
let syntax_ptr = SyntaxNodePtr::new(expr.syntax()); let syntax_ptr = SyntaxNodePtr::new(expr.syntax());
match expr.kind() { match expr.kind() {
ast::ExprKind::IfExpr(e) => { ast::ExprKind::IfExpr(e) => {
if let Some(pat) = e.condition().and_then(|c| c.pat()) { let then_branch = self.collect_block_opt(e.then_branch());
// if let -- desugar to match
let pat = self.collect_pat(pat); let else_branch = e.else_branch().map(|b| match b {
let match_expr = ast::ElseBranch::Block(it) => self.collect_block(it),
self.collect_expr_opt(e.condition().expect("checked above").expr()); ast::ElseBranch::IfExpr(elif) => {
let then_branch = self.collect_block_opt(e.then_branch()); let expr: ast::Expr = ast::Expr::cast(elif.syntax().clone()).unwrap();
let else_branch = e self.collect_expr(expr)
.else_branch() }
.map(|b| match b { });
ast::ElseBranch::Block(it) => self.collect_block(it),
ast::ElseBranch::IfExpr(elif) => self.collect_expr(elif.into()), let condition = match e.condition() {
}) None => self.exprs.alloc(Expr::Missing),
.unwrap_or_else(|| self.empty_block()); Some(condition) => match condition.pat() {
let placeholder_pat = self.pats.alloc(Pat::Missing); None => self.collect_expr_opt(condition.expr()),
let arms = vec![ // if let -- desugar to match
MatchArm { pats: vec![pat], expr: then_branch, guard: None }, Some(pat) => {
MatchArm { pats: vec![placeholder_pat], expr: else_branch, guard: None }, let pat = self.collect_pat(pat);
]; let match_expr = self.collect_expr_opt(condition.expr());
self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr) let placeholder_pat = self.pats.alloc(Pat::Missing);
} else { let arms = vec![
let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr())); MatchArm { pats: vec![pat], expr: then_branch, guard: None },
let then_branch = self.collect_block_opt(e.then_branch()); MatchArm {
let else_branch = e.else_branch().map(|b| match b { pats: vec![placeholder_pat],
ast::ElseBranch::Block(it) => self.collect_block(it), expr: else_branch.unwrap_or_else(|| self.empty_block()),
ast::ElseBranch::IfExpr(elif) => { guard: None,
let expr: ast::Expr = ast::Expr::cast(elif.syntax().clone()).unwrap(); },
self.collect_expr(expr) ];
return self
.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr);
} }
}); },
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr) };
}
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
} }
ast::ExprKind::TryBlockExpr(e) => { ast::ExprKind::TryBlockExpr(e) => {
let body = self.collect_block_opt(e.try_body()); let body = self.collect_block_opt(e.try_body());
@ -600,17 +602,30 @@ where
self.alloc_expr(Expr::Loop { body }, syntax_ptr) self.alloc_expr(Expr::Loop { body }, syntax_ptr)
} }
ast::ExprKind::WhileExpr(e) => { ast::ExprKind::WhileExpr(e) => {
let condition = if let Some(condition) = e.condition() {
if condition.pat().is_none() {
self.collect_expr_opt(condition.expr())
} else {
// FIXME handle while let
return self.alloc_expr(Expr::Missing, syntax_ptr);
}
} else {
self.exprs.alloc(Expr::Missing)
};
let body = self.collect_block_opt(e.loop_body()); let body = self.collect_block_opt(e.loop_body());
let condition = match e.condition() {
None => self.exprs.alloc(Expr::Missing),
Some(condition) => match condition.pat() {
None => self.collect_expr_opt(condition.expr()),
// if let -- desugar to match
Some(pat) => {
tested_by!(infer_while_let);
let pat = self.collect_pat(pat);
let match_expr = self.collect_expr_opt(condition.expr());
let placeholder_pat = self.pats.alloc(Pat::Missing);
let break_ = self.exprs.alloc(Expr::Break { expr: None });
let arms = vec![
MatchArm { pats: vec![pat], expr: body, guard: None },
MatchArm { pats: vec![placeholder_pat], expr: break_, guard: None },
];
let match_expr =
self.exprs.alloc(Expr::Match { expr: match_expr, arms });
return self.alloc_expr(Expr::Loop { body: match_expr }, syntax_ptr);
}
},
};
self.alloc_expr(Expr::While { condition, body }, syntax_ptr) self.alloc_expr(Expr::While { condition, body }, syntax_ptr)
} }
ast::ExprKind::ForExpr(e) => { ast::ExprKind::ForExpr(e) => {

View File

@ -10,4 +10,5 @@ test_utils::marks!(
std_prelude std_prelude
match_ergonomics_ref match_ergonomics_ref
trait_resolution_on_fn_type trait_resolution_on_fn_type
infer_while_let
); );

View File

@ -144,6 +144,26 @@ mod collections {
assert_eq!("&str", type_at_pos(&db, pos)); assert_eq!("&str", type_at_pos(&db, pos));
} }
#[test]
fn infer_while_let() {
covers!(infer_while_let);
let (db, pos) = MockDatabase::with_position(
r#"
//- /main.rs
enum Option<T> { Some(T), None }
fn test() {
let foo: Option<f32> = None;
while let Option::Some(x) = foo {
<|>x
}
}
"#,
);
assert_eq!("f32", type_at_pos(&db, pos));
}
#[test] #[test]
fn infer_basics() { fn infer_basics() {
assert_snapshot_matches!( assert_snapshot_matches!(

View File

@ -414,13 +414,35 @@ fn main() {
}"#, }"#,
); );
assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r###"
InlayHint { [
range: [166; 170), InlayHint {
kind: TypeHint, range: [166; 170),
label: "CustomOption<Test>", kind: TypeHint,
}, label: "CustomOption<Test>",
]"# },
InlayHint {
range: [343; 347),
kind: TypeHint,
label: "&Test",
},
InlayHint {
range: [401; 402),
kind: TypeHint,
label: "&CustomOption<u32>",
},
InlayHint {
range: [404; 405),
kind: TypeHint,
label: "&u8",
},
InlayHint {
range: [549; 550),
kind: TypeHint,
label: "&u32",
},
]
"###
); );
} }