From cf3b4f1e208247c9d171273dabff9c6b3c98a240 Mon Sep 17 00:00:00 2001 From: cynecx Date: Sat, 10 Apr 2021 17:49:12 +0200 Subject: [PATCH 1/7] hir_ty: Expand macros at type position --- crates/hir/src/semantics.rs | 4 +- crates/hir/src/source_analyzer.rs | 9 +- 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 ++++++++++- crates/hir_expand/src/db.rs | 1 + crates/hir_expand/src/eager.rs | 1 + crates/hir_ty/src/display.rs | 2 +- crates/hir_ty/src/lower.rs | 12 +- crates/hir_ty/src/tests/macros.rs | 169 ++++++++++++++++++ crates/parser/src/grammar/types.rs | 10 +- .../inline/ok/0117_macro_call_type.rast | 48 ++--- 17 files changed, 434 insertions(+), 81 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 7955bf0b52f..29c0821cfed 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -6,6 +6,7 @@ use base_db::{FileId, FileRange}; use hir_def::{ + body, resolver::{self, HasResolver, Resolver, TypeNs}, AsMacroCall, FunctionId, TraitId, VariantId, }; @@ -854,7 +855,8 @@ pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { /// necessary a heuristic, as it doesn't take hygiene into account. pub fn speculative_resolve(&self, path: &ast::Path) -> Option { let hygiene = Hygiene::new(self.db.upcast(), self.file_id); - let path = Path::from_src(path.clone(), &hygiene)?; + let ctx = body::LowerCtx::with_hygiene(&hygiene); + let path = Path::from_src(path.clone(), &ctx)?; resolve_hir_path(self.db, &self.resolver, &path) } } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 847d2537d2f..0895bd6f1f1 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -9,6 +9,7 @@ use hir_def::{ body::{ + self, scope::{ExprScopes, ScopeId}, Body, BodySourceMap, }, @@ -202,8 +203,8 @@ pub(crate) fn resolve_macro_call( db: &dyn HirDatabase, macro_call: InFile<&ast::MacroCall>, ) -> Option { - let hygiene = Hygiene::new(db.upcast(), macro_call.file_id); - let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; + let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id); + let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?; self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into()) } @@ -281,7 +282,9 @@ pub(crate) fn resolve_path( } // This must be a normal source file rather than macro file. - let hir_path = Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; + let hygiene = Hygiene::new(db.upcast(), self.file_id); + let ctx = body::LowerCtx::with_hygiene(&hygiene); + let hir_path = Path::from_src(path.clone(), &ctx)?; // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we // trying to resolve foo::bar. diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 96b959967f1..44ae136437f 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs @@ -19,9 +19,9 @@ 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 @@ pub(crate) fn new( } } - 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 @@ pub(crate) fn enter_expand( } }; + 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 @@ pub(crate) fn enter_expand( 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 @@ pub(crate) fn cfg_options(&self) -> &CfgOptions { } 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 @@ fn ast_id(&self, item: &N) -> AstId { } } +#[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 c0b0b784151..c11da30d2e0 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 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(crate) fn with_hygiene(hygiene: &Hygiene) -> Self { - LowerCtx { hygiene: hygiene.clone() } + + 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 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 135a6698e15..8732b1e3ed6 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs @@ -123,10 +123,11 @@ pub(crate) fn type_alias_data_query( 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 @@ pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc 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 45b099cf319..2975786dd4c 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -189,12 +189,16 @@ fn collect_inner_items(&mut self, container: &SyntaxNode) { 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 000567d990f..059724daafa 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs @@ -676,6 +676,7 @@ fn new(file_id: HirFileId, ast_id: FileAstId, path: path::ModPath) -> AstIdWi } } +#[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 b528ff8bad7..509f77850a3 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 7b29d9d4fba..1df6db5250e 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs @@ -6,10 +6,7 @@ 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 @@ /// 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 4c24aae94f3..0832371c0dc 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 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option { } } } + /// 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 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { 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 @@ pub(crate) fn unit() -> 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 @@ fn go(type_ref: &TypeRef, f: &mut impl FnMut(&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 @@ pub fn as_path(&self) -> Option<&Path> { } } } + +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) + } +} diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index ca705ee9d4d..1e4b0cc194b 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs @@ -440,6 +440,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, MACRO_STMTS => FragmentKind::Statements, MACRO_PAT => FragmentKind::Pattern, + MACRO_TYPE => FragmentKind::Type, ITEM_LIST => FragmentKind::Items, LET_STMT => { // FIXME: Handle LHS Pattern diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index ef126e4adbd..a5ac32d3cb0 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs @@ -31,6 +31,7 @@ use std::sync::Arc; use syntax::{algo::SyntaxRewriter, SyntaxNode}; +#[derive(Debug)] pub struct ErrorEmitted { _private: (), } diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index e7c9dabc222..63bcb064069 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -997,7 +997,7 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { write!(f, "dyn ")?; f.write_joined(bounds, " + ")?; } - TypeRef::Error => write!(f, "{{error}}")?, + TypeRef::Error | TypeRef::Macro(_) => write!(f, "{{error}}")?, } Ok(()) } diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index a035686bc9e..95ca5bdb097 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -15,7 +15,7 @@ generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, path::{GenericArg, Path, PathSegment, PathSegments}, resolver::{HasResolver, Resolver, TypeNs}, - type_ref::{TraitRef as HirTraitRef, TypeBound, TypeRef}, + type_ref::{expand_type_ref, TraitRef as HirTraitRef, TypeBound, TypeRef}, AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, @@ -287,6 +287,16 @@ pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option) { } } } + mt @ TypeRef::Macro(_) => { + if let Some(module_id) = self.resolver.module() { + match expand_type_ref(self.db.upcast(), module_id, mt) { + Some(type_ref) => self.lower_ty(type_ref.as_ref()), + None => TyKind::Error.intern(&Interner), + } + } else { + TyKind::Error.intern(&Interner) + } + } TypeRef::Error => TyKind::Error.intern(&Interner), }; (ty, res) diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index b8e373ed889..cbe05a5c166 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -1074,3 +1074,172 @@ fn main() { "#]], ); } + +#[test] +fn macro_in_type_alias_position() { + check_infer( + r#" + macro_rules! U32 { + () => { u32 }; + } + + trait Foo { + type Ty; + } + + impl Foo for T { + type Ty = U32!(); + } + + type TayTo = U32!(); + + fn testy() { + let a: <() as Foo>::Ty; + let b: TayTo; + } + "#, + expect![[r#" + 147..196 '{ ...yTo; }': () + 157..158 'a': u32 + 185..186 'b': u32 + "#]], + ); +} + +#[test] +fn nested_macro_in_type_alias_position() { + check_infer( + r#" + macro_rules! U32Inner2 { + () => { u32 }; + } + + macro_rules! U32Inner1 { + () => { U32Inner2!() }; + } + + macro_rules! U32 { + () => { U32Inner1!() }; + } + + trait Foo { + type Ty; + } + + impl Foo for T { + type Ty = U32!(); + } + + type TayTo = U32!(); + + fn testy() { + let a: <() as Foo>::Ty; + let b: TayTo; + } + "#, + expect![[r#" + 259..308 '{ ...yTo; }': () + 269..270 'a': u32 + 297..298 'b': u32 + "#]], + ); +} + +#[test] +fn macros_in_type_alias_position_generics() { + check_infer( + r#" + struct Foo(A, B); + + macro_rules! U32 { + () => { u32 }; + } + + macro_rules! Bar { + () => { Foo }; + } + + trait Moo { + type Ty; + } + + impl Moo for T { + type Ty = Bar!(); + } + + type TayTo = Bar!(); + + fn main() { + let a: <() as Moo>::Ty; + let b: TayTo; + } + "#, + expect![[r#" + 228..277 '{ ...yTo; }': () + 238..239 'a': Foo + 266..267 'b': Foo + "#]], + ); +} + +#[test] +fn macros_in_type_position() { + check_infer( + r#" + struct Foo(A, B); + + macro_rules! U32 { + () => { u32 }; + } + + macro_rules! Bar { + () => { Foo }; + } + + fn main() { + let a: Bar!(); + } + "#, + expect![[r#" + 133..155 '{ ...!(); }': () + 143..144 'a': Foo + "#]], + ); +} + +#[test] +fn macros_in_type_generics() { + check_infer( + r#" + struct Foo(A, B); + + macro_rules! U32 { + () => { u32 }; + } + + macro_rules! Bar { + () => { Foo }; + } + + trait Moo { + type Ty; + } + + impl Moo for T { + type Ty = Foo; + } + + type TayTo = Foo; + + fn main() { + let a: <() as Moo>::Ty; + let b: TayTo; + } + "#, + expect![[r#" + 254..303 '{ ...yTo; }': () + 264..265 'a': Foo, Foo> + 292..293 'b': Foo, u32> + "#]], + ); +} diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs index 94cbf7d85a0..6ae3e734fe4 100644 --- a/crates/parser/src/grammar/types.rs +++ b/crates/parser/src/grammar/types.rs @@ -283,17 +283,21 @@ pub(super) fn path_type(p: &mut Parser) { // type B = crate::foo!(); fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) { assert!(paths::is_path_start(p)); + let r = p.start(); let m = p.start(); + paths::type_path(p); let kind = if p.at(T![!]) && !p.at(T![!=]) { items::macro_call_after_excl(p); - MACRO_CALL + m.complete(p, MACRO_CALL); + MACRO_TYPE } else { + m.abandon(p); PATH_TYPE }; - let path = m.complete(p, kind); + let path = r.complete(p, kind); if allow_bounds { opt_type_bounds_as_dyn_trait_type(p, path); @@ -319,7 +323,7 @@ pub(super) fn path_type_(p: &mut Parser, allow_bounds: bool) { fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) { assert!(matches!( type_marker.kind(), - SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_CALL + SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE )); if !p.at(T![+]) { return; diff --git a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast index 3016a65741d..1ff3f7656a4 100644 --- a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast +++ b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast @@ -7,15 +7,16 @@ SOURCE_FILE@0..41 WHITESPACE@6..7 " " EQ@7..8 "=" WHITESPACE@8..9 " " - MACRO_CALL@9..15 - PATH@9..12 - PATH_SEGMENT@9..12 - NAME_REF@9..12 - IDENT@9..12 "foo" - BANG@12..13 "!" - TOKEN_TREE@13..15 - L_PAREN@13..14 "(" - R_PAREN@14..15 ")" + MACRO_TYPE@9..15 + MACRO_CALL@9..15 + PATH@9..12 + PATH_SEGMENT@9..12 + NAME_REF@9..12 + IDENT@9..12 "foo" + BANG@12..13 "!" + TOKEN_TREE@13..15 + L_PAREN@13..14 "(" + R_PAREN@14..15 ")" SEMICOLON@15..16 ";" WHITESPACE@16..17 "\n" TYPE_ALIAS@17..40 @@ -26,19 +27,20 @@ SOURCE_FILE@0..41 WHITESPACE@23..24 " " EQ@24..25 "=" WHITESPACE@25..26 " " - MACRO_CALL@26..39 - PATH@26..36 - PATH@26..31 - PATH_SEGMENT@26..31 - NAME_REF@26..31 - CRATE_KW@26..31 "crate" - COLON2@31..33 "::" - PATH_SEGMENT@33..36 - NAME_REF@33..36 - IDENT@33..36 "foo" - BANG@36..37 "!" - TOKEN_TREE@37..39 - L_PAREN@37..38 "(" - R_PAREN@38..39 ")" + MACRO_TYPE@26..39 + MACRO_CALL@26..39 + PATH@26..36 + PATH@26..31 + PATH_SEGMENT@26..31 + NAME_REF@26..31 + CRATE_KW@26..31 "crate" + COLON2@31..33 "::" + PATH_SEGMENT@33..36 + NAME_REF@33..36 + IDENT@33..36 "foo" + BANG@36..37 "!" + TOKEN_TREE@37..39 + L_PAREN@37..38 "(" + R_PAREN@38..39 ")" SEMICOLON@39..40 ";" WHITESPACE@40..41 "\n" From 28ef7c20d79803403be58eeffa18ab1fb21e261c Mon Sep 17 00:00:00 2001 From: cynecx Date: Mon, 12 Apr 2021 16:24:48 +0200 Subject: [PATCH 2/7] hir_ty: deal with TypeRef::Macro in HirFormatter --- crates/hir/src/semantics.rs | 5 ++--- crates/hir_ty/src/display.rs | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 29c0821cfed..62500602a71 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -10,7 +10,7 @@ resolver::{self, HasResolver, Resolver, TypeNs}, AsMacroCall, FunctionId, TraitId, VariantId, }; -use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo}; +use hir_expand::{name::AsName, ExpansionInfo}; use hir_ty::associated_type_shorthand_candidates; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; @@ -854,8 +854,7 @@ pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { /// Resolve a path as-if it was written at the given scope. This is /// necessary a heuristic, as it doesn't take hygiene into account. pub fn speculative_resolve(&self, path: &ast::Path) -> Option { - let hygiene = Hygiene::new(self.db.upcast(), self.file_id); - let ctx = body::LowerCtx::with_hygiene(&hygiene); + let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id); let path = Path::from_src(path.clone(), &ctx)?; resolve_hir_path(self.db, &self.resolver, &path) } diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 63bcb064069..4fb7d9cf2fd 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -9,6 +9,7 @@ use chalk_ir::BoundVar; use hir_def::{ + body, db::DefDatabase, find_path, generics::TypeParamProvenance, @@ -18,7 +19,7 @@ visibility::Visibility, AssocContainerId, Lookup, ModuleId, TraitId, }; -use hir_expand::name::Name; +use hir_expand::{hygiene::Hygiene, name::Name}; use crate::{ const_from_placeholder_idx, db::HirDatabase, from_assoc_type_id, from_foreign_def_id, @@ -997,7 +998,19 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { write!(f, "dyn ")?; f.write_joined(bounds, " + ")?; } - TypeRef::Error | TypeRef::Macro(_) => write!(f, "{{error}}")?, + TypeRef::Macro(macro_call) => { + let macro_call = macro_call.to_node(f.db.upcast()); + let ctx = body::LowerCtx::with_hygiene(&Hygiene::new_unhygienic()); + match macro_call.path() { + Some(path) => match Path::from_src(path, &ctx) { + Some(path) => path.hir_fmt(f)?, + None => write!(f, "{{macro}}")?, + }, + None => write!(f, "{{macro}}")?, + } + write!(f, "!(..)")?; + } + TypeRef::Error => write!(f, "{{error}}")?, } Ok(()) } From 14918a3870d568778473f0a5697a547b85acf20a Mon Sep 17 00:00:00 2001 From: cynecx Date: Wed, 14 Apr 2021 02:36:05 +0200 Subject: [PATCH 3/7] hir_def: ignore ast::Type in file_item_tree query --- crates/hir_def/src/item_tree.rs | 5 +++++ crates/hir_def/src/item_tree/lower.rs | 16 ++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 94e08f83590..fed28550521 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -104,6 +104,11 @@ pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> // items and expanded during block DefMap computation return Default::default(); }, + ast::Type(_ty) => { + // FIXME: This occurs because macros in type position are treated as inner + // items and expanded during block DefMap computation + return Default::default(); + }, ast::Expr(e) => { // Macros can expand to expressions. We return an empty item tree in this case, but // still need to collect inner items. diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 2975786dd4c..45b099cf319 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -189,16 +189,12 @@ fn collect_inner_items(&mut self, container: &SyntaxNode) { block_stack.push(self.source_ast_id_map.ast_id(&block)); }, ast::Item(item) => { - // 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()); - } + // 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()); } } }, From 7ed42a3a527b2c39826cfeb3626521c11abb25f0 Mon Sep 17 00:00:00 2001 From: cynecx Date: Sat, 17 Apr 2021 17:38:38 +0200 Subject: [PATCH 4/7] hir_def: refactor expand_macro_type and cleanups --- crates/hir_def/src/body.rs | 72 +++++++----------------------- crates/hir_def/src/type_ref.rs | 80 +++++++--------------------------- crates/hir_ty/src/lower.rs | 6 +-- 3 files changed, 34 insertions(+), 124 deletions(-) diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 44ae136437f..8a9b936ea40 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs @@ -19,7 +19,7 @@ use la_arena::{Arena, ArenaMap}; use profile::Count; use rustc_hash::FxHashMap; -use syntax::{ast, AstNode, AstPtr, SyntaxNode}; +use syntax::{ast, AstNode, AstPtr}; pub use lower::LowerCtx; @@ -98,14 +98,11 @@ pub(crate) fn new( } } - fn enter_expand_intern( + pub(crate) fn enter_expand( &mut self, db: &dyn DefDatabase, macro_call: ast::MacroCall, - ) -> Result< - ExpandResult Mark + '_)>>, - UnresolvedMacro, - > { + ) -> Result>, UnresolvedMacro> { if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { cov_mark::hit!(your_stack_belongs_to_me); return Ok(ExpandResult::str_err( @@ -150,55 +147,6 @@ fn enter_expand_intern( } }; - 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 => { @@ -209,7 +157,15 @@ pub(crate) fn enter_expand( log::debug!("macro expansion {:#?}", node.syntax()); - let mark = advance_state(db); + 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); Ok(ExpandResult { value: Some((mark, node)), err }) } @@ -234,6 +190,10 @@ pub(crate) fn cfg_options(&self) -> &CfgOptions { &self.cfg_expander.cfg_options } + pub(crate) fn current_file_id(&self) -> HirFileId { + self.current_file_id + } + fn parse_path(&mut self, path: ast::Path) -> Option { let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene); Path::from_src(path, &ctx) diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index 0832371c0dc..cf8a584ab67 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs @@ -1,9 +1,8 @@ //! 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 std::borrow::Cow; use hir_expand::{ast_id_map::FileAstId, name::Name, ExpandResult, InFile}; -use syntax::{algo::SyntaxRewriter, ast, AstNode, SyntaxKind, SyntaxNode}; +use syntax::ast; use crate::{ body::{Expander, LowerCtx}, @@ -207,16 +206,6 @@ pub(crate) fn unit() -> 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); @@ -315,68 +304,29 @@ pub fn as_path(&self) -> Option<&Path> { } } -pub fn expand_type_ref<'a>( +pub fn expand_macro_type( db: &dyn DefDatabase, module_id: ModuleId, - type_ref: &'a TypeRef, -) -> Option> { - let macro_call = match type_ref { + macro_type: &TypeRef, +) -> Option { + let macro_call = match macro_type { TypeRef::Macro(macro_call) => macro_call, - _ => return Some(Cow::Borrowed(type_ref)), + _ => panic!("expected TypeRef::Macro"), }; 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 (file_id, expanded) = match expander.enter_expand::(db, macro_call.clone()) { + Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { + let file_id = expander.current_file_id(); + expander.exit(db, mark); + (file_id, expanded) + } + _ => return None, + }; 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) - } + return Some(TypeRef::from_ast(&ctx, expanded)); } diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 95ca5bdb097..e01b7aa9190 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -15,7 +15,7 @@ generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, path::{GenericArg, Path, PathSegment, PathSegments}, resolver::{HasResolver, Resolver, TypeNs}, - type_ref::{expand_type_ref, TraitRef as HirTraitRef, TypeBound, TypeRef}, + type_ref::{expand_macro_type, TraitRef as HirTraitRef, TypeBound, TypeRef}, AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, @@ -289,8 +289,8 @@ pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option) { } mt @ TypeRef::Macro(_) => { if let Some(module_id) = self.resolver.module() { - match expand_type_ref(self.db.upcast(), module_id, mt) { - Some(type_ref) => self.lower_ty(type_ref.as_ref()), + match expand_macro_type(self.db.upcast(), module_id, mt) { + Some(type_ref) => self.lower_ty(&type_ref), None => TyKind::Error.intern(&Interner), } } else { From 3d39e77003c5fe5ed9f8f3ac00a170f3804f8337 Mon Sep 17 00:00:00 2001 From: cynecx Date: Sun, 18 Apr 2021 18:35:45 +0200 Subject: [PATCH 5/7] hir_def: various cleanups --- crates/hir_def/src/data.rs | 8 +++----- crates/hir_def/src/item_tree.rs | 8 ++++---- crates/hir_def/src/type_ref.rs | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index 8732b1e3ed6..135a6698e15 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs @@ -123,11 +123,10 @@ pub(crate) fn type_alias_data_query( 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: type_ref, + type_ref: typ.type_ref.clone(), visibility: item_tree[typ.visibility].clone(), is_extern: typ.is_extern, bounds: typ.bounds.to_vec(), @@ -203,13 +202,12 @@ pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc 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 file_id = impl_loc.id.file_id(); - let self_ty = impl_def.self_ty.clone(); + let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id); - 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.rs b/crates/hir_def/src/item_tree.rs index fed28550521..16a94a05884 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -104,10 +104,10 @@ pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> // items and expanded during block DefMap computation return Default::default(); }, - ast::Type(_ty) => { - // FIXME: This occurs because macros in type position are treated as inner - // items and expanded during block DefMap computation - return Default::default(); + ast::Type(ty) => { + // Types can contain inner items. We return an empty item tree in this case, but + // still need to collect inner items. + ctx.lower_inner_items(ty.syntax()) }, ast::Expr(e) => { // Macros can expand to expressions. We return an empty item tree in this case, but diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index cf8a584ab67..e18712d2460 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs @@ -1,7 +1,7 @@ //! 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::{ast_id_map::FileAstId, name::Name, ExpandResult, InFile}; +use hir_expand::{name::Name, AstId, ExpandResult, InFile}; use syntax::ast; use crate::{ @@ -91,7 +91,7 @@ pub enum TypeRef { // For ImplTrait(Vec), DynTrait(Vec), - Macro(InFile>), + Macro(AstId), Error, } From 6ed2fd233b569d01169fc888f30c358dd289d260 Mon Sep 17 00:00:00 2001 From: cynecx Date: Sun, 18 Apr 2021 19:56:13 +0200 Subject: [PATCH 6/7] hir_ty: keep body::Expander in TyLoweringContext --- crates/hir_def/src/body.rs | 18 ++++---- crates/hir_def/src/type_ref.rs | 38 ++--------------- crates/hir_ty/src/lower.rs | 71 +++++++++++++++++++++++++------ crates/hir_ty/src/tests/macros.rs | 26 +++++++++++ 4 files changed, 94 insertions(+), 59 deletions(-) diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 8a9b936ea40..131f424cc8b 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs @@ -37,13 +37,15 @@ /// A subset of Expander that only deals with cfg attributes. We only need it to /// avoid cyclic queries in crate def map during enum processing. +#[derive(Debug)] pub(crate) struct CfgExpander { cfg_options: CfgOptions, hygiene: Hygiene, krate: CrateId, } -pub(crate) struct Expander { +#[derive(Debug)] +pub struct Expander { cfg_expander: CfgExpander, def_map: Arc, current_file_id: HirFileId, @@ -80,11 +82,7 @@ pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::Attrs } impl Expander { - pub(crate) fn new( - db: &dyn DefDatabase, - current_file_id: HirFileId, - module: ModuleId, - ) -> Expander { + pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); let def_map = module.def_map(db); let ast_id_map = db.ast_id_map(current_file_id); @@ -98,7 +96,7 @@ pub(crate) fn new( } } - pub(crate) fn enter_expand( + pub fn enter_expand( &mut self, db: &dyn DefDatabase, macro_call: ast::MacroCall, @@ -170,7 +168,7 @@ pub(crate) fn enter_expand( Ok(ExpandResult { value: Some((mark, node)), err }) } - pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { + pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); self.current_file_id = mark.file_id; self.ast_id_map = mem::take(&mut mark.ast_id_map); @@ -190,7 +188,7 @@ pub(crate) fn cfg_options(&self) -> &CfgOptions { &self.cfg_expander.cfg_options } - pub(crate) fn current_file_id(&self) -> HirFileId { + pub fn current_file_id(&self) -> HirFileId { self.current_file_id } @@ -210,7 +208,7 @@ fn ast_id(&self, item: &N) -> AstId { } #[derive(Debug)] -pub(crate) struct Mark { +pub struct Mark { file_id: HirFileId, ast_id_map: Arc, bomb: DropBomb, diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index e18712d2460..ea29da5daae 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs @@ -1,15 +1,10 @@ //! 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, AstId, ExpandResult, InFile}; +use hir_expand::{name::Name, AstId, InFile}; use syntax::ast; -use crate::{ - body::{Expander, LowerCtx}, - db::DefDatabase, - path::Path, - ModuleId, -}; +use crate::{body::LowerCtx, path::Path}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum Mutability { @@ -124,7 +119,7 @@ pub enum TypeBound { impl TypeRef { /// Converts an `ast::TypeRef` to a `hir::TypeRef`. - pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { + pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { match node { ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), ast::Type::TupleType(inner) => { @@ -303,30 +298,3 @@ pub fn as_path(&self) -> Option<&Path> { } } } - -pub fn expand_macro_type( - db: &dyn DefDatabase, - module_id: ModuleId, - macro_type: &TypeRef, -) -> Option { - let macro_call = match macro_type { - TypeRef::Macro(macro_call) => macro_call, - _ => panic!("expected TypeRef::Macro"), - }; - - 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 (file_id, expanded) = match expander.enter_expand::(db, macro_call.clone()) { - Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { - let file_id = expander.current_file_id(); - expander.exit(db, mark); - (file_id, expanded) - } - _ => return None, - }; - - let ctx = LowerCtx::new(db, file_id); - return Some(TypeRef::from_ast(&ctx, expanded)); -} diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index e01b7aa9190..a883334afbb 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -5,25 +5,28 @@ //! - Building the type for an item: This happens through the `type_for_def` query. //! //! This usually involves resolving names, collecting generic arguments etc. +use std::cell::{Cell, RefCell}; use std::{iter, sync::Arc}; use base_db::CrateId; use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety}; use hir_def::{ adt::StructKind, + body::{Expander, LowerCtx}, builtin_type::BuiltinType, generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, path::{GenericArg, Path, PathSegment, PathSegments}, resolver::{HasResolver, Resolver, TypeNs}, - type_ref::{expand_macro_type, TraitRef as HirTraitRef, TypeBound, TypeRef}, + type_ref::{TraitRef as HirTraitRef, TypeBound, TypeRef}, AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, }; -use hir_expand::name::Name; +use hir_expand::{name::Name, ExpandResult}; use la_arena::ArenaMap; use smallvec::SmallVec; use stdx::impl_from; +use syntax::ast; use crate::{ db::HirDatabase, @@ -50,7 +53,7 @@ pub struct TyLoweringContext<'a> { /// possible currently, so this should be fine for now. pub type_param_mode: TypeParamLoweringMode, pub impl_trait_mode: ImplTraitLoweringMode, - impl_trait_counter: std::cell::Cell, + impl_trait_counter: Cell, /// When turning `impl Trait` into opaque types, we have to collect the /// bounds at the same time to get the IDs correct (without becoming too /// complicated). I don't like using interior mutability (as for the @@ -59,16 +62,17 @@ pub struct TyLoweringContext<'a> { /// we're grouping the mutable data (the counter and this field) together /// with the immutable context (the references to the DB and resolver). /// Splitting this up would be a possible fix. - opaque_type_data: std::cell::RefCell>, + opaque_type_data: RefCell>, + expander: RefCell>, } impl<'a> TyLoweringContext<'a> { pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self { - let impl_trait_counter = std::cell::Cell::new(0); + let impl_trait_counter = Cell::new(0); let impl_trait_mode = ImplTraitLoweringMode::Disallowed; let type_param_mode = TypeParamLoweringMode::Placeholder; let in_binders = DebruijnIndex::INNERMOST; - let opaque_type_data = std::cell::RefCell::new(Vec::new()); + let opaque_type_data = RefCell::new(Vec::new()); Self { db, resolver, @@ -77,6 +81,7 @@ pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self { impl_trait_counter, type_param_mode, opaque_type_data, + expander: RefCell::new(None), } } @@ -86,15 +91,18 @@ pub fn with_debruijn( f: impl FnOnce(&TyLoweringContext) -> T, ) -> T { let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); + let expander = self.expander.replace(None); let new_ctx = Self { in_binders: debruijn, - impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), - opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec), + impl_trait_counter: Cell::new(self.impl_trait_counter.get()), + opaque_type_data: RefCell::new(opaque_ty_data_vec), + expander: RefCell::new(expander), ..*self }; let result = f(&new_ctx); self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); + self.expander.replace(new_ctx.expander.into_inner()); result } @@ -287,15 +295,50 @@ pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option) { } } } - mt @ TypeRef::Macro(_) => { - if let Some(module_id) = self.resolver.module() { - match expand_macro_type(self.db.upcast(), module_id, mt) { - Some(type_ref) => self.lower_ty(&type_ref), - None => TyKind::Error.intern(&Interner), + TypeRef::Macro(macro_call) => { + let (expander, recursion_start) = match self.expander.borrow_mut() { + expander if expander.is_some() => (Some(expander), false), + mut expander => { + if let Some(module_id) = self.resolver.module() { + *expander = Some(Expander::new( + self.db.upcast(), + macro_call.file_id, + module_id, + )); + (Some(expander), true) + } else { + (None, false) + } + } + }; + let ty = if let Some(mut expander) = expander { + let expander_mut = expander.as_mut().unwrap(); + let macro_call = macro_call.to_node(self.db.upcast()); + match expander_mut.enter_expand::(self.db.upcast(), macro_call) { + Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { + let ctx = + LowerCtx::new(self.db.upcast(), expander_mut.current_file_id()); + let type_ref = TypeRef::from_ast(&ctx, expanded); + + drop(expander); + let ty = self.lower_ty(&type_ref); + + self.expander + .borrow_mut() + .as_mut() + .unwrap() + .exit(self.db.upcast(), mark); + Some(ty) + } + _ => None, } } else { - TyKind::Error.intern(&Interner) + None + }; + if recursion_start { + *self.expander.borrow_mut() = None; } + ty.unwrap_or_else(|| TyKind::Error.intern(&Interner)) } TypeRef::Error => TyKind::Error.intern(&Interner), }; diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index cbe05a5c166..8de1e229f0d 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -1243,3 +1243,29 @@ fn main() { "#]], ); } + +#[test] +fn infinitely_recursive_macro_type() { + check_infer( + r#" + struct Bar(T); + + macro_rules! Foo { + () => { Foo!() } + } + + type A = Foo!(); + type B = Bar; + + fn main() { + let a: A; + let b: B; + } + "#, + expect![[r#" + 112..143 '{ ...: B; }': () + 122..123 'a': {unknown} + 136..137 'b': Bar<{unknown}> + "#]], + ); +} From f0507ab7c697ba4bcd59dd2f673dfff5072e3e1a Mon Sep 17 00:00:00 2001 From: cynecx Date: Sun, 18 Apr 2021 20:18:48 +0200 Subject: [PATCH 7/7] hir_ty: cleanups and extend infinitely_recursive_macro_type test --- crates/hir_ty/src/lower.rs | 8 +++++--- crates/hir_ty/src/tests/macros.rs | 14 +++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index a883334afbb..7fd46becdb9 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -296,9 +296,11 @@ pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option) { } } TypeRef::Macro(macro_call) => { - let (expander, recursion_start) = match self.expander.borrow_mut() { - expander if expander.is_some() => (Some(expander), false), - mut expander => { + let (expander, recursion_start) = { + let mut expander = self.expander.borrow_mut(); + if expander.is_some() { + (Some(expander), false) + } else { if let Some(module_id) = self.resolver.module() { *expander = Some(Expander::new( self.db.upcast(), diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 8de1e229f0d..6588aa46c1e 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -1248,14 +1248,18 @@ fn main() { fn infinitely_recursive_macro_type() { check_infer( r#" - struct Bar(T); + struct Bar(T, X); macro_rules! Foo { () => { Foo!() } } + macro_rules! U32 { + () => { u32 } + } + type A = Foo!(); - type B = Bar; + type B = Bar; fn main() { let a: A; @@ -1263,9 +1267,9 @@ fn main() { } "#, expect![[r#" - 112..143 '{ ...: B; }': () - 122..123 'a': {unknown} - 136..137 'b': Bar<{unknown}> + 166..197 '{ ...: B; }': () + 176..177 'a': {unknown} + 190..191 'b': Bar<{unknown}, u32> "#]], ); }