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