diff options
author | cynecx <[email protected]> | 2021-04-10 16:49:12 +0100 |
---|---|---|
committer | cynecx <[email protected]> | 2021-04-17 15:24:56 +0100 |
commit | cf3b4f1e208247c9d171273dabff9c6b3c98a240 (patch) | |
tree | 0cae2703b98ba8640ce6693abe32fb51fe27fdda /crates/hir_def | |
parent | fb2d284f28f70426e39e1b92d95bdbb217a48109 (diff) |
hir_ty: Expand macros at type position
Diffstat (limited to 'crates/hir_def')
-rw-r--r-- | crates/hir_def/src/body.rs | 74 | ||||
-rw-r--r-- | crates/hir_def/src/body/lower.rs | 34 | ||||
-rw-r--r-- | crates/hir_def/src/data.rs | 8 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 16 | ||||
-rw-r--r-- | crates/hir_def/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/hir_def/src/path.rs | 7 | ||||
-rw-r--r-- | crates/hir_def/src/path/lower.rs | 17 | ||||
-rw-r--r-- | crates/hir_def/src/type_ref.rs | 102 |
8 files changed, 210 insertions, 49 deletions
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::{ | |||
19 | use la_arena::{Arena, ArenaMap}; | 19 | use la_arena::{Arena, ArenaMap}; |
20 | use profile::Count; | 20 | use profile::Count; |
21 | use rustc_hash::FxHashMap; | 21 | use rustc_hash::FxHashMap; |
22 | use syntax::{ast, AstNode, AstPtr}; | 22 | use syntax::{ast, AstNode, AstPtr, SyntaxNode}; |
23 | 23 | ||
24 | pub(crate) use lower::LowerCtx; | 24 | pub use lower::LowerCtx; |
25 | 25 | ||
26 | use crate::{ | 26 | use crate::{ |
27 | attr::{Attrs, RawAttrs}, | 27 | attr::{Attrs, RawAttrs}, |
@@ -98,11 +98,14 @@ impl Expander { | |||
98 | } | 98 | } |
99 | } | 99 | } |
100 | 100 | ||
101 | pub(crate) fn enter_expand<T: ast::AstNode>( | 101 | fn enter_expand_intern( |
102 | &mut self, | 102 | &mut self, |
103 | db: &dyn DefDatabase, | 103 | db: &dyn DefDatabase, |
104 | macro_call: ast::MacroCall, | 104 | macro_call: ast::MacroCall, |
105 | ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> { | 105 | ) -> Result< |
106 | ExpandResult<Option<(SyntaxNode, impl FnMut(&dyn DefDatabase) -> Mark + '_)>>, | ||
107 | UnresolvedMacro, | ||
108 | > { | ||
106 | if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { | 109 | if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { |
107 | cov_mark::hit!(your_stack_belongs_to_me); | 110 | cov_mark::hit!(your_stack_belongs_to_me); |
108 | return Ok(ExpandResult::str_err( | 111 | return Ok(ExpandResult::str_err( |
@@ -147,6 +150,55 @@ impl Expander { | |||
147 | } | 150 | } |
148 | }; | 151 | }; |
149 | 152 | ||
153 | let this = self; | ||
154 | |||
155 | let advance_state = move |db: &dyn DefDatabase| { | ||
156 | this.recursion_limit += 1; | ||
157 | let mark = Mark { | ||
158 | file_id: this.current_file_id, | ||
159 | ast_id_map: mem::take(&mut this.ast_id_map), | ||
160 | bomb: DropBomb::new("expansion mark dropped"), | ||
161 | }; | ||
162 | this.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); | ||
163 | this.current_file_id = file_id; | ||
164 | this.ast_id_map = db.ast_id_map(file_id); | ||
165 | mark | ||
166 | }; | ||
167 | |||
168 | Ok(ExpandResult { value: Some((raw_node, advance_state)), err }) | ||
169 | } | ||
170 | |||
171 | pub(crate) fn enter_expand_raw( | ||
172 | &mut self, | ||
173 | db: &dyn DefDatabase, | ||
174 | macro_call: ast::MacroCall, | ||
175 | ) -> Result<ExpandResult<Option<(Mark, SyntaxNode)>>, UnresolvedMacro> { | ||
176 | let (raw_node, mut advance_state, err) = match self.enter_expand_intern(db, macro_call)? { | ||
177 | ExpandResult { value: Some((raw_node, advance_state)), err } => { | ||
178 | (raw_node, advance_state, err) | ||
179 | } | ||
180 | ExpandResult { value: None, err } => return Ok(ExpandResult { value: None, err }), | ||
181 | }; | ||
182 | |||
183 | log::debug!("macro expansion {:#?}", raw_node); | ||
184 | |||
185 | let mark = advance_state(db); | ||
186 | |||
187 | Ok(ExpandResult { value: Some((mark, raw_node)), err }) | ||
188 | } | ||
189 | |||
190 | pub(crate) fn enter_expand<T: ast::AstNode>( | ||
191 | &mut self, | ||
192 | db: &dyn DefDatabase, | ||
193 | macro_call: ast::MacroCall, | ||
194 | ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> { | ||
195 | let (raw_node, mut advance_state, err) = match self.enter_expand_intern(db, macro_call)? { | ||
196 | ExpandResult { value: Some((raw_node, advance_state)), err } => { | ||
197 | (raw_node, advance_state, err) | ||
198 | } | ||
199 | ExpandResult { value: None, err } => return Ok(ExpandResult { value: None, err }), | ||
200 | }; | ||
201 | |||
150 | let node = match T::cast(raw_node) { | 202 | let node = match T::cast(raw_node) { |
151 | Some(it) => it, | 203 | Some(it) => it, |
152 | None => { | 204 | None => { |
@@ -157,15 +209,7 @@ impl Expander { | |||
157 | 209 | ||
158 | log::debug!("macro expansion {:#?}", node.syntax()); | 210 | log::debug!("macro expansion {:#?}", node.syntax()); |
159 | 211 | ||
160 | self.recursion_limit += 1; | 212 | let mark = advance_state(db); |
161 | let mark = Mark { | ||
162 | file_id: self.current_file_id, | ||
163 | ast_id_map: mem::take(&mut self.ast_id_map), | ||
164 | bomb: DropBomb::new("expansion mark dropped"), | ||
165 | }; | ||
166 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); | ||
167 | self.current_file_id = file_id; | ||
168 | self.ast_id_map = db.ast_id_map(file_id); | ||
169 | 213 | ||
170 | Ok(ExpandResult { value: Some((mark, node)), err }) | 214 | Ok(ExpandResult { value: Some((mark, node)), err }) |
171 | } | 215 | } |
@@ -191,7 +235,8 @@ impl Expander { | |||
191 | } | 235 | } |
192 | 236 | ||
193 | fn parse_path(&mut self, path: ast::Path) -> Option<Path> { | 237 | fn parse_path(&mut self, path: ast::Path) -> Option<Path> { |
194 | Path::from_src(path, &self.cfg_expander.hygiene) | 238 | let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene); |
239 | Path::from_src(path, &ctx) | ||
195 | } | 240 | } |
196 | 241 | ||
197 | fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { | 242 | fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { |
@@ -204,6 +249,7 @@ impl Expander { | |||
204 | } | 249 | } |
205 | } | 250 | } |
206 | 251 | ||
252 | #[derive(Debug)] | ||
207 | pub(crate) struct Mark { | 253 | pub(crate) struct Mark { |
208 | file_id: HirFileId, | 254 | file_id: HirFileId, |
209 | ast_id_map: Arc<AstIdMap>, | 255 | ast_id_map: Arc<AstIdMap>, |
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 @@ | |||
1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` | 1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` |
2 | //! representation. | 2 | //! representation. |
3 | 3 | ||
4 | use std::mem; | 4 | use std::{mem, sync::Arc}; |
5 | 5 | ||
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{ | 7 | use hir_expand::{ |
8 | ast_id_map::{AstIdMap, FileAstId}, | ||
8 | hygiene::Hygiene, | 9 | hygiene::Hygiene, |
9 | name::{name, AsName, Name}, | 10 | name::{name, AsName, Name}, |
10 | ExpandError, HirFileId, | 11 | ExpandError, HirFileId, |
@@ -39,20 +40,39 @@ use crate::{ | |||
39 | 40 | ||
40 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; | 41 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; |
41 | 42 | ||
42 | pub(crate) struct LowerCtx { | 43 | pub struct LowerCtx { |
43 | hygiene: Hygiene, | 44 | hygiene: Hygiene, |
45 | file_id: Option<HirFileId>, | ||
46 | source_ast_id_map: Option<Arc<AstIdMap>>, | ||
44 | } | 47 | } |
45 | 48 | ||
46 | impl LowerCtx { | 49 | impl LowerCtx { |
47 | pub(crate) fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { | 50 | pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { |
48 | LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) } | 51 | LowerCtx { |
52 | hygiene: Hygiene::new(db.upcast(), file_id), | ||
53 | file_id: Some(file_id), | ||
54 | source_ast_id_map: Some(db.ast_id_map(file_id)), | ||
55 | } | ||
56 | } | ||
57 | |||
58 | pub fn with_hygiene(hygiene: &Hygiene) -> Self { | ||
59 | LowerCtx { hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None } | ||
60 | } | ||
61 | |||
62 | pub(crate) fn hygiene(&self) -> &Hygiene { | ||
63 | &self.hygiene | ||
49 | } | 64 | } |
50 | pub(crate) fn with_hygiene(hygiene: &Hygiene) -> Self { | 65 | |
51 | LowerCtx { hygiene: hygiene.clone() } | 66 | pub(crate) fn file_id(&self) -> HirFileId { |
67 | self.file_id.unwrap() | ||
52 | } | 68 | } |
53 | 69 | ||
54 | pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> { | 70 | pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> { |
55 | Path::from_src(ast, &self.hygiene) | 71 | Path::from_src(ast, self) |
72 | } | ||
73 | |||
74 | pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<FileAstId<N>> { | ||
75 | self.source_ast_id_map.as_ref().map(|ast_id_map| ast_id_map.ast_id(item)) | ||
56 | } | 76 | } |
57 | } | 77 | } |
58 | 78 | ||
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 { | |||
123 | let loc = typ.lookup(db); | 123 | let loc = typ.lookup(db); |
124 | let item_tree = loc.id.item_tree(db); | 124 | let item_tree = loc.id.item_tree(db); |
125 | let typ = &item_tree[loc.id.value]; | 125 | let typ = &item_tree[loc.id.value]; |
126 | let type_ref = typ.type_ref.clone(); | ||
126 | 127 | ||
127 | Arc::new(TypeAliasData { | 128 | Arc::new(TypeAliasData { |
128 | name: typ.name.clone(), | 129 | name: typ.name.clone(), |
129 | type_ref: typ.type_ref.clone(), | 130 | type_ref: type_ref, |
130 | visibility: item_tree[typ.visibility].clone(), | 131 | visibility: item_tree[typ.visibility].clone(), |
131 | is_extern: typ.is_extern, | 132 | is_extern: typ.is_extern, |
132 | bounds: typ.bounds.to_vec(), | 133 | bounds: typ.bounds.to_vec(), |
@@ -202,12 +203,13 @@ impl ImplData { | |||
202 | let item_tree = impl_loc.id.item_tree(db); | 203 | let item_tree = impl_loc.id.item_tree(db); |
203 | let impl_def = &item_tree[impl_loc.id.value]; | 204 | let impl_def = &item_tree[impl_loc.id.value]; |
204 | let target_trait = impl_def.target_trait.clone(); | 205 | let target_trait = impl_def.target_trait.clone(); |
205 | let self_ty = impl_def.self_ty.clone(); | ||
206 | let is_negative = impl_def.is_negative; | 206 | let is_negative = impl_def.is_negative; |
207 | let module_id = impl_loc.container; | 207 | let module_id = impl_loc.container; |
208 | let container = AssocContainerId::ImplId(id); | 208 | let container = AssocContainerId::ImplId(id); |
209 | let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id); | 209 | let file_id = impl_loc.id.file_id(); |
210 | let self_ty = impl_def.self_ty.clone(); | ||
210 | 211 | ||
212 | let mut expander = Expander::new(db, file_id, module_id); | ||
211 | let items = collect_items( | 213 | let items = collect_items( |
212 | db, | 214 | db, |
213 | module_id, | 215 | 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 { | |||
189 | block_stack.push(self.source_ast_id_map.ast_id(&block)); | 189 | block_stack.push(self.source_ast_id_map.ast_id(&block)); |
190 | }, | 190 | }, |
191 | ast::Item(item) => { | 191 | ast::Item(item) => { |
192 | // FIXME: This triggers for macro calls in expression/pattern/type position | 192 | // FIXME: This triggers for macro calls in expression/pattern |
193 | let mod_items = self.lower_mod_item(&item, true); | 193 | if let Some(SyntaxKind::MACRO_TYPE) = node.parent().map(|p| p.kind()) { |
194 | let current_block = block_stack.last(); | 194 | // Ignore macros at type position |
195 | if let (Some(mod_items), Some(block)) = (mod_items, current_block) { | 195 | } else { |
196 | if !mod_items.0.is_empty() { | 196 | let mod_items = self.lower_mod_item(&item, true); |
197 | self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied()); | 197 | let current_block = block_stack.last(); |
198 | if let (Some(mod_items), Some(block)) = (mod_items, current_block) { | ||
199 | if !mod_items.0.is_empty() { | ||
200 | self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied()); | ||
201 | } | ||
198 | } | 202 | } |
199 | } | 203 | } |
200 | }, | 204 | }, |
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<T: ast::AstNode> AstIdWithPath<T> { | |||
676 | } | 676 | } |
677 | } | 677 | } |
678 | 678 | ||
679 | #[derive(Debug)] | ||
679 | pub struct UnresolvedMacro { | 680 | pub struct UnresolvedMacro { |
680 | pub path: ModPath, | 681 | pub path: ModPath, |
681 | } | 682 | } |
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 { | |||
48 | 48 | ||
49 | impl ModPath { | 49 | impl ModPath { |
50 | pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { | 50 | pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { |
51 | lower::lower_path(path, hygiene).map(|it| (*it.mod_path).clone()) | 51 | let ctx = LowerCtx::with_hygiene(hygiene); |
52 | lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone()) | ||
52 | } | 53 | } |
53 | 54 | ||
54 | pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { | 55 | pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { |
@@ -167,8 +168,8 @@ pub enum GenericArg { | |||
167 | impl Path { | 168 | impl Path { |
168 | /// Converts an `ast::Path` to `Path`. Works with use trees. | 169 | /// Converts an `ast::Path` to `Path`. Works with use trees. |
169 | /// It correctly handles `$crate` based path from macro call. | 170 | /// It correctly handles `$crate` based path from macro call. |
170 | pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<Path> { | 171 | pub fn from_src(path: ast::Path, ctx: &LowerCtx) -> Option<Path> { |
171 | lower::lower_path(path, hygiene) | 172 | lower::lower_path(path, ctx) |
172 | } | 173 | } |
173 | 174 | ||
174 | /// Converts a known mod path to `Path`. | 175 | /// 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; | |||
6 | use std::sync::Arc; | 6 | use std::sync::Arc; |
7 | 7 | ||
8 | use either::Either; | 8 | use either::Either; |
9 | use hir_expand::{ | 9 | use hir_expand::name::{name, AsName}; |
10 | hygiene::Hygiene, | ||
11 | name::{name, AsName}, | ||
12 | }; | ||
13 | use syntax::ast::{self, AstNode, TypeBoundsOwner}; | 10 | use syntax::ast::{self, AstNode, TypeBoundsOwner}; |
14 | 11 | ||
15 | use super::AssociatedTypeBinding; | 12 | use super::AssociatedTypeBinding; |
@@ -23,12 +20,12 @@ pub(super) use lower_use::lower_use_tree; | |||
23 | 20 | ||
24 | /// Converts an `ast::Path` to `Path`. Works with use trees. | 21 | /// Converts an `ast::Path` to `Path`. Works with use trees. |
25 | /// It correctly handles `$crate` based path from macro call. | 22 | /// It correctly handles `$crate` based path from macro call. |
26 | pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> { | 23 | pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> { |
27 | let mut kind = PathKind::Plain; | 24 | let mut kind = PathKind::Plain; |
28 | let mut type_anchor = None; | 25 | let mut type_anchor = None; |
29 | let mut segments = Vec::new(); | 26 | let mut segments = Vec::new(); |
30 | let mut generic_args = Vec::new(); | 27 | let mut generic_args = Vec::new(); |
31 | let ctx = LowerCtx::with_hygiene(hygiene); | 28 | let hygiene = ctx.hygiene(); |
32 | loop { | 29 | loop { |
33 | let segment = path.segment()?; | 30 | let segment = path.segment()?; |
34 | 31 | ||
@@ -43,10 +40,10 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> | |||
43 | Either::Left(name) => { | 40 | Either::Left(name) => { |
44 | let args = segment | 41 | let args = segment |
45 | .generic_arg_list() | 42 | .generic_arg_list() |
46 | .and_then(|it| lower_generic_args(&ctx, it)) | 43 | .and_then(|it| lower_generic_args(ctx, it)) |
47 | .or_else(|| { | 44 | .or_else(|| { |
48 | lower_generic_args_from_fn_path( | 45 | lower_generic_args_from_fn_path( |
49 | &ctx, | 46 | ctx, |
50 | segment.param_list(), | 47 | segment.param_list(), |
51 | segment.ret_type(), | 48 | segment.ret_type(), |
52 | ) | 49 | ) |
@@ -64,7 +61,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> | |||
64 | ast::PathSegmentKind::Type { type_ref, trait_ref } => { | 61 | ast::PathSegmentKind::Type { type_ref, trait_ref } => { |
65 | assert!(path.qualifier().is_none()); // this can only occur at the first segment | 62 | assert!(path.qualifier().is_none()); // this can only occur at the first segment |
66 | 63 | ||
67 | let self_type = TypeRef::from_ast(&ctx, type_ref?); | 64 | let self_type = TypeRef::from_ast(ctx, type_ref?); |
68 | 65 | ||
69 | match trait_ref { | 66 | match trait_ref { |
70 | // <T>::foo | 67 | // <T>::foo |
@@ -74,7 +71,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> | |||
74 | } | 71 | } |
75 | // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo | 72 | // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo |
76 | Some(trait_ref) => { | 73 | Some(trait_ref) => { |
77 | let path = Path::from_src(trait_ref.path()?, hygiene)?; | 74 | let path = Path::from_src(trait_ref.path()?, ctx)?; |
78 | let mod_path = (*path.mod_path).clone(); | 75 | let mod_path = (*path.mod_path).clone(); |
79 | let num_segments = path.mod_path.segments.len(); | 76 | let num_segments = path.mod_path.segments.len(); |
80 | kind = mod_path.kind; | 77 | 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 @@ | |||
1 | //! HIR for references to types. Paths in these are not yet resolved. They can | 1 | //! HIR for references to types. Paths in these are not yet resolved. They can |
2 | //! be directly created from an ast::TypeRef, without further queries. | 2 | //! be directly created from an ast::TypeRef, without further queries. |
3 | use hir_expand::name::Name; | 3 | use std::borrow::Cow; |
4 | use syntax::ast; | ||
5 | 4 | ||
6 | use crate::{body::LowerCtx, path::Path}; | 5 | use hir_expand::{ast_id_map::FileAstId, name::Name, ExpandResult, InFile}; |
6 | use syntax::{algo::SyntaxRewriter, ast, AstNode, SyntaxKind, SyntaxNode}; | ||
7 | |||
8 | use crate::{ | ||
9 | body::{Expander, LowerCtx}, | ||
10 | db::DefDatabase, | ||
11 | path::Path, | ||
12 | ModuleId, | ||
13 | }; | ||
7 | 14 | ||
8 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | 15 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
9 | pub enum Mutability { | 16 | pub enum Mutability { |
@@ -68,6 +75,7 @@ impl TraitRef { | |||
68 | } | 75 | } |
69 | } | 76 | } |
70 | } | 77 | } |
78 | |||
71 | /// Compare ty::Ty | 79 | /// Compare ty::Ty |
72 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | 80 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
73 | pub enum TypeRef { | 81 | pub enum TypeRef { |
@@ -84,6 +92,7 @@ pub enum TypeRef { | |||
84 | // For | 92 | // For |
85 | ImplTrait(Vec<TypeBound>), | 93 | ImplTrait(Vec<TypeBound>), |
86 | DynTrait(Vec<TypeBound>), | 94 | DynTrait(Vec<TypeBound>), |
95 | Macro(InFile<FileAstId<ast::MacroCall>>), | ||
87 | Error, | 96 | Error, |
88 | } | 97 | } |
89 | 98 | ||
@@ -176,8 +185,13 @@ impl TypeRef { | |||
176 | ast::Type::DynTraitType(inner) => { | 185 | ast::Type::DynTraitType(inner) => { |
177 | TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) | 186 | TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) |
178 | } | 187 | } |
179 | // FIXME: Macros in type position are not yet supported. | 188 | ast::Type::MacroType(mt) => match mt.macro_call() { |
180 | ast::Type::MacroType(_) => TypeRef::Error, | 189 | Some(mc) => ctx |
190 | .ast_id(&mc) | ||
191 | .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc))) | ||
192 | .unwrap_or(TypeRef::Error), | ||
193 | None => TypeRef::Error, | ||
194 | }, | ||
181 | } | 195 | } |
182 | } | 196 | } |
183 | 197 | ||
@@ -193,6 +207,16 @@ impl TypeRef { | |||
193 | TypeRef::Tuple(Vec::new()) | 207 | TypeRef::Tuple(Vec::new()) |
194 | } | 208 | } |
195 | 209 | ||
210 | pub fn has_macro_calls(&self) -> bool { | ||
211 | let mut has_macro_call = false; | ||
212 | self.walk(&mut |ty_ref| { | ||
213 | if let TypeRef::Macro(_) = ty_ref { | ||
214 | has_macro_call |= true | ||
215 | } | ||
216 | }); | ||
217 | has_macro_call | ||
218 | } | ||
219 | |||
196 | pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) { | 220 | pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) { |
197 | go(self, f); | 221 | go(self, f); |
198 | 222 | ||
@@ -215,7 +239,7 @@ impl TypeRef { | |||
215 | } | 239 | } |
216 | } | 240 | } |
217 | TypeRef::Path(path) => go_path(path, f), | 241 | TypeRef::Path(path) => go_path(path, f), |
218 | TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {} | 242 | TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {} |
219 | }; | 243 | }; |
220 | } | 244 | } |
221 | 245 | ||
@@ -290,3 +314,69 @@ impl TypeBound { | |||
290 | } | 314 | } |
291 | } | 315 | } |
292 | } | 316 | } |
317 | |||
318 | pub fn expand_type_ref<'a>( | ||
319 | db: &dyn DefDatabase, | ||
320 | module_id: ModuleId, | ||
321 | type_ref: &'a TypeRef, | ||
322 | ) -> Option<Cow<'a, TypeRef>> { | ||
323 | let macro_call = match type_ref { | ||
324 | TypeRef::Macro(macro_call) => macro_call, | ||
325 | _ => return Some(Cow::Borrowed(type_ref)), | ||
326 | }; | ||
327 | |||
328 | let file_id = macro_call.file_id; | ||
329 | let macro_call = macro_call.to_node(db.upcast()); | ||
330 | |||
331 | let mut expander = Expander::new(db, file_id, module_id); | ||
332 | let expanded = expand(db, &mut expander, ¯o_call, true)?; | ||
333 | |||
334 | let node = ast::Type::cast(expanded)?; | ||
335 | |||
336 | let ctx = LowerCtx::new(db, file_id); | ||
337 | return Some(Cow::Owned(TypeRef::from_ast(&ctx, node))); | ||
338 | |||
339 | fn expand( | ||
340 | db: &dyn DefDatabase, | ||
341 | expander: &mut Expander, | ||
342 | macro_call: &ast::MacroCall, | ||
343 | expect_type: bool, | ||
344 | ) -> Option<SyntaxNode> { | ||
345 | let (mark, mut expanded) = match expander.enter_expand_raw(db, macro_call.clone()) { | ||
346 | Ok(ExpandResult { value: Some((mark, expanded)), .. }) => (mark, expanded), | ||
347 | _ => return None, | ||
348 | }; | ||
349 | |||
350 | if expect_type && !ast::Type::can_cast(expanded.kind()) { | ||
351 | expander.exit(db, mark); | ||
352 | return None; | ||
353 | } | ||
354 | |||
355 | if ast::MacroType::can_cast(expanded.kind()) { | ||
356 | expanded = expanded.first_child()?; // MACRO_CALL | ||
357 | } | ||
358 | |||
359 | let mut rewriter = SyntaxRewriter::default(); | ||
360 | |||
361 | let children = expanded.descendants().filter_map(ast::MacroCall::cast); | ||
362 | for child in children { | ||
363 | if let Some(new_node) = expand(db, expander, &child, false) { | ||
364 | if expanded == *child.syntax() { | ||
365 | expanded = new_node; | ||
366 | } else { | ||
367 | let parent = child.syntax().parent(); | ||
368 | let old_node = match &parent { | ||
369 | Some(node) if node.kind() == SyntaxKind::MACRO_TYPE => node, | ||
370 | _ => child.syntax(), | ||
371 | }; | ||
372 | rewriter.replace(old_node, &new_node) | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | |||
377 | expander.exit(db, mark); | ||
378 | |||
379 | let res = rewriter.rewrite(&expanded); | ||
380 | Some(res) | ||
381 | } | ||
382 | } | ||