diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs index eeb4ff39f4..2c602a79ee 100644 --- a/crates/ra_assists/src/assists/add_explicit_type.rs +++ b/crates/ra_assists/src/assists/add_explicit_type.rs @@ -73,6 +73,24 @@ mod tests { ); } + #[test] + fn add_explicit_type_works_for_macro_call() { + check_assist( + add_explicit_type, + "macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", + "macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }", + ); + } + + #[test] + fn add_explicit_type_works_for_macro_call_recursive() { + check_assist( + add_explicit_type, + "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }", + "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }", + ); + } + #[test] fn add_explicit_type_not_applicable_if_ty_not_inferred() { check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }"); diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 85b378483c..2c422af8bd 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -215,8 +215,32 @@ impl SourceAnalyzer { self.body_source_map.as_ref()?.node_pat(src) } + fn expand_expr( + &self, + db: &impl HirDatabase, + expr: InFile<&ast::Expr>, + ) -> Option> { + let macro_call = ast::MacroCall::cast(expr.value.syntax().clone())?; + let macro_file = + self.body_source_map.as_ref()?.node_macro_file(expr.with_value(¯o_call))?; + let expanded = db.parse_or_expand(macro_file)?; + let kind = expanded.kind(); + let expr = InFile::new(macro_file, ast::Expr::cast(expanded)?); + + if ast::MacroCall::can_cast(kind) { + self.expand_expr(db, expr.as_ref()) + } else { + Some(expr) + } + } + pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option { - let expr_id = self.expr_id(expr)?; + let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { + self.body_source_map.as_ref()?.node_expr(expr.as_ref())? + } else { + self.expr_id(expr)? + }; + let ty = self.infer.as_ref()?[expr_id].clone(); let environment = TraitEnvironment::lower(db, &self.resolver); Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index d3e4c50ae0..142c52d358 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -163,6 +163,7 @@ pub struct BodySourceMap { pat_map: FxHashMap, pat_map_back: ArenaMap, field_map: FxHashMap<(ExprId, usize), AstPtr>, + expansions: FxHashMap>, HirFileId>, } impl Body { @@ -237,6 +238,11 @@ impl BodySourceMap { self.expr_map.get(&src).cloned() } + pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option { + let src = node.map(|it| AstPtr::new(it)); + self.expansions.get(&src).cloned() + } + pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option { let src = node.map(|it| Either::Right(AstPtr::new(it))); self.expr_map.get(&src).cloned() diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index fc3a028e0d..e656f9a41b 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -446,14 +446,20 @@ where } } // FIXME expand to statements in statement position - ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) { - Some((mark, expansion)) => { - let id = self.collect_expr(expansion); - self.expander.exit(self.db, mark); - id + ast::Expr::MacroCall(e) => { + let macro_call = self.expander.to_source(AstPtr::new(&e)); + match self.expander.enter_expand(self.db, e.clone()) { + Some((mark, expansion)) => { + self.source_map + .expansions + .insert(macro_call, self.expander.current_file_id); + let id = self.collect_expr(expansion); + self.expander.exit(self.db, mark); + id + } + None => self.alloc_expr(Expr::Missing, syntax_ptr), } - None => self.alloc_expr(Expr::Missing, syntax_ptr), - }, + } // FIXME implement HIR for these: ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),