aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/type_ref.rs
diff options
context:
space:
mode:
authorcynecx <[email protected]>2021-04-10 16:49:12 +0100
committercynecx <[email protected]>2021-04-17 15:24:56 +0100
commitcf3b4f1e208247c9d171273dabff9c6b3c98a240 (patch)
tree0cae2703b98ba8640ce6693abe32fb51fe27fdda /crates/hir_def/src/type_ref.rs
parentfb2d284f28f70426e39e1b92d95bdbb217a48109 (diff)
hir_ty: Expand macros at type position
Diffstat (limited to 'crates/hir_def/src/type_ref.rs')
-rw-r--r--crates/hir_def/src/type_ref.rs102
1 files changed, 96 insertions, 6 deletions
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.
3use hir_expand::name::Name; 3use std::borrow::Cow;
4use syntax::ast;
5 4
6use crate::{body::LowerCtx, path::Path}; 5use hir_expand::{ast_id_map::FileAstId, name::Name, ExpandResult, InFile};
6use syntax::{algo::SyntaxRewriter, ast, AstNode, SyntaxKind, SyntaxNode};
7
8use 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)]
9pub enum Mutability { 16pub 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)]
73pub enum TypeRef { 81pub 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
318pub 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, &macro_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}