Repository: rust-analyzer/rust-analyzer
Pull request #683 (2019-01-26 21:23:41)

fix AST for if expressions


Patch:
diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs
index 418d59c91e..480ec27bf5 100644
--- a/crates/ra_hir/src/code_model_impl/module.rs
+++ b/crates/ra_hir/src/code_model_impl/module.rs
@@ -25,10 +25,9 @@ impl Module {
 
     pub(crate) fn definition_source_impl(&self, db: &impl HirDatabase) -> (FileId, ModuleSource) {
         let module_tree = db.module_tree(self.krate);
-        let file_id = self.module_id.file_id(&module_tree);
-        let decl_id = self.module_id.decl_id(&module_tree);
-        let module_source = ModuleSource::new(db, file_id, decl_id);
-        let file_id = file_id.as_original_file();
+        let source = self.module_id.source(&module_tree);
+        let module_source = ModuleSource::from_source_item_id(db, source);
+        let file_id = source.file_id.as_original_file();
         (file_id, module_source)
     }
 
@@ -40,7 +39,8 @@ impl Module {
         let link = self.module_id.parent_link(&module_tree)?;
         let file_id = link
             .owner(&module_tree)
-            .file_id(&module_tree)
+            .source(&module_tree)
+            .file_id
             .as_original_file();
         let src = link.source(&module_tree, db);
         Some((file_id, src))
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 3f76b769dd..5df4bd4a15 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -16,7 +16,6 @@ use crate::{
     adt::{StructData, EnumData},
     impl_block::ModuleImplBlocks,
     generics::{GenericParams, GenericDef},
-    ids::SourceFileItemId,
 };
 
 #[salsa::query_group(HirDatabaseStorage)]
@@ -52,11 +51,7 @@ pub trait HirDatabase: SourceDatabase + AsRef<HirInterner> {
     fn file_item(&self, source_item_id: SourceItemId) -> TreeArc<SyntaxNode>;
 
     #[salsa::invoke(crate::module_tree::Submodule::submodules_query)]
-    fn submodules(
-        &self,
-        file_id: HirFileId,
-        delc_id: Option<SourceFileItemId>,
-    ) -> Arc<Vec<crate::module_tree::Submodule>>;
+    fn submodules(&self, source: SourceItemId) -> Arc<Vec<crate::module_tree::Submodule>>;
 
     #[salsa::invoke(crate::nameres::lower::LoweredModule::lower_module_query)]
     fn lower_module(&self, module: Module) -> (Arc<LoweredModule>, Arc<ImportSourceMap>);
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 29469af2cf..60d997bbe5 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/ids.rs b/crates/ra_hir/src/ids.rs
index 0e4dc62614..7dd4b540e7 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -4,7 +4,7 @@ use std::{
 };
 
 use ra_db::{LocationIntener, FileId};
-use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, SyntaxNodePtr, ast};
+use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, ast};
 use ra_arena::{Arena, RawId, ArenaId, impl_arena_id};
 
 use crate::{
@@ -203,7 +203,7 @@ pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone {
         let items = ctx.db.file_items(ctx.file_id);
         let raw = SourceItemId {
             file_id: ctx.file_id,
-            item_id: items.id_of(ctx.file_id, ast.syntax()),
+            item_id: Some(items.id_of(ctx.file_id, ast.syntax())),
         };
         let loc = ItemLoc {
             module: ctx.module,
@@ -301,14 +301,15 @@ impl_arena_id!(SourceFileItemId);
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct SourceItemId {
     pub(crate) file_id: HirFileId,
-    pub(crate) item_id: SourceFileItemId,
+    /// None for the whole file.
+    pub(crate) item_id: Option<SourceFileItemId>,
 }
 
 /// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back.
 #[derive(Debug, PartialEq, Eq)]
 pub struct SourceFileItems {
     file_id: HirFileId,
-    arena: Arena<SourceFileItemId, SyntaxNodePtr>,
+    arena: Arena<SourceFileItemId, TreeArc<SyntaxNode>>,
 }
 
 impl SourceFileItems {
@@ -328,15 +329,15 @@ impl SourceFileItems {
         // trait does not chage ids of top-level items, which helps caching.
         bfs(source_file.syntax(), |it| {
             if let Some(module_item) = ast::ModuleItem::cast(it) {
-                self.alloc(module_item.syntax());
+                self.alloc(module_item.syntax().to_owned());
             } else if let Some(macro_call) = ast::MacroCall::cast(it) {
-                self.alloc(macro_call.syntax());
+                self.alloc(macro_call.syntax().to_owned());
             }
         })
     }
 
-    fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId {
-        self.arena.alloc(SyntaxNodePtr::new(item))
+    fn alloc(&mut self, item: TreeArc<SyntaxNode>) -> SourceFileItemId {
+        self.arena.alloc(item)
     }
     pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId {
         assert_eq!(
@@ -347,8 +348,17 @@ impl SourceFileItems {
         self.id_of_unchecked(item)
     }
     pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId {
-        let ptr = SyntaxNodePtr::new(item);
-        if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) {
+        if let Some((id, _)) = self.arena.iter().find(|(_id, i)| *i == item) {
+            return id;
+        }
+        // This should not happen. Let's try to give a sensible diagnostics.
+        if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
+            // FIXME(#288): whyyy are we getting here?
+            log::error!(
+                "unequal syntax nodes with the same range:\n{:?}\n{:?}",
+                item,
+                i
+            );
             return id;
         }
         panic!(
@@ -357,11 +367,15 @@ impl SourceFileItems {
             self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
         );
     }
+    pub fn id_of_parse(&self) -> SourceFileItemId {
+        let (id, _syntax) = self.arena.iter().next().unwrap();
+        id
+    }
 }
 
 impl std::ops::Index<SourceFileItemId> for SourceFileItems {
-    type Output = SyntaxNodePtr;
-    fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr {
+    type Output = SyntaxNode;
+    fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
         &self.arena[idx]
     }
 }
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs
index d1dc3fa4b7..d5ad9decbf 100644
--- a/crates/ra_hir/src/module_tree.rs
+++ b/crates/ra_hir/src/module_tree.rs
@@ -11,28 +11,21 @@ use ra_syntax::{
 use ra_arena::{Arena, RawId, impl_arena_id};
 use test_utils::tested_by;
 
-use crate::{
-    Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource,
-    ids::SourceFileItemId,
-};
+use crate::{Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource};
 
 impl ModuleSource {
-    pub(crate) fn new(
+    pub(crate) fn from_source_item_id(
         db: &impl HirDatabase,
-        file_id: HirFileId,
-        decl_id: Option<SourceFileItemId>,
+        source_item_id: SourceItemId,
     ) -> ModuleSource {
-        match decl_id {
-            Some(item_id) => {
-                let module = db.file_item(SourceItemId { file_id, item_id });
-                let module = ast::Module::cast(&*module).unwrap();
-                assert!(module.item_list().is_some(), "expected inline module");
-                ModuleSource::Module(module.to_owned())
-            }
-            None => {
-                let source_file = db.hir_parse(file_id);
-                ModuleSource::SourceFile(source_file)
-            }
+        let module_syntax = db.file_item(source_item_id);
+        if let Some(source_file) = ast::SourceFile::cast(&module_syntax) {
+            ModuleSource::SourceFile(source_file.to_owned())
+        } else if let Some(module) = ast::Module::cast(&module_syntax) {
+            assert!(module.item_list().is_some(), "expected inline module");
+            ModuleSource::Module(module.to_owned())
+        } else {
+            panic!("expected file or inline module")
         }
     }
 }
@@ -41,18 +34,18 @@ impl ModuleSource {
 pub struct Submodule {
     name: Name,
     is_declaration: bool,
-    decl_id: SourceFileItemId,
+    source: SourceItemId,
 }
 
 impl Submodule {
     pub(crate) fn submodules_query(
         db: &impl HirDatabase,
-        file_id: HirFileId,
-        decl_id: Option<SourceFileItemId>,
+        source: SourceItemId,
     ) -> Arc<Vec<Submodule>> {
         db.check_canceled();
+        let file_id = source.file_id;
         let file_items = db.file_items(file_id);
-        let module_source = ModuleSource::new(db, file_id, decl_id);
+        let module_source = ModuleSource::from_source_item_id(db, source);
         let submodules = match module_source {
             ModuleSource::SourceFile(source_file) => {
                 collect_submodules(file_id, &file_items, &*source_file)
@@ -61,7 +54,6 @@ impl Submodule {
                 collect_submodules(file_id, &file_items, module.item_list().unwrap())
             }
         };
-
         return Arc::new(submodules);
 
         fn collect_submodules(
@@ -83,7 +75,10 @@ impl Submodule {
                     let sub = Submodule {
                         name,
                         is_declaration: module.has_semi(),
-                        decl_id: file_items.id_of(file_id, module.syntax()),
+                        source: SourceItemId {
+                            file_id,
+                            item_id: Some(file_items.id_of(file_id, module.syntax())),
+                        },
                     };
                     Some(sub)
                 })
@@ -115,9 +110,7 @@ pub struct ModuleTree {
 
 #[derive(Debug, PartialEq, Eq, Hash)]
 pub struct ModuleData {
-    file_id: HirFileId,
-    /// Points to `ast::Module`, `None` for the whole file.
-    decl_id: Option<SourceFileItemId>,
+    source: SourceItemId,
     parent: Option<LinkId>,
     children: Vec<LinkId>,
 }
@@ -143,15 +136,8 @@ impl ModuleTree {
         self.mods.iter().map(|(id, _)| id)
     }
 
-    pub(crate) fn find_module_by_source(
-        &self,
-        file_id: HirFileId,
-        decl_id: Option<SourceFileItemId>,
-    ) -> Option<ModuleId> {
-        let (res, _) = self
-            .mods
-            .iter()
-            .find(|(_, m)| (m.file_id, m.decl_id) == (file_id, decl_id))?;
+    pub(crate) fn find_module_by_source(&self, source: SourceItemId) -> Option<ModuleId> {
+        let (res, _) = self.mods.iter().find(|(_, m)| m.source == source)?;
         Some(res)
     }
 
@@ -161,7 +147,11 @@ impl ModuleTree {
         let source_root_id = db.file_source_root(file_id);
 
         let source_root = db.source_root(source_root_id);
-        self.init_subtree(db, &source_root, None, file_id.into(), None);
+        let source = SourceItemId {
+            file_id: file_id.into(),
+            item_id: None,
+        };
+        self.init_subtree(db, &source_root, None, source);
     }
 
     fn init_subtree(
@@ -169,21 +159,16 @@ impl ModuleTree {
         db: &impl HirDatabase,
         source_root: &SourceRoot,
         parent: Option<LinkId>,
-        file_id: HirFileId,
-        decl_id: Option<SourceFileItemId>,
+        source: SourceItemId,
     ) -> ModuleId {
         let id = self.alloc_mod(ModuleData {
-            file_id,
-            decl_id,
+            source,
             parent,
             children: Vec::new(),
         });
-        for sub in db.submodules(file_id, decl_id).iter() {
+        for sub in db.submodules(source).iter() {
             let link = self.alloc_link(LinkData {
-                source: SourceItemId {
-                    file_id,
-                    item_id: sub.decl_id,
-                },
+                source: sub.source,
                 name: sub.name.clone(),
                 owner: id,
                 points_to: Vec::new(),
@@ -191,17 +176,24 @@ impl ModuleTree {
             });
 
             let (points_to, problem) = if sub.is_declaration {
-                let (points_to, problem) = resolve_submodule(db, file_id, &sub.name);
+                let (points_to, problem) = resolve_submodule(db, source.file_id, &sub.name);
                 let points_to = points_to
                     .into_iter()
                     .map(|file_id| {
-                        self.init_subtree(db, source_root, Some(link), file_id.into(), None)
+                        self.init_subtree(
+                            db,
+                            source_root,
+                            Some(link),
+                            SourceItemId {
+                                file_id: file_id.into(),
+                                item_id: None,
+                            },
+                        )
                     })
                     .collect::<Vec<_>>();
                 (points_to, problem)
             } else {
-                let points_to =
-                    self.init_subtree(db, source_root, Some(link), file_id, Some(sub.decl_id));
+                let points_to = self.init_subtree(db, source_root, Some(link), sub.source);
                 (vec![points_to], None)
             };
 
@@ -224,11 +216,8 @@ impl ModuleTree {
 }
 
 impl ModuleId {
-    pub(crate) fn file_id(self, tree: &ModuleTree) -> HirFileId {
-        tree.mods[self].file_id
-    }
-    pub(crate) fn decl_id(self, tree: &ModuleTree) -> Option<SourceFileItemId> {
-        tree.mods[self].decl_id
+    pub(crate) fn source(self, tree: &ModuleTree) -> SourceItemId {
+        tree.mods[self].source
     }
     pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
         tree.mods[self].parent
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index 97ce6c946d..5193900e01 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -215,7 +215,7 @@ where
         // Populate extern crates prelude
         {
             let root_id = module_id.crate_root(&self.module_tree);
-            let file_id = root_id.file_id(&self.module_tree);
+            let file_id = root_id.source(&self.module_tree).file_id;
             let crate_graph = self.db.crate_graph();
             if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file())
             {
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs
index 8df11a5f41..1d77548f38 100644
--- a/crates/ra_hir/src/nameres/lower.rs
+++ b/crates/ra_hir/src/nameres/lower.rs
@@ -121,7 +121,10 @@ impl LoweredModule {
                     let item_id = file_items.id_of_unchecked(macro_call.syntax());
                     let loc = MacroCallLoc {
                         module,
-                        source_item_id: SourceItemId { file_id, item_id },
+                        source_item_id: SourceItemId {
+                            file_id,
+                            item_id: Some(item_id),
+                        },
                     };
                     let id = loc.id(db);
                     let file_id = HirFileId::from(id);
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index bf9ac0dfb4..61c93a9648 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -4,7 +4,9 @@ use std::{
 };
 
 use rustc_hash::FxHashMap;
-use ra_syntax::{SyntaxNode, TreeArc};
+use ra_syntax::{
+    AstNode, SyntaxNode, TreeArc,
+};
 use ra_db::{CrateId};
 
 use crate::{
@@ -30,10 +32,10 @@ pub(super) fn file_item(
     db: &impl HirDatabase,
     source_item_id: SourceItemId,
 ) -> TreeArc<SyntaxNode> {
-    let source_file = db.hir_parse(source_item_id.file_id);
-    db.file_items(source_item_id.file_id)[source_item_id.item_id]
-        .to_node(&source_file)
-        .to_owned()
+    match source_item_id.item_id {
+        Some(id) => db.file_items(source_item_id.file_id)[id].to_owned(),
+        None => db.hir_parse(source_item_id.file_id).syntax().to_owned(),
+    }
 }
 
 pub(super) fn item_map(db: &impl HirDatabase, crate_id: CrateId) -> Arc<ItemMap> {
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index f523f06476..c0b3f1cd48 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -13,14 +13,18 @@ use ra_syntax::{
 };
 
 use crate::{
-    HirDatabase, Function, ModuleDef,
-    AsName, Module, HirFileId,
-    ids::{LocationCtx, SourceFileItemId},
+    HirDatabase, Function, SourceItemId, ModuleDef,
+    AsName, Module,
+    ids::LocationCtx,
 };
 
 /// Locates the module by `FileId`. Picks topmost module in the file.
 pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Option<Module> {
-    module_from_source(db, file_id.into(), None)
+    let module_source = SourceItemId {
+        file_id: file_id.into(),
+        item_id: None,
+    };
+    module_from_source(db, module_source)
 }
 
 /// Locates the child module by `mod child;` declaration.
@@ -55,7 +59,11 @@ fn module_from_inline(
     let file_id = file_id.into();
     let file_items = db.file_items(file_id);
     let item_id = file_items.id_of(file_id, module.syntax());
-    module_from_source(db, file_id, Some(item_id))
+    let source = SourceItemId {
+        file_id,
+        item_id: Some(item_id),
+    };
+    module_from_source(db, source)
 }
 
 /// Locates the module by child syntax element within the module
@@ -75,17 +83,13 @@ pub fn module_from_child_node(
     }
 }
 
-fn module_from_source(
-    db: &impl HirDatabase,
-    file_id: HirFileId,
-    decl_id: Option<SourceFileItemId>,
-) -> Option<Module> {
-    let source_root_id = db.file_source_root(file_id.as_original_file());
+fn module_from_source(db: &impl HirDatabase, source: SourceItemId) -> Option<Module> {
+    let source_root_id = db.file_source_root(source.file_id.as_original_file());
     db.source_root_crates(source_root_id)
         .iter()
         .find_map(|&krate| {
             let module_tree = db.module_tree(krate);
-            let module_id = module_tree.find_module_by_source(file_id, decl_id)?;
+            let module_id = module_tree.find_module_by_source(source)?;
             Some(Module { krate, module_id })
         })
 }
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 d64c34d54a..71880b9198 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<Assist> {
     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_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index ace3da0208..8ea9edc84f 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -520,33 +520,21 @@ pub fn handle_formatting(
     let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
 
     use std::process;
-    let mut rustfmt = process::Command::new("rustfmt");
-    rustfmt
+    let mut rustfmt = process::Command::new("rustfmt")
         .stdin(process::Stdio::piped())
-        .stdout(process::Stdio::piped());
-
-    if let Ok(path) = params.text_document.uri.to_file_path() {
-        if let Some(parent) = path.parent() {
-            rustfmt.current_dir(parent);
-        }
-    }
-    let mut rustfmt = rustfmt.spawn()?;
+        .stdout(process::Stdio::piped())
+        .spawn()?;
 
     rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
 
     let output = rustfmt.wait_with_output()?;
     let captured_stdout = String::from_utf8(output.stdout)?;
     if !output.status.success() {
-        return Err(LspError::new(
-            -32900,
-            format!(
-                r#"rustfmt exited with:
-            Status: {}
-            stdout: {}"#,
-                output.status, captured_stdout,
-            ),
-        )
-        .into());
+        failure::bail!(
+            "rustfmt exited with error code {}: {}.",
+            output.status,
+            captured_stdout,
+        );
     }
 
     Ok(Some(vec![TextEdit {
diff --git a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs b/crates/ra_lsp_server/src/project_model/cargo_workspace.rs
index 8cf99d5865..75ae78bca7 100644
--- a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs
+++ b/crates/ra_lsp_server/src/project_model/cargo_workspace.rs
@@ -117,13 +117,9 @@ impl Target {
 
 impl CargoWorkspace {
     pub fn from_cargo_metadata(cargo_toml: &Path) -> Result<CargoWorkspace> {
-        let mut meta = MetadataCommand::new();
-        meta.manifest_path(cargo_toml)
-            .features(CargoOpt::AllFeatures);
-        if let Some(parent) = cargo_toml.parent() {
-            meta.current_dir(parent);
-        }
-        let meta = meta
+        let meta = MetadataCommand::new()
+            .manifest_path(cargo_toml)
+            .features(CargoOpt::AllFeatures)
             .exec()
             .map_err(|e| format_err!("cargo metadata failed: {}", e))?;
         let mut pkg_by_id = FxHashMap::default();
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 00c60ebf34..ab3dd1b84d 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<ElseBranchFlavor> {
+        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<Block> {
         children(self)
     }
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 3ace6533c4..4f8723ae7f 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<RaTypes>;
+}
+
+#[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<ElseBranch>;
+    fn to_owned(&self) -> TreeArc<ElseBranch> { 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)]


Test patch:
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 0000000000..6a435e5cfe
--- /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 e0b0689f8f..e1165f6821 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(