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