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