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