From 16156d766dd8a5624d9455ffded6358fe879c416 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Mon, 23 Dec 2019 13:18:28 +0800 Subject: [PATCH 1/2] Add macro call support for type_of --- .../src/assists/add_explicit_type.rs | 18 +++++++++++++ crates/ra_hir/src/source_binder.rs | 26 +++++++++++++++++-- crates/ra_hir_def/src/body.rs | 16 +++++------- crates/ra_hir_def/src/lib.rs | 2 +- 4 files changed, 49 insertions(+), 13 deletions(-) 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..3af477818a 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -17,7 +17,7 @@ use hir_def::{ nameres::ModuleSource, path::path, resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs}, - AssocItemId, DefWithBodyId, + AssocItemId, DefWithBodyId, Expander, }; use hir_expand::{ hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, @@ -216,7 +216,14 @@ impl SourceAnalyzer { } 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(macro_call) = ast::MacroCall::cast(expr.syntax().clone()) { + let mut expander = Expander::new(db, self.file_id, self.body_owner?.module(db).id); + let expr = expand_macro_call_to_expr(db, &mut expander, macro_call)?; + 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 } }) @@ -501,6 +508,21 @@ fn scope_for_offset( }) } +fn expand_macro_call_to_expr( + db: &impl HirDatabase, + expander: &mut Expander, + macro_call: ast::MacroCall, +) -> Option> { + let (mark, expr): (_, ast::Expr) = expander.enter_expand(db, macro_call)?; + let expr = if let Some(child) = ast::MacroCall::cast(expr.syntax().clone()) { + expand_macro_call_to_expr(db, expander, child) + } else { + Some(expander.to_source(expr)) + }; + expander.exit(db, mark); + expr +} + // XXX: during completion, cursor might be outside of any particular // expression. Try to figure out the correct scope... fn adjust( diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index d3e4c50ae0..8ab92b23a8 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -26,7 +26,7 @@ use crate::{ DefWithBodyId, HasModule, Lookup, ModuleId, }; -pub(crate) struct Expander { +pub struct Expander { crate_def_map: Arc, current_file_id: HirFileId, hygiene: Hygiene, @@ -35,18 +35,14 @@ pub(crate) struct Expander { } impl Expander { - pub(crate) fn new( - db: &impl DefDatabase, - current_file_id: HirFileId, - module: ModuleId, - ) -> Expander { + pub fn new(db: &impl DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { let crate_def_map = db.crate_def_map(module.krate); let hygiene = Hygiene::new(db, current_file_id); let ast_id_map = db.ast_id_map(current_file_id); Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module } } - pub(crate) fn enter_expand( + pub fn enter_expand( &mut self, db: &DB, macro_call: ast::MacroCall, @@ -84,14 +80,14 @@ impl Expander { None } - pub(crate) fn exit(&mut self, db: &impl DefDatabase, mut mark: Mark) { + pub fn exit(&mut self, db: &impl DefDatabase, mut mark: Mark) { self.hygiene = Hygiene::new(db, mark.file_id); self.current_file_id = mark.file_id; self.ast_id_map = mem::take(&mut mark.ast_id_map); mark.bomb.defuse(); } - pub(crate) fn to_source(&self, value: T) -> InFile { + pub fn to_source(&self, value: T) -> InFile { InFile { file_id: self.current_file_id, value } } @@ -116,7 +112,7 @@ impl Expander { } } -pub(crate) struct Mark { +pub struct Mark { file_id: HirFileId, ast_id_map: Arc, bomb: DropBomb, diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index f6c7f38d17..f58ce9a955 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -48,7 +48,7 @@ use ra_arena::{impl_arena_id, RawId}; use ra_db::{impl_intern_key, salsa, CrateId}; use ra_syntax::{ast, AstNode}; -use crate::body::Expander; +pub use crate::body::Expander; use crate::builtin_type::BuiltinType; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] From 12cc2ed5572e4df8cfbdaef79ed3c865fda0764c Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Mon, 23 Dec 2019 21:47:11 +0800 Subject: [PATCH 2/2] Use pre-stored macro file --- crates/ra_hir/src/source_binder.rs | 40 +++++++++++++++-------------- crates/ra_hir_def/src/body.rs | 22 +++++++++++----- crates/ra_hir_def/src/body/lower.rs | 20 ++++++++++----- crates/ra_hir_def/src/lib.rs | 2 +- 4 files changed, 51 insertions(+), 33 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 3af477818a..2c422af8bd 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -17,7 +17,7 @@ use hir_def::{ nameres::ModuleSource, path::path, resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs}, - AssocItemId, DefWithBodyId, Expander, + AssocItemId, DefWithBodyId, }; use hir_expand::{ hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, @@ -215,10 +215,27 @@ 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 = if let Some(macro_call) = ast::MacroCall::cast(expr.syntax().clone()) { - let mut expander = Expander::new(db, self.file_id, self.body_owner?.module(db).id); - let expr = expand_macro_call_to_expr(db, &mut expander, macro_call)?; + 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)? @@ -508,21 +525,6 @@ fn scope_for_offset( }) } -fn expand_macro_call_to_expr( - db: &impl HirDatabase, - expander: &mut Expander, - macro_call: ast::MacroCall, -) -> Option> { - let (mark, expr): (_, ast::Expr) = expander.enter_expand(db, macro_call)?; - let expr = if let Some(child) = ast::MacroCall::cast(expr.syntax().clone()) { - expand_macro_call_to_expr(db, expander, child) - } else { - Some(expander.to_source(expr)) - }; - expander.exit(db, mark); - expr -} - // XXX: during completion, cursor might be outside of any particular // expression. Try to figure out the correct scope... fn adjust( diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 8ab92b23a8..142c52d358 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -26,7 +26,7 @@ use crate::{ DefWithBodyId, HasModule, Lookup, ModuleId, }; -pub struct Expander { +pub(crate) struct Expander { crate_def_map: Arc, current_file_id: HirFileId, hygiene: Hygiene, @@ -35,14 +35,18 @@ pub struct Expander { } impl Expander { - pub fn new(db: &impl DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { + pub(crate) fn new( + db: &impl DefDatabase, + current_file_id: HirFileId, + module: ModuleId, + ) -> Expander { let crate_def_map = db.crate_def_map(module.krate); let hygiene = Hygiene::new(db, current_file_id); let ast_id_map = db.ast_id_map(current_file_id); Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module } } - pub fn enter_expand( + pub(crate) fn enter_expand( &mut self, db: &DB, macro_call: ast::MacroCall, @@ -80,14 +84,14 @@ impl Expander { None } - pub fn exit(&mut self, db: &impl DefDatabase, mut mark: Mark) { + pub(crate) fn exit(&mut self, db: &impl DefDatabase, mut mark: Mark) { self.hygiene = Hygiene::new(db, mark.file_id); self.current_file_id = mark.file_id; self.ast_id_map = mem::take(&mut mark.ast_id_map); mark.bomb.defuse(); } - pub fn to_source(&self, value: T) -> InFile { + pub(crate) fn to_source(&self, value: T) -> InFile { InFile { file_id: self.current_file_id, value } } @@ -112,7 +116,7 @@ impl Expander { } } -pub struct Mark { +pub(crate) struct Mark { file_id: HirFileId, ast_id_map: Arc, bomb: DropBomb, @@ -159,6 +163,7 @@ pub struct BodySourceMap { pat_map: FxHashMap, pat_map_back: ArenaMap, field_map: FxHashMap<(ExprId, usize), AstPtr>, + expansions: FxHashMap>, HirFileId>, } impl Body { @@ -233,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 5323af0972..57c77304cb 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), diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index f58ce9a955..f6c7f38d17 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -48,7 +48,7 @@ use ra_arena::{impl_arena_id, RawId}; use ra_db::{impl_intern_key, salsa, CrateId}; use ra_syntax::{ast, AstNode}; -pub use crate::body::Expander; +use crate::body::Expander; use crate::builtin_type::BuiltinType; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]