]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/data.rs
Merge #8967
[rust.git] / crates / hir_def / src / data.rs
1 //! Contains basic data about various HIR declarations.
2
3 use std::sync::Arc;
4
5 use hir_expand::{name::Name, InFile};
6 use syntax::ast;
7
8 use crate::{
9     attr::Attrs,
10     body::Expander,
11     db::DefDatabase,
12     intern::Interned,
13     item_tree::{AssocItem, FnFlags, ItemTreeId, ModItem, Param},
14     type_ref::{TraitRef, TypeBound, TypeRef},
15     visibility::RawVisibility,
16     AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
17     Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
18 };
19
20 #[derive(Debug, Clone, PartialEq, Eq)]
21 pub struct FunctionData {
22     pub name: Name,
23     pub params: Vec<Interned<TypeRef>>,
24     pub ret_type: Interned<TypeRef>,
25     pub attrs: Attrs,
26     pub visibility: RawVisibility,
27     pub abi: Option<Interned<str>>,
28     flags: FnFlags,
29 }
30
31 impl FunctionData {
32     pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
33         let loc = func.lookup(db);
34         let krate = loc.container.module(db).krate;
35         let crate_graph = db.crate_graph();
36         let cfg_options = &crate_graph[krate].cfg_options;
37         let item_tree = loc.id.item_tree(db);
38         let func = &item_tree[loc.id.value];
39
40         let enabled_params = func
41             .params
42             .clone()
43             .filter(|&param| item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options));
44
45         // If last cfg-enabled param is a `...` param, it's a varargs function.
46         let is_varargs = enabled_params
47             .clone()
48             .next_back()
49             .map_or(false, |param| matches!(item_tree[param], Param::Varargs));
50
51         let mut flags = func.flags;
52         if is_varargs {
53             flags.bits |= FnFlags::IS_VARARGS;
54         }
55
56         Arc::new(FunctionData {
57             name: func.name.clone(),
58             params: enabled_params
59                 .clone()
60                 .filter_map(|id| match &item_tree[id] {
61                     Param::Normal(ty) => Some(ty.clone()),
62                     Param::Varargs => None,
63                 })
64                 .collect(),
65             ret_type: func.ret_type.clone(),
66             attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
67             visibility: item_tree[func.visibility].clone(),
68             abi: func.abi.clone(),
69             flags,
70         })
71     }
72
73     pub fn has_body(&self) -> bool {
74         self.flags.bits & FnFlags::HAS_BODY != 0
75     }
76
77     /// True if the first param is `self`. This is relevant to decide whether this
78     /// can be called as a method.
79     pub fn has_self_param(&self) -> bool {
80         self.flags.bits & FnFlags::HAS_SELF_PARAM != 0
81     }
82
83     pub fn is_default(&self) -> bool {
84         self.flags.bits & FnFlags::IS_DEFAULT != 0
85     }
86
87     pub fn is_const(&self) -> bool {
88         self.flags.bits & FnFlags::IS_CONST != 0
89     }
90
91     pub fn is_async(&self) -> bool {
92         self.flags.bits & FnFlags::IS_ASYNC != 0
93     }
94
95     pub fn is_unsafe(&self) -> bool {
96         self.flags.bits & FnFlags::IS_UNSAFE != 0
97     }
98
99     pub fn is_in_extern_block(&self) -> bool {
100         self.flags.bits & FnFlags::IS_IN_EXTERN_BLOCK != 0
101     }
102
103     pub fn is_varargs(&self) -> bool {
104         self.flags.bits & FnFlags::IS_VARARGS != 0
105     }
106 }
107
108 #[derive(Debug, Clone, PartialEq, Eq)]
109 pub struct TypeAliasData {
110     pub name: Name,
111     pub type_ref: Option<Interned<TypeRef>>,
112     pub visibility: RawVisibility,
113     pub is_extern: bool,
114     /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
115     pub bounds: Vec<Interned<TypeBound>>,
116 }
117
118 impl TypeAliasData {
119     pub(crate) fn type_alias_data_query(
120         db: &dyn DefDatabase,
121         typ: TypeAliasId,
122     ) -> Arc<TypeAliasData> {
123         let loc = typ.lookup(db);
124         let item_tree = loc.id.item_tree(db);
125         let typ = &item_tree[loc.id.value];
126
127         Arc::new(TypeAliasData {
128             name: typ.name.clone(),
129             type_ref: typ.type_ref.clone(),
130             visibility: item_tree[typ.visibility].clone(),
131             is_extern: typ.is_extern,
132             bounds: typ.bounds.to_vec(),
133         })
134     }
135 }
136
137 #[derive(Debug, Clone, PartialEq, Eq)]
138 pub struct TraitData {
139     pub name: Name,
140     pub items: Vec<(Name, AssocItemId)>,
141     pub is_auto: bool,
142     pub is_unsafe: bool,
143     pub visibility: RawVisibility,
144     pub bounds: Box<[Interned<TypeBound>]>,
145 }
146
147 impl TraitData {
148     pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
149         let tr_loc = tr.lookup(db);
150         let item_tree = tr_loc.id.item_tree(db);
151         let tr_def = &item_tree[tr_loc.id.value];
152         let name = tr_def.name.clone();
153         let is_auto = tr_def.is_auto;
154         let is_unsafe = tr_def.is_unsafe;
155         let module_id = tr_loc.container;
156         let container = AssocContainerId::TraitId(tr);
157         let visibility = item_tree[tr_def.visibility].clone();
158         let bounds = tr_def.bounds.clone();
159         let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id);
160
161         let items = collect_items(
162             db,
163             module_id,
164             &mut expander,
165             tr_def.items.iter().copied(),
166             tr_loc.id.file_id(),
167             container,
168             100,
169         );
170
171         Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility, bounds })
172     }
173
174     pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
175         self.items.iter().filter_map(|(_name, item)| match item {
176             AssocItemId::TypeAliasId(t) => Some(*t),
177             _ => None,
178         })
179     }
180
181     pub fn associated_type_by_name(&self, name: &Name) -> Option<TypeAliasId> {
182         self.items.iter().find_map(|(item_name, item)| match item {
183             AssocItemId::TypeAliasId(t) if item_name == name => Some(*t),
184             _ => None,
185         })
186     }
187 }
188
189 #[derive(Debug, Clone, PartialEq, Eq)]
190 pub struct ImplData {
191     pub target_trait: Option<Interned<TraitRef>>,
192     pub self_ty: Interned<TypeRef>,
193     pub items: Vec<AssocItemId>,
194     pub is_negative: bool,
195 }
196
197 impl ImplData {
198     pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
199         let _p = profile::span("impl_data_query");
200         let impl_loc = id.lookup(db);
201
202         let item_tree = impl_loc.id.item_tree(db);
203         let impl_def = &item_tree[impl_loc.id.value];
204         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;
207         let module_id = impl_loc.container;
208         let container = AssocContainerId::ImplId(id);
209         let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id);
210
211         let items = collect_items(
212             db,
213             module_id,
214             &mut expander,
215             impl_def.items.iter().copied(),
216             impl_loc.id.file_id(),
217             container,
218             100,
219         );
220         let items = items.into_iter().map(|(_, item)| item).collect();
221
222         Arc::new(ImplData { target_trait, self_ty, items, is_negative })
223     }
224 }
225
226 #[derive(Debug, Clone, PartialEq, Eq)]
227 pub struct ConstData {
228     /// const _: () = ();
229     pub name: Option<Name>,
230     pub type_ref: Interned<TypeRef>,
231     pub visibility: RawVisibility,
232 }
233
234 impl ConstData {
235     pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
236         let loc = konst.lookup(db);
237         let item_tree = loc.id.item_tree(db);
238         let konst = &item_tree[loc.id.value];
239
240         Arc::new(ConstData {
241             name: konst.name.clone(),
242             type_ref: konst.type_ref.clone(),
243             visibility: item_tree[konst.visibility].clone(),
244         })
245     }
246 }
247
248 #[derive(Debug, Clone, PartialEq, Eq)]
249 pub struct StaticData {
250     pub name: Option<Name>,
251     pub type_ref: Interned<TypeRef>,
252     pub visibility: RawVisibility,
253     pub mutable: bool,
254     pub is_extern: bool,
255 }
256
257 impl StaticData {
258     pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
259         let node = konst.lookup(db);
260         let item_tree = node.id.item_tree(db);
261         let statik = &item_tree[node.id.value];
262
263         Arc::new(StaticData {
264             name: Some(statik.name.clone()),
265             type_ref: statik.type_ref.clone(),
266             visibility: item_tree[statik.visibility].clone(),
267             mutable: statik.mutable,
268             is_extern: statik.is_extern,
269         })
270     }
271 }
272
273 fn collect_items(
274     db: &dyn DefDatabase,
275     module: ModuleId,
276     expander: &mut Expander,
277     assoc_items: impl Iterator<Item = AssocItem>,
278     file_id: crate::HirFileId,
279     container: AssocContainerId,
280     limit: usize,
281 ) -> Vec<(Name, AssocItemId)> {
282     if limit == 0 {
283         return Vec::new();
284     }
285
286     let item_tree = db.file_item_tree(file_id);
287     let crate_graph = db.crate_graph();
288     let cfg_options = &crate_graph[module.krate].cfg_options;
289
290     let mut items = Vec::new();
291     for item in assoc_items {
292         let attrs = item_tree.attrs(db, module.krate, ModItem::from(item).into());
293         if !attrs.is_cfg_enabled(cfg_options) {
294             continue;
295         }
296
297         match item {
298             AssocItem::Function(id) => {
299                 let item = &item_tree[id];
300                 let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
301                 items.push((item.name.clone(), def.into()));
302             }
303             AssocItem::Const(id) => {
304                 let item = &item_tree[id];
305                 let name = match item.name.clone() {
306                     Some(name) => name,
307                     None => continue,
308                 };
309                 let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
310                 items.push((name, def.into()));
311             }
312             AssocItem::TypeAlias(id) => {
313                 let item = &item_tree[id];
314                 let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
315                 items.push((item.name.clone(), def.into()));
316             }
317             AssocItem::MacroCall(call) => {
318                 let call = &item_tree[call];
319                 let ast_id_map = db.ast_id_map(file_id);
320                 let root = db.parse_or_expand(file_id).unwrap();
321                 let call = ast_id_map.get(call.ast_id).to_node(&root);
322                 let res = expander.enter_expand(db, call);
323
324                 if let Ok(res) = res {
325                     if let Some((mark, mac)) = res.value {
326                         let src: InFile<ast::MacroItems> = expander.to_source(mac);
327                         let item_tree = db.file_item_tree(src.file_id);
328                         let iter =
329                             item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
330                         items.extend(collect_items(
331                             db,
332                             module,
333                             expander,
334                             iter,
335                             src.file_id,
336                             container,
337                             limit - 1,
338                         ));
339
340                         expander.exit(db, mark);
341                     }
342                 }
343             }
344         }
345     }
346
347     items
348 }