]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/type_ref.rs
hir_def: refactor expand_macro_type and cleanups
[rust.git] / crates / hir_def / src / type_ref.rs
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.
3
4 use hir_expand::{ast_id_map::FileAstId, name::Name, ExpandResult, InFile};
5 use syntax::ast;
6
7 use crate::{
8     body::{Expander, LowerCtx},
9     db::DefDatabase,
10     path::Path,
11     ModuleId,
12 };
13
14 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
15 pub enum Mutability {
16     Shared,
17     Mut,
18 }
19
20 impl Mutability {
21     pub fn from_mutable(mutable: bool) -> Mutability {
22         if mutable {
23             Mutability::Mut
24         } else {
25             Mutability::Shared
26         }
27     }
28
29     pub fn as_keyword_for_ref(self) -> &'static str {
30         match self {
31             Mutability::Shared => "",
32             Mutability::Mut => "mut ",
33         }
34     }
35
36     pub fn as_keyword_for_ptr(self) -> &'static str {
37         match self {
38             Mutability::Shared => "const ",
39             Mutability::Mut => "mut ",
40         }
41     }
42 }
43
44 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
45 pub enum Rawness {
46     RawPtr,
47     Ref,
48 }
49
50 impl Rawness {
51     pub fn from_raw(is_raw: bool) -> Rawness {
52         if is_raw {
53             Rawness::RawPtr
54         } else {
55             Rawness::Ref
56         }
57     }
58 }
59
60 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
61 pub struct TraitRef {
62     pub path: Path,
63 }
64
65 impl TraitRef {
66     /// Converts an `ast::PathType` to a `hir::TraitRef`.
67     pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
68         // FIXME: Use `Path::from_src`
69         match node {
70             ast::Type::PathType(path) => {
71                 path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
72             }
73             _ => None,
74         }
75     }
76 }
77
78 /// Compare ty::Ty
79 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
80 pub enum TypeRef {
81     Never,
82     Placeholder,
83     Tuple(Vec<TypeRef>),
84     Path(Path),
85     RawPtr(Box<TypeRef>, Mutability),
86     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
87     Array(Box<TypeRef> /*, Expr*/),
88     Slice(Box<TypeRef>),
89     /// A fn pointer. Last element of the vector is the return type.
90     Fn(Vec<TypeRef>, bool /*varargs*/),
91     // For
92     ImplTrait(Vec<TypeBound>),
93     DynTrait(Vec<TypeBound>),
94     Macro(InFile<FileAstId<ast::MacroCall>>),
95     Error,
96 }
97
98 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
99 pub struct LifetimeRef {
100     pub name: Name,
101 }
102
103 impl LifetimeRef {
104     pub(crate) fn new_name(name: Name) -> Self {
105         LifetimeRef { name }
106     }
107
108     pub(crate) fn new(lifetime: &ast::Lifetime) -> Self {
109         LifetimeRef { name: Name::new_lifetime(lifetime) }
110     }
111
112     pub fn missing() -> LifetimeRef {
113         LifetimeRef { name: Name::missing() }
114     }
115 }
116
117 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
118 pub enum TypeBound {
119     Path(Path),
120     // ForLifetime(Vec<LifetimeRef>, Path), FIXME ForLifetime
121     Lifetime(LifetimeRef),
122     Error,
123 }
124
125 impl TypeRef {
126     /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
127     pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
128         match node {
129             ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
130             ast::Type::TupleType(inner) => {
131                 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
132             }
133             ast::Type::NeverType(..) => TypeRef::Never,
134             ast::Type::PathType(inner) => {
135                 // FIXME: Use `Path::from_src`
136                 inner
137                     .path()
138                     .and_then(|it| ctx.lower_path(it))
139                     .map(TypeRef::Path)
140                     .unwrap_or(TypeRef::Error)
141             }
142             ast::Type::PtrType(inner) => {
143                 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
144                 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
145                 TypeRef::RawPtr(Box::new(inner_ty), mutability)
146             }
147             ast::Type::ArrayType(inner) => {
148                 TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
149             }
150             ast::Type::SliceType(inner) => {
151                 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
152             }
153             ast::Type::RefType(inner) => {
154                 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
155                 let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
156                 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
157                 TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
158             }
159             ast::Type::InferType(_inner) => TypeRef::Placeholder,
160             ast::Type::FnPtrType(inner) => {
161                 let ret_ty = inner
162                     .ret_type()
163                     .and_then(|rt| rt.ty())
164                     .map(|it| TypeRef::from_ast(ctx, it))
165                     .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
166                 let mut is_varargs = false;
167                 let mut params = if let Some(pl) = inner.param_list() {
168                     if let Some(param) = pl.params().last() {
169                         is_varargs = param.dotdotdot_token().is_some();
170                     }
171
172                     pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(&ctx, it)).collect()
173                 } else {
174                     Vec::new()
175                 };
176                 params.push(ret_ty);
177                 TypeRef::Fn(params, is_varargs)
178             }
179             // for types are close enough for our purposes to the inner type for now...
180             ast::Type::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
181             ast::Type::ImplTraitType(inner) => {
182                 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
183             }
184             ast::Type::DynTraitType(inner) => {
185                 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
186             }
187             ast::Type::MacroType(mt) => match mt.macro_call() {
188                 Some(mc) => ctx
189                     .ast_id(&mc)
190                     .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
191                     .unwrap_or(TypeRef::Error),
192                 None => TypeRef::Error,
193             },
194         }
195     }
196
197     pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::Type>) -> Self {
198         if let Some(node) = node {
199             TypeRef::from_ast(ctx, node)
200         } else {
201             TypeRef::Error
202         }
203     }
204
205     pub(crate) fn unit() -> TypeRef {
206         TypeRef::Tuple(Vec::new())
207     }
208
209     pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
210         go(self, f);
211
212         fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
213             f(type_ref);
214             match type_ref {
215                 TypeRef::Fn(types, _) | TypeRef::Tuple(types) => {
216                     types.iter().for_each(|t| go(t, f))
217                 }
218                 TypeRef::RawPtr(type_ref, _)
219                 | TypeRef::Reference(type_ref, ..)
220                 | TypeRef::Array(type_ref)
221                 | TypeRef::Slice(type_ref) => go(&type_ref, f),
222                 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
223                     for bound in bounds {
224                         match bound {
225                             TypeBound::Path(path) => go_path(path, f),
226                             TypeBound::Lifetime(_) | TypeBound::Error => (),
227                         }
228                     }
229                 }
230                 TypeRef::Path(path) => go_path(path, f),
231                 TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
232             };
233         }
234
235         fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
236             if let Some(type_ref) = path.type_anchor() {
237                 go(type_ref, f);
238             }
239             for segment in path.segments().iter() {
240                 if let Some(args_and_bindings) = segment.args_and_bindings {
241                     for arg in &args_and_bindings.args {
242                         match arg {
243                             crate::path::GenericArg::Type(type_ref) => {
244                                 go(type_ref, f);
245                             }
246                             crate::path::GenericArg::Lifetime(_) => {}
247                         }
248                     }
249                     for binding in &args_and_bindings.bindings {
250                         if let Some(type_ref) = &binding.type_ref {
251                             go(type_ref, f);
252                         }
253                         for bound in &binding.bounds {
254                             match bound {
255                                 TypeBound::Path(path) => go_path(path, f),
256                                 TypeBound::Lifetime(_) | TypeBound::Error => (),
257                             }
258                         }
259                     }
260                 }
261             }
262         }
263     }
264 }
265
266 pub(crate) fn type_bounds_from_ast(
267     lower_ctx: &LowerCtx,
268     type_bounds_opt: Option<ast::TypeBoundList>,
269 ) -> Vec<TypeBound> {
270     if let Some(type_bounds) = type_bounds_opt {
271         type_bounds.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
272     } else {
273         vec![]
274     }
275 }
276
277 impl TypeBound {
278     pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
279         match node.kind() {
280             ast::TypeBoundKind::PathType(path_type) => {
281                 let path = match path_type.path() {
282                     Some(p) => p,
283                     None => return TypeBound::Error,
284                 };
285
286                 let path = match ctx.lower_path(path) {
287                     Some(p) => p,
288                     None => return TypeBound::Error,
289                 };
290                 TypeBound::Path(path)
291             }
292             ast::TypeBoundKind::ForType(_) => TypeBound::Error, // FIXME ForType
293             ast::TypeBoundKind::Lifetime(lifetime) => {
294                 TypeBound::Lifetime(LifetimeRef::new(&lifetime))
295             }
296         }
297     }
298
299     pub fn as_path(&self) -> Option<&Path> {
300         match self {
301             TypeBound::Path(p) => Some(p),
302             _ => None,
303         }
304     }
305 }
306
307 pub fn expand_macro_type(
308     db: &dyn DefDatabase,
309     module_id: ModuleId,
310     macro_type: &TypeRef,
311 ) -> Option<TypeRef> {
312     let macro_call = match macro_type {
313         TypeRef::Macro(macro_call) => macro_call,
314         _ => panic!("expected TypeRef::Macro"),
315     };
316
317     let file_id = macro_call.file_id;
318     let macro_call = macro_call.to_node(db.upcast());
319
320     let mut expander = Expander::new(db, file_id, module_id);
321     let (file_id, expanded) = match expander.enter_expand::<ast::Type>(db, macro_call.clone()) {
322         Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
323             let file_id = expander.current_file_id();
324             expander.exit(db, mark);
325             (file_id, expanded)
326         }
327         _ => return None,
328     };
329
330     let ctx = LowerCtx::new(db, file_id);
331     return Some(TypeRef::from_ast(&ctx, expanded));
332 }