]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/data.rs
Merge #9026
[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 }
145
146 impl TraitData {
147     pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
148         let tr_loc = tr.lookup(db);
149         let item_tree = tr_loc.id.item_tree(db);
150         let tr_def = &item_tree[tr_loc.id.value];
151         let name = tr_def.name.clone();
152         let is_auto = tr_def.is_auto;
153         let is_unsafe = tr_def.is_unsafe;
154         let module_id = tr_loc.container;
155         let container = AssocContainerId::TraitId(tr);
156         let visibility = item_tree[tr_def.visibility].clone();
157         let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id);
158
159         let items = collect_items(
160             db,
161             module_id,
162             &mut expander,
163             tr_def.items.iter().copied(),
164             tr_loc.id.file_id(),
165             container,
166             100,
167         );
168
169         Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility })
170     }
171
172     pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
173         self.items.iter().filter_map(|(_name, item)| match item {
174             AssocItemId::TypeAliasId(t) => Some(*t),
175             _ => None,
176         })
177     }
178
179     pub fn associated_type_by_name(&self, name: &Name) -> Option<TypeAliasId> {
180         self.items.iter().find_map(|(item_name, item)| match item {
181             AssocItemId::TypeAliasId(t) if item_name == name => Some(*t),
182             _ => None,
183         })
184     }
185 }
186
187 #[derive(Debug, Clone, PartialEq, Eq)]
188 pub struct ImplData {
189     pub target_trait: Option<Interned<TraitRef>>,
190     pub self_ty: Interned<TypeRef>,
191     pub items: Vec<AssocItemId>,
192     pub is_negative: bool,
193 }
194
195 impl ImplData {
196     pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
197         let _p = profile::span("impl_data_query");
198         let impl_loc = id.lookup(db);
199
200         let item_tree = impl_loc.id.item_tree(db);
201         let impl_def = &item_tree[impl_loc.id.value];
202         let target_trait = impl_def.target_trait.clone();
203         let self_ty = impl_def.self_ty.clone();
204         let is_negative = impl_def.is_negative;
205         let module_id = impl_loc.container;
206         let container = AssocContainerId::ImplId(id);
207         let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id);
208
209         let items = collect_items(
210             db,
211             module_id,
212             &mut expander,
213             impl_def.items.iter().copied(),
214             impl_loc.id.file_id(),
215             container,
216             100,
217         );
218         let items = items.into_iter().map(|(_, item)| item).collect();
219
220         Arc::new(ImplData { target_trait, self_ty, items, is_negative })
221     }
222 }
223
224 #[derive(Debug, Clone, PartialEq, Eq)]
225 pub struct ConstData {
226     /// const _: () = ();
227     pub name: Option<Name>,
228     pub type_ref: Interned<TypeRef>,
229     pub visibility: RawVisibility,
230 }
231
232 impl ConstData {
233     pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
234         let loc = konst.lookup(db);
235         let item_tree = loc.id.item_tree(db);
236         let konst = &item_tree[loc.id.value];
237
238         Arc::new(ConstData {
239             name: konst.name.clone(),
240             type_ref: konst.type_ref.clone(),
241             visibility: item_tree[konst.visibility].clone(),
242         })
243     }
244 }
245
246 #[derive(Debug, Clone, PartialEq, Eq)]
247 pub struct StaticData {
248     pub name: Option<Name>,
249     pub type_ref: Interned<TypeRef>,
250     pub visibility: RawVisibility,
251     pub mutable: bool,
252     pub is_extern: bool,
253 }
254
255 impl StaticData {
256     pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
257         let node = konst.lookup(db);
258         let item_tree = node.id.item_tree(db);
259         let statik = &item_tree[node.id.value];
260
261         Arc::new(StaticData {
262             name: Some(statik.name.clone()),
263             type_ref: statik.type_ref.clone(),
264             visibility: item_tree[statik.visibility].clone(),
265             mutable: statik.mutable,
266             is_extern: statik.is_extern,
267         })
268     }
269 }
270
271 fn collect_items(
272     db: &dyn DefDatabase,
273     module: ModuleId,
274     expander: &mut Expander,
275     assoc_items: impl Iterator<Item = AssocItem>,
276     file_id: crate::HirFileId,
277     container: AssocContainerId,
278     limit: usize,
279 ) -> Vec<(Name, AssocItemId)> {
280     if limit == 0 {
281         return Vec::new();
282     }
283
284     let item_tree = db.file_item_tree(file_id);
285     let crate_graph = db.crate_graph();
286     let cfg_options = &crate_graph[module.krate].cfg_options;
287
288     let mut items = Vec::new();
289     for item in assoc_items {
290         let attrs = item_tree.attrs(db, module.krate, ModItem::from(item).into());
291         if !attrs.is_cfg_enabled(cfg_options) {
292             continue;
293         }
294
295         match item {
296             AssocItem::Function(id) => {
297                 let item = &item_tree[id];
298                 let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
299                 items.push((item.name.clone(), def.into()));
300             }
301             AssocItem::Const(id) => {
302                 let item = &item_tree[id];
303                 let name = match item.name.clone() {
304                     Some(name) => name,
305                     None => continue,
306                 };
307                 let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
308                 items.push((name, def.into()));
309             }
310             AssocItem::TypeAlias(id) => {
311                 let item = &item_tree[id];
312                 let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
313                 items.push((item.name.clone(), def.into()));
314             }
315             AssocItem::MacroCall(call) => {
316                 let call = &item_tree[call];
317                 let ast_id_map = db.ast_id_map(file_id);
318                 let root = db.parse_or_expand(file_id).unwrap();
319                 let call = ast_id_map.get(call.ast_id).to_node(&root);
320                 let res = expander.enter_expand(db, call);
321
322                 if let Ok(res) = res {
323                     if let Some((mark, mac)) = res.value {
324                         let src: InFile<ast::MacroItems> = expander.to_source(mac);
325                         let item_tree = db.file_item_tree(src.file_id);
326                         let iter =
327                             item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
328                         items.extend(collect_items(
329                             db,
330                             module,
331                             expander,
332                             iter,
333                             src.file_id,
334                             container,
335                             limit - 1,
336                         ));
337
338                         expander.exit(db, mark);
339                     }
340                 }
341             }
342         }
343     }
344
345     items
346 }