Repository: rust-analyzer/rust-analyzer
Pull request #8462 (2021-04-10 19:39:43)

Expand macros at type position

cynecx: the latest push is still doing the old approach where `TypeRef` expansion happens in `{data,..}.rs`. i plan to work on moving the expansion logic to the `TypeRef -> Ty` lowering code.
cynecx: Okay, I've implemented the expansion inside `TypeRef -> Ty` lowering instead, that was much easier than the previous approach. This is also nice because we get macro expansion at any type position for free.
cynecx: Hm, I think the parser changes are wrong. The syntax tree looks wrong.
PandaLord69: > Hm, I think the parser changes are wrong. The syntax tree looks wrong.

Can you reply to your emails or Twitter?
cynecx: Nvm, the parser changes should be correct (Updated the tests though). GH Actions log output was weird.

This should be ready for a proper review.
jonas-schievink: Can you remove the GitHub mentions from the PR description? Otherwise they'll end up in a commit message and send a lot of notifications
matklad: cc https://github.com/bors-ng/bors-ng/issues/984
cynecx: @jonas-schievink Done.
cynecx: Is there any left to address?

Patch:
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 30f85bf3a0..04e2be3906 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -54,9 +54,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
-        let (file_id, range_or_offset) = fixture
-            .file_position
-            .expect("Could not find file position in fixture. Did you forget to add an `$0`?");
+        let (file_id, range_or_offset) = fixture.file_position.unwrap();
         (db, file_id, range_or_offset)
     }
 
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 7955bf0b52..62500602a7 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -6,10 +6,11 @@ use std::{cell::RefCell, fmt, iter::successors};
 
 use base_db::{FileId, FileRange};
 use hir_def::{
+    body,
     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};
@@ -853,8 +854,8 @@ impl<'a> SemanticsScope<'a> {
     /// 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<PathResolution> {
-        let hygiene = Hygiene::new(self.db.upcast(), self.file_id);
-        let path = Path::from_src(path.clone(), &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/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 847d2537d2..0895bd6f1f 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -9,6 +9,7 @@ use std::{iter::once, sync::Arc};
 
 use hir_def::{
     body::{
+        self,
         scope::{ExprScopes, ScopeId},
         Body, BodySourceMap,
     },
@@ -202,8 +203,8 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         macro_call: InFile<&ast::MacroCall>,
     ) -> Option<MacroDef> {
-        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 @@ impl SourceAnalyzer {
         }
 
         // 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 96b959967f..131f424cc8 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -21,7 +21,7 @@ use profile::Count;
 use rustc_hash::FxHashMap;
 use syntax::{ast, AstNode, AstPtr};
 
-pub(crate) use lower::LowerCtx;
+pub use lower::LowerCtx;
 
 use crate::{
     attr::{Attrs, RawAttrs},
@@ -37,13 +37,15 @@ use crate::{
 
 /// 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<DefMap>,
     current_file_id: HirFileId,
@@ -80,11 +82,7 @@ impl CfgExpander {
 }
 
 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 @@ impl Expander {
         }
     }
 
-    pub(crate) fn enter_expand<T: ast::AstNode>(
+    pub fn enter_expand<T: ast::AstNode>(
         &mut self,
         db: &dyn DefDatabase,
         macro_call: ast::MacroCall,
@@ -170,7 +168,7 @@ impl Expander {
         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,8 +188,13 @@ impl Expander {
         &self.cfg_expander.cfg_options
     }
 
+    pub fn current_file_id(&self) -> HirFileId {
+        self.current_file_id
+    }
+
     fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
-        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<MacroDefId> {
@@ -204,7 +207,8 @@ impl Expander {
     }
 }
 
-pub(crate) struct Mark {
+#[derive(Debug)]
+pub struct Mark {
     file_id: HirFileId,
     ast_id_map: Arc<AstIdMap>,
     bomb: DropBomb,
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index c0b0b78415..c11da30d2e 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<HirFileId>,
+    source_ast_id_map: Option<Arc<AstIdMap>>,
 }
 
 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> {
-        Path::from_src(ast, &self.hygiene)
+        Path::from_src(ast, self)
+    }
+
+    pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<FileAstId<N>> {
+        self.source_ast_id_map.as_ref().map(|ast_id_map| ast_id_map.ast_id(item))
     }
 }
 
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 94e08f8359..16a94a0588 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -104,6 +104,11 @@ impl ItemTree {
                     // 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
                     // still need to collect inner items.
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 000567d990..059724daaf 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -676,6 +676,7 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
     }
 }
 
+#[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 b528ff8bad..509f77850a 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<ModPath> {
-        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<Item = Name>) -> 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<Path> {
-        lower::lower_path(path, hygiene)
+    pub fn from_src(path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
+        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 7b29d9d4fb..1df6db5250 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<Path> {
+pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
     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<Path>
                     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<Path>
             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 {
                     // <T>::foo
@@ -74,7 +71,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
                     }
                     // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::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 4c24aae94f..ea29da5daa 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -1,6 +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::name::Name;
+
+use hir_expand::{name::Name, AstId, InFile};
 use syntax::ast;
 
 use crate::{body::LowerCtx, path::Path};
@@ -68,6 +69,7 @@ impl TraitRef {
         }
     }
 }
+
 /// Compare ty::Ty
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum TypeRef {
@@ -84,6 +86,7 @@ pub enum TypeRef {
     // For
     ImplTrait(Vec<TypeBound>),
     DynTrait(Vec<TypeBound>),
+    Macro(AstId<ast::MacroCall>),
     Error,
 }
 
@@ -116,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) => {
@@ -176,8 +179,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,
+            },
         }
     }
 
@@ -215,7 +223,7 @@ impl TypeRef {
                     }
                 }
                 TypeRef::Path(path) => go_path(path, f),
-                TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {}
+                TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
             };
         }
 
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index ca705ee9d4..1e4b0cc194 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 ef126e4adb..a5ac32d3cb 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -31,6 +31,7 @@ use parser::FragmentKind;
 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 e7c9dabc22..4fb7d9cf2f 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -9,6 +9,7 @@ use std::{
 
 use chalk_ir::BoundVar;
 use hir_def::{
+    body,
     db::DefDatabase,
     find_path,
     generics::TypeParamProvenance,
@@ -18,7 +19,7 @@ use hir_def::{
     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,6 +998,18 @@ impl HirDisplay for TypeRef {
                 write!(f, "dyn ")?;
                 f.write_joined(bounds, " + ")?;
             }
+            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(())
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index a035686bc9..7fd46becdb 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -5,12 +5,14 @@
 //!  - 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},
@@ -20,10 +22,11 @@ use hir_def::{
     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<u16>,
+    impl_trait_counter: Cell<u16>,
     /// 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<Vec<ReturnTypeImplTrait>>,
+    opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>,
+    expander: RefCell<Option<Expander>>,
 }
 
 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 @@ impl<'a> TyLoweringContext<'a> {
             impl_trait_counter,
             type_param_mode,
             opaque_type_data,
+            expander: RefCell::new(None),
         }
     }
 
@@ -86,15 +91,18 @@ impl<'a> TyLoweringContext<'a> {
         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,6 +295,53 @@ impl<'a> TyLoweringContext<'a> {
                     }
                 }
             }
+            TypeRef::Macro(macro_call) => {
+                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(),
+                                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::<ast::Type>(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 {
+                    None
+                };
+                if recursion_start {
+                    *self.expander.borrow_mut() = None;
+                }
+                ty.unwrap_or_else(|| TyKind::Error.intern(&Interner))
+            }
             TypeRef::Error => TyKind::Error.intern(&Interner),
         };
         (ty, res)
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index 94cbf7d85a..6ae3e734fe 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;


Test patch:
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index b8e373ed88..6588aa46c1 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -1074,3 +1074,202 @@ fn macro_in_arm() {
         "#]],
     );
 }
+
+#[test]
+fn macro_in_type_alias_position() {
+    check_infer(
+        r#"
+        macro_rules! U32 {
+            () => { u32 };
+        }
+
+        trait Foo {
+            type Ty;
+        }
+
+        impl<T> 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<T> 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>(A, B);
+
+        macro_rules! U32 {
+            () => { u32 };
+        }
+
+        macro_rules! Bar {
+            () => { Foo<U32!(), U32!()> };
+        }
+
+        trait Moo {
+            type Ty;
+        }
+
+        impl<T> 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<u32, u32>
+            266..267 'b': Foo<u32, u32>
+        "#]],
+    );
+}
+
+#[test]
+fn macros_in_type_position() {
+    check_infer(
+        r#"
+        struct Foo<A, B>(A, B);
+
+        macro_rules! U32 {
+            () => { u32 };
+        }
+
+        macro_rules! Bar {
+            () => { Foo<U32!(), U32!()> };
+        }
+
+        fn main() {
+            let a: Bar!();
+        }
+        "#,
+        expect![[r#"
+            133..155 '{     ...!(); }': ()
+            143..144 'a': Foo<u32, u32>
+        "#]],
+    );
+}
+
+#[test]
+fn macros_in_type_generics() {
+    check_infer(
+        r#"
+        struct Foo<A, B>(A, B);
+
+        macro_rules! U32 {
+            () => { u32 };
+        }
+
+        macro_rules! Bar {
+            () => { Foo<U32!(), U32!()> };
+        }
+
+        trait Moo {
+            type Ty;
+        }
+
+        impl<T> Moo for T {
+            type Ty = Foo<Bar!(), Bar!()>;
+        }
+
+        type TayTo = Foo<Bar!(), U32!()>;
+
+        fn main() {
+            let a: <() as Moo>::Ty;
+            let b: TayTo;
+        }
+        "#,
+        expect![[r#"
+            254..303 '{     ...yTo; }': ()
+            264..265 'a': Foo<Foo<u32, u32>, Foo<u32, u32>>
+            292..293 'b': Foo<Foo<u32, u32>, u32>
+        "#]],
+    );
+}
+
+#[test]
+fn infinitely_recursive_macro_type() {
+    check_infer(
+        r#"
+        struct Bar<T, X>(T, X);
+
+        macro_rules! Foo {
+            () => { Foo!() }
+        }
+
+        macro_rules! U32 {
+            () => { u32 }
+        }
+
+        type A = Foo!();
+        type B = Bar<Foo!(), U32!()>;
+
+        fn main() {
+            let a: A;
+            let b: B;
+        }
+        "#,
+        expect![[r#"
+            166..197 '{     ...: B; }': ()
+            176..177 'a': {unknown}
+            190..191 'b': Bar<{unknown}, u32>
+        "#]],
+    );
+}
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 3016a65741..1ff3f7656a 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"