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