From cf3b4f1e208247c9d171273dabff9c6b3c98a240 Mon Sep 17 00:00:00 2001 From: cynecx Date: Sat, 10 Apr 2021 17:49:12 +0200 Subject: hir_ty: Expand macros at type position --- crates/hir_def/src/body.rs | 74 +++++++++++++++++++----- crates/hir_def/src/body/lower.rs | 34 +++++++++--- crates/hir_def/src/data.rs | 8 ++- crates/hir_def/src/item_tree/lower.rs | 16 ++++-- crates/hir_def/src/lib.rs | 1 + crates/hir_def/src/path.rs | 7 ++- crates/hir_def/src/path/lower.rs | 17 +++--- crates/hir_def/src/type_ref.rs | 102 ++++++++++++++++++++++++++++++++-- 8 files changed, 210 insertions(+), 49 deletions(-) (limited to 'crates/hir_def/src') diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 96b959967..44ae13643 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs @@ -19,9 +19,9 @@ use hir_expand::{ use la_arena::{Arena, ArenaMap}; use profile::Count; use rustc_hash::FxHashMap; -use syntax::{ast, AstNode, AstPtr}; +use syntax::{ast, AstNode, AstPtr, SyntaxNode}; -pub(crate) use lower::LowerCtx; +pub use lower::LowerCtx; use crate::{ attr::{Attrs, RawAttrs}, @@ -98,11 +98,14 @@ impl Expander { } } - pub(crate) fn enter_expand( + fn enter_expand_intern( &mut self, db: &dyn DefDatabase, macro_call: ast::MacroCall, - ) -> Result>, UnresolvedMacro> { + ) -> Result< + ExpandResult Mark + '_)>>, + UnresolvedMacro, + > { if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { cov_mark::hit!(your_stack_belongs_to_me); return Ok(ExpandResult::str_err( @@ -147,6 +150,55 @@ impl Expander { } }; + let this = self; + + let advance_state = move |db: &dyn DefDatabase| { + this.recursion_limit += 1; + let mark = Mark { + file_id: this.current_file_id, + ast_id_map: mem::take(&mut this.ast_id_map), + bomb: DropBomb::new("expansion mark dropped"), + }; + this.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); + this.current_file_id = file_id; + this.ast_id_map = db.ast_id_map(file_id); + mark + }; + + Ok(ExpandResult { value: Some((raw_node, advance_state)), err }) + } + + pub(crate) fn enter_expand_raw( + &mut self, + db: &dyn DefDatabase, + macro_call: ast::MacroCall, + ) -> Result>, UnresolvedMacro> { + let (raw_node, mut advance_state, err) = match self.enter_expand_intern(db, macro_call)? { + ExpandResult { value: Some((raw_node, advance_state)), err } => { + (raw_node, advance_state, err) + } + ExpandResult { value: None, err } => return Ok(ExpandResult { value: None, err }), + }; + + log::debug!("macro expansion {:#?}", raw_node); + + let mark = advance_state(db); + + Ok(ExpandResult { value: Some((mark, raw_node)), err }) + } + + pub(crate) fn enter_expand( + &mut self, + db: &dyn DefDatabase, + macro_call: ast::MacroCall, + ) -> Result>, UnresolvedMacro> { + let (raw_node, mut advance_state, err) = match self.enter_expand_intern(db, macro_call)? { + ExpandResult { value: Some((raw_node, advance_state)), err } => { + (raw_node, advance_state, err) + } + ExpandResult { value: None, err } => return Ok(ExpandResult { value: None, err }), + }; + let node = match T::cast(raw_node) { Some(it) => it, None => { @@ -157,15 +209,7 @@ impl Expander { log::debug!("macro expansion {:#?}", node.syntax()); - self.recursion_limit += 1; - let mark = Mark { - file_id: self.current_file_id, - ast_id_map: mem::take(&mut self.ast_id_map), - bomb: DropBomb::new("expansion mark dropped"), - }; - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); - self.current_file_id = file_id; - self.ast_id_map = db.ast_id_map(file_id); + let mark = advance_state(db); Ok(ExpandResult { value: Some((mark, node)), err }) } @@ -191,7 +235,8 @@ impl Expander { } fn parse_path(&mut self, path: ast::Path) -> Option { - Path::from_src(path, &self.cfg_expander.hygiene) + let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene); + Path::from_src(path, &ctx) } fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option { @@ -204,6 +249,7 @@ impl Expander { } } +#[derive(Debug)] pub(crate) struct Mark { file_id: HirFileId, ast_id_map: Arc, diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index c0b0b7841..c11da30d2 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -1,10 +1,11 @@ //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` //! representation. -use std::mem; +use std::{mem, sync::Arc}; use either::Either; use hir_expand::{ + ast_id_map::{AstIdMap, FileAstId}, hygiene::Hygiene, name::{name, AsName, Name}, ExpandError, HirFileId, @@ -39,20 +40,39 @@ use crate::{ use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; -pub(crate) struct LowerCtx { +pub struct LowerCtx { hygiene: Hygiene, + file_id: Option, + source_ast_id_map: Option>, } impl LowerCtx { - pub(crate) fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { - LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) } + pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { + LowerCtx { + hygiene: Hygiene::new(db.upcast(), file_id), + file_id: Some(file_id), + source_ast_id_map: Some(db.ast_id_map(file_id)), + } + } + + pub fn with_hygiene(hygiene: &Hygiene) -> Self { + LowerCtx { hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None } + } + + pub(crate) fn hygiene(&self) -> &Hygiene { + &self.hygiene } - pub(crate) fn with_hygiene(hygiene: &Hygiene) -> Self { - LowerCtx { hygiene: hygiene.clone() } + + pub(crate) fn file_id(&self) -> HirFileId { + self.file_id.unwrap() } pub(crate) fn lower_path(&self, ast: ast::Path) -> Option { - Path::from_src(ast, &self.hygiene) + Path::from_src(ast, self) + } + + pub(crate) fn ast_id(&self, item: &N) -> Option> { + self.source_ast_id_map.as_ref().map(|ast_id_map| ast_id_map.ast_id(item)) } } diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index 135a6698e..8732b1e3e 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs @@ -123,10 +123,11 @@ impl TypeAliasData { let loc = typ.lookup(db); let item_tree = loc.id.item_tree(db); let typ = &item_tree[loc.id.value]; + let type_ref = typ.type_ref.clone(); Arc::new(TypeAliasData { name: typ.name.clone(), - type_ref: typ.type_ref.clone(), + type_ref: type_ref, visibility: item_tree[typ.visibility].clone(), is_extern: typ.is_extern, bounds: typ.bounds.to_vec(), @@ -202,12 +203,13 @@ impl ImplData { let item_tree = impl_loc.id.item_tree(db); let impl_def = &item_tree[impl_loc.id.value]; let target_trait = impl_def.target_trait.clone(); - let self_ty = impl_def.self_ty.clone(); let is_negative = impl_def.is_negative; let module_id = impl_loc.container; let container = AssocContainerId::ImplId(id); - let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id); + let file_id = impl_loc.id.file_id(); + let self_ty = impl_def.self_ty.clone(); + let mut expander = Expander::new(db, file_id, module_id); let items = collect_items( db, module_id, diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 45b099cf3..2975786dd 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -189,12 +189,16 @@ impl Ctx { block_stack.push(self.source_ast_id_map.ast_id(&block)); }, ast::Item(item) => { - // FIXME: This triggers for macro calls in expression/pattern/type position - let mod_items = self.lower_mod_item(&item, true); - let current_block = block_stack.last(); - if let (Some(mod_items), Some(block)) = (mod_items, current_block) { - if !mod_items.0.is_empty() { - self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied()); + // FIXME: This triggers for macro calls in expression/pattern + if let Some(SyntaxKind::MACRO_TYPE) = node.parent().map(|p| p.kind()) { + // Ignore macros at type position + } else { + let mod_items = self.lower_mod_item(&item, true); + let current_block = block_stack.last(); + if let (Some(mod_items), Some(block)) = (mod_items, current_block) { + if !mod_items.0.is_empty() { + self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied()); + } } } }, diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 000567d99..059724daa 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs @@ -676,6 +676,7 @@ impl AstIdWithPath { } } +#[derive(Debug)] pub struct UnresolvedMacro { pub path: ModPath, } diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index b528ff8ba..509f77850 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs @@ -48,7 +48,8 @@ pub enum ImportAlias { impl ModPath { pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option { - lower::lower_path(path, hygiene).map(|it| (*it.mod_path).clone()) + let ctx = LowerCtx::with_hygiene(hygiene); + lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone()) } pub fn from_segments(kind: PathKind, segments: impl IntoIterator) -> ModPath { @@ -167,8 +168,8 @@ pub enum GenericArg { impl Path { /// Converts an `ast::Path` to `Path`. Works with use trees. /// It correctly handles `$crate` based path from macro call. - pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option { - lower::lower_path(path, hygiene) + pub fn from_src(path: ast::Path, ctx: &LowerCtx) -> Option { + lower::lower_path(path, ctx) } /// Converts a known mod path to `Path`. diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index 7b29d9d4f..1df6db525 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs @@ -6,10 +6,7 @@ use crate::intern::Interned; use std::sync::Arc; use either::Either; -use hir_expand::{ - hygiene::Hygiene, - name::{name, AsName}, -}; +use hir_expand::name::{name, AsName}; use syntax::ast::{self, AstNode, TypeBoundsOwner}; use super::AssociatedTypeBinding; @@ -23,12 +20,12 @@ pub(super) use lower_use::lower_use_tree; /// Converts an `ast::Path` to `Path`. Works with use trees. /// It correctly handles `$crate` based path from macro call. -pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option { +pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option { let mut kind = PathKind::Plain; let mut type_anchor = None; let mut segments = Vec::new(); let mut generic_args = Vec::new(); - let ctx = LowerCtx::with_hygiene(hygiene); + let hygiene = ctx.hygiene(); loop { let segment = path.segment()?; @@ -43,10 +40,10 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option Either::Left(name) => { let args = segment .generic_arg_list() - .and_then(|it| lower_generic_args(&ctx, it)) + .and_then(|it| lower_generic_args(ctx, it)) .or_else(|| { lower_generic_args_from_fn_path( - &ctx, + ctx, segment.param_list(), segment.ret_type(), ) @@ -64,7 +61,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option ast::PathSegmentKind::Type { type_ref, trait_ref } => { assert!(path.qualifier().is_none()); // this can only occur at the first segment - let self_type = TypeRef::from_ast(&ctx, type_ref?); + let self_type = TypeRef::from_ast(ctx, type_ref?); match trait_ref { // ::foo @@ -74,7 +71,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option } // >::Foo desugars to Trait::Foo Some(trait_ref) => { - let path = Path::from_src(trait_ref.path()?, hygiene)?; + let path = Path::from_src(trait_ref.path()?, ctx)?; let mod_path = (*path.mod_path).clone(); let num_segments = path.mod_path.segments.len(); kind = mod_path.kind; diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index 4c24aae94..0832371c0 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs @@ -1,9 +1,16 @@ //! HIR for references to types. Paths in these are not yet resolved. They can //! be directly created from an ast::TypeRef, without further queries. -use hir_expand::name::Name; -use syntax::ast; +use std::borrow::Cow; -use crate::{body::LowerCtx, path::Path}; +use hir_expand::{ast_id_map::FileAstId, name::Name, ExpandResult, InFile}; +use syntax::{algo::SyntaxRewriter, ast, AstNode, SyntaxKind, SyntaxNode}; + +use crate::{ + body::{Expander, LowerCtx}, + db::DefDatabase, + path::Path, + ModuleId, +}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum Mutability { @@ -68,6 +75,7 @@ impl TraitRef { } } } + /// Compare ty::Ty #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum TypeRef { @@ -84,6 +92,7 @@ pub enum TypeRef { // For ImplTrait(Vec), DynTrait(Vec), + Macro(InFile>), Error, } @@ -176,8 +185,13 @@ impl TypeRef { ast::Type::DynTraitType(inner) => { TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) } - // FIXME: Macros in type position are not yet supported. - ast::Type::MacroType(_) => TypeRef::Error, + ast::Type::MacroType(mt) => match mt.macro_call() { + Some(mc) => ctx + .ast_id(&mc) + .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc))) + .unwrap_or(TypeRef::Error), + None => TypeRef::Error, + }, } } @@ -193,6 +207,16 @@ impl TypeRef { TypeRef::Tuple(Vec::new()) } + pub fn has_macro_calls(&self) -> bool { + let mut has_macro_call = false; + self.walk(&mut |ty_ref| { + if let TypeRef::Macro(_) = ty_ref { + has_macro_call |= true + } + }); + has_macro_call + } + pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) { go(self, f); @@ -215,7 +239,7 @@ impl TypeRef { } } TypeRef::Path(path) => go_path(path, f), - TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {} + TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {} }; } @@ -290,3 +314,69 @@ impl TypeBound { } } } + +pub fn expand_type_ref<'a>( + db: &dyn DefDatabase, + module_id: ModuleId, + type_ref: &'a TypeRef, +) -> Option> { + let macro_call = match type_ref { + TypeRef::Macro(macro_call) => macro_call, + _ => return Some(Cow::Borrowed(type_ref)), + }; + + let file_id = macro_call.file_id; + let macro_call = macro_call.to_node(db.upcast()); + + let mut expander = Expander::new(db, file_id, module_id); + let expanded = expand(db, &mut expander, ¯o_call, true)?; + + let node = ast::Type::cast(expanded)?; + + let ctx = LowerCtx::new(db, file_id); + return Some(Cow::Owned(TypeRef::from_ast(&ctx, node))); + + fn expand( + db: &dyn DefDatabase, + expander: &mut Expander, + macro_call: &ast::MacroCall, + expect_type: bool, + ) -> Option { + let (mark, mut expanded) = match expander.enter_expand_raw(db, macro_call.clone()) { + Ok(ExpandResult { value: Some((mark, expanded)), .. }) => (mark, expanded), + _ => return None, + }; + + if expect_type && !ast::Type::can_cast(expanded.kind()) { + expander.exit(db, mark); + return None; + } + + if ast::MacroType::can_cast(expanded.kind()) { + expanded = expanded.first_child()?; // MACRO_CALL + } + + let mut rewriter = SyntaxRewriter::default(); + + let children = expanded.descendants().filter_map(ast::MacroCall::cast); + for child in children { + if let Some(new_node) = expand(db, expander, &child, false) { + if expanded == *child.syntax() { + expanded = new_node; + } else { + let parent = child.syntax().parent(); + let old_node = match &parent { + Some(node) if node.kind() == SyntaxKind::MACRO_TYPE => node, + _ => child.syntax(), + }; + rewriter.replace(old_node, &new_node) + } + } + } + + expander.exit(db, mark); + + let res = rewriter.rewrite(&expanded); + Some(res) + } +} -- cgit v1.2.3