From 619af1e22cb71b981fde4cedbf6ebce9b3488028 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 27 Jan 2019 00:23:07 +0300 Subject: fix AST for if expressions then is not always a block... --- crates/ra_hir/src/expr.rs | 16 +++++++- .../src/ty/snapshots/tests__infer_in_elseif.snap | 17 +++++++++ crates/ra_hir/src/ty/tests.rs | 17 +++++++++ .../src/assists/replace_if_let_with_match.rs | 5 ++- crates/ra_syntax/src/ast.rs | 18 ++++++++- crates/ra_syntax/src/ast/generated.rs | 44 ++++++++++++++++++++++ 6 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 crates/ra_hir/src/ty/snapshots/tests__infer_in_elseif.snap diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 29469af2c..60d997bbe 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -498,7 +498,13 @@ impl ExprCollector { let then_branch = self.collect_block_opt(e.then_branch()); let else_branch = e .else_branch() - .map(|e| self.collect_block(e)) + .map(|b| match b { + ast::ElseBranchFlavor::Block(it) => self.collect_block(it), + ast::ElseBranchFlavor::IfExpr(elif) => { + let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap(); + self.collect_expr(expr) + } + }) .unwrap_or_else(|| self.empty_block()); let placeholder_pat = self.pats.alloc(Pat::Missing); let arms = vec![ @@ -521,7 +527,13 @@ impl ExprCollector { } else { let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr())); let then_branch = self.collect_block_opt(e.then_branch()); - let else_branch = e.else_branch().map(|e| self.collect_block(e)); + let else_branch = e.else_branch().map(|b| match b { + ast::ElseBranchFlavor::Block(it) => self.collect_block(it), + ast::ElseBranchFlavor::IfExpr(elif) => { + let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap(); + self.collect_expr(expr) + } + }); self.alloc_expr( Expr::If { condition, diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_in_elseif.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_in_elseif.snap new file mode 100644 index 000000000..6a435e5cf --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_in_elseif.snap @@ -0,0 +1,17 @@ +--- +created: "2019-01-26T21:36:52.714121185+00:00" +creator: insta@0.5.2 +expression: "&result" +source: crates/ra_hir/src/ty/tests.rs +--- +[35; 38) 'foo': Foo +[45; 109) '{ ... } }': () +[51; 107) 'if tru... }': () +[54; 58) 'true': bool +[59; 67) '{ }': () +[73; 107) 'if fal... }': i32 +[76; 81) 'false': bool +[82; 107) '{ ... }': i32 +[92; 95) 'foo': Foo +[92; 101) 'foo.field': i32 + diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index e0b0689f8..e1165f682 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -284,6 +284,23 @@ fn test() { ); } +#[test] +fn infer_in_elseif() { + check_inference( + "infer_in_elseif", + r#" +struct Foo { field: i32 } +fn main(foo: Foo) { + if true { + + } else if false { + foo.field + } +} +"#, + ) +} + #[test] fn infer_inherent_method() { check_inference( diff --git a/crates/ra_ide_api_light/src/assists/replace_if_let_with_match.rs b/crates/ra_ide_api_light/src/assists/replace_if_let_with_match.rs index d64c34d54..71880b919 100644 --- a/crates/ra_ide_api_light/src/assists/replace_if_let_with_match.rs +++ b/crates/ra_ide_api_light/src/assists/replace_if_let_with_match.rs @@ -11,7 +11,10 @@ pub fn replace_if_let_with_match(ctx: AssistCtx) -> Option { let pat = cond.pat()?; let expr = cond.expr()?; let then_block = if_expr.then_branch()?; - let else_block = if_expr.else_branch()?; + let else_block = match if_expr.else_branch()? { + ast::ElseBranchFlavor::Block(it) => it, + ast::ElseBranchFlavor::IfExpr(_) => return None, + }; ctx.build("replace with match", |edit| { let match_expr = build_match_expr(expr, pat, then_block, else_block); diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 00c60ebf3..ab3dd1b84 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -285,13 +285,27 @@ impl LetStmt { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ElseBranchFlavor<'a> { + Block(&'a Block), + IfExpr(&'a IfExpr), +} + impl IfExpr { pub fn then_branch(&self) -> Option<&Block> { self.blocks().nth(0) } - pub fn else_branch(&self) -> Option<&Block> { - self.blocks().nth(1) + pub fn else_branch(&self) -> Option { + let res = match self.blocks().nth(1) { + Some(block) => ElseBranchFlavor::Block(block), + None => { + let elif: &IfExpr = child_opt(self)?; + ElseBranchFlavor::IfExpr(elif) + } + }; + Some(res) } + fn blocks(&self) -> AstChildren { children(self) } diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 3ace6533c..4f8723ae7 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -660,6 +660,50 @@ impl ToOwned for DynTraitType { impl DynTraitType {} +// ElseBranch +#[derive(Debug, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct ElseBranch { + pub(crate) syntax: SyntaxNode, +} +unsafe impl TransparentNewType for ElseBranch { + type Repr = rowan::SyntaxNode; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ElseBranchKind<'a> { + Block(&'a Block), + IfExpr(&'a IfExpr), +} + +impl AstNode for ElseBranch { + fn cast(syntax: &SyntaxNode) -> Option<&Self> { + match syntax.kind() { + | BLOCK + | IF_EXPR => Some(ElseBranch::from_repr(syntax.into_repr())), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} + +impl ToOwned for ElseBranch { + type Owned = TreeArc; + fn to_owned(&self) -> TreeArc { TreeArc::cast(self.syntax.to_owned()) } +} + +impl ElseBranch { + pub fn kind(&self) -> ElseBranchKind { + match self.syntax.kind() { + BLOCK => ElseBranchKind::Block(Block::cast(&self.syntax).unwrap()), + IF_EXPR => ElseBranchKind::IfExpr(IfExpr::cast(&self.syntax).unwrap()), + _ => unreachable!(), + } + } +} + +impl ElseBranch {} + // EnumDef #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] -- cgit v1.2.3