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 {
fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<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 
has_body(&self) -> bool75     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.
has_self_param(&self) -> bool81     pub fn has_self_param(&self) -> bool {
82         self.flags.bits & FnFlags::HAS_SELF_PARAM != 0
83     }
84 
is_default(&self) -> bool85     pub fn is_default(&self) -> bool {
86         self.flags.bits & FnFlags::IS_DEFAULT != 0
87     }
88 
is_const(&self) -> bool89     pub fn is_const(&self) -> bool {
90         self.flags.bits & FnFlags::IS_CONST != 0
91     }
92 
is_async(&self) -> bool93     pub fn is_async(&self) -> bool {
94         self.flags.bits & FnFlags::IS_ASYNC != 0
95     }
96 
is_unsafe(&self) -> bool97     pub fn is_unsafe(&self) -> bool {
98         self.flags.bits & FnFlags::IS_UNSAFE != 0
99     }
100 
is_in_extern_block(&self) -> bool101     pub fn is_in_extern_block(&self) -> bool {
102         self.flags.bits & FnFlags::IS_IN_EXTERN_BLOCK != 0
103     }
104 
is_varargs(&self) -> bool105     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 {
type_alias_data_query( db: &dyn DefDatabase, typ: TypeAliasId, ) -> Arc<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 {
trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<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 _cx = stdx::panic_context::enter(format!(
158             "trait_data_query({:?} -> {:?} -> {:?})",
159             tr, tr_loc, tr_def
160         ));
161         let name = tr_def.name.clone();
162         let is_auto = tr_def.is_auto;
163         let is_unsafe = tr_def.is_unsafe;
164         let module_id = tr_loc.container;
165         let container = AssocContainerId::TraitId(tr);
166         let visibility = item_tree[tr_def.visibility].clone();
167         let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id);
168         let skip_array_during_method_dispatch = item_tree
169             .attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into())
170             .by_key("rustc_skip_array_during_method_dispatch")
171             .exists();
172 
173         let items = collect_items(
174             db,
175             module_id,
176             &mut expander,
177             tr_def.items.iter().copied(),
178             tr_loc.id.tree_id(),
179             container,
180             100,
181         );
182 
183         Arc::new(TraitData {
184             name,
185             items,
186             is_auto,
187             is_unsafe,
188             visibility,
189             skip_array_during_method_dispatch,
190         })
191     }
192 
associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_193     pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
194         self.items.iter().filter_map(|(_name, item)| match item {
195             AssocItemId::TypeAliasId(t) => Some(*t),
196             _ => None,
197         })
198     }
199 
associated_type_by_name(&self, name: &Name) -> Option<TypeAliasId>200     pub fn associated_type_by_name(&self, name: &Name) -> Option<TypeAliasId> {
201         self.items.iter().find_map(|(item_name, item)| match item {
202             AssocItemId::TypeAliasId(t) if item_name == name => Some(*t),
203             _ => None,
204         })
205     }
206 
method_by_name(&self, name: &Name) -> Option<FunctionId>207     pub fn method_by_name(&self, name: &Name) -> Option<FunctionId> {
208         self.items.iter().find_map(|(item_name, item)| match item {
209             AssocItemId::FunctionId(t) if item_name == name => Some(*t),
210             _ => None,
211         })
212     }
213 }
214 
215 #[derive(Debug, Clone, PartialEq, Eq)]
216 pub struct ImplData {
217     pub target_trait: Option<Interned<TraitRef>>,
218     pub self_ty: Interned<TypeRef>,
219     pub items: Vec<AssocItemId>,
220     pub is_negative: bool,
221 }
222 
223 impl ImplData {
impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData>224     pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
225         let _p = profile::span("impl_data_query");
226         let impl_loc = id.lookup(db);
227 
228         let item_tree = impl_loc.id.item_tree(db);
229         let impl_def = &item_tree[impl_loc.id.value];
230         let target_trait = impl_def.target_trait.clone();
231         let self_ty = impl_def.self_ty.clone();
232         let is_negative = impl_def.is_negative;
233         let module_id = impl_loc.container;
234         let container = AssocContainerId::ImplId(id);
235         let mut expander = Expander::new(db, impl_loc.id.file_id(), module_id);
236 
237         let items = collect_items(
238             db,
239             module_id,
240             &mut expander,
241             impl_def.items.iter().copied(),
242             impl_loc.id.tree_id(),
243             container,
244             100,
245         );
246         let items = items.into_iter().map(|(_, item)| item).collect();
247 
248         Arc::new(ImplData { target_trait, self_ty, items, is_negative })
249     }
250 }
251 
252 #[derive(Debug, Clone, PartialEq, Eq)]
253 pub struct ConstData {
254     /// const _: () = ();
255     pub name: Option<Name>,
256     pub type_ref: Interned<TypeRef>,
257     pub visibility: RawVisibility,
258 }
259 
260 impl ConstData {
const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData>261     pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
262         let loc = konst.lookup(db);
263         let item_tree = loc.id.item_tree(db);
264         let konst = &item_tree[loc.id.value];
265 
266         Arc::new(ConstData {
267             name: konst.name.clone(),
268             type_ref: konst.type_ref.clone(),
269             visibility: item_tree[konst.visibility].clone(),
270         })
271     }
272 }
273 
274 #[derive(Debug, Clone, PartialEq, Eq)]
275 pub struct StaticData {
276     pub name: Name,
277     pub type_ref: Interned<TypeRef>,
278     pub visibility: RawVisibility,
279     pub mutable: bool,
280     pub is_extern: bool,
281 }
282 
283 impl StaticData {
static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData>284     pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
285         let node = konst.lookup(db);
286         let item_tree = node.id.item_tree(db);
287         let statik = &item_tree[node.id.value];
288 
289         Arc::new(StaticData {
290             name: statik.name.clone(),
291             type_ref: statik.type_ref.clone(),
292             visibility: item_tree[statik.visibility].clone(),
293             mutable: statik.mutable,
294             is_extern: statik.is_extern,
295         })
296     }
297 }
298 
collect_items( db: &dyn DefDatabase, module: ModuleId, expander: &mut Expander, assoc_items: impl Iterator<Item = AssocItem>, tree_id: item_tree::TreeId, container: AssocContainerId, limit: usize, ) -> Vec<(Name, AssocItemId)>299 fn collect_items(
300     db: &dyn DefDatabase,
301     module: ModuleId,
302     expander: &mut Expander,
303     assoc_items: impl Iterator<Item = AssocItem>,
304     tree_id: item_tree::TreeId,
305     container: AssocContainerId,
306     limit: usize,
307 ) -> Vec<(Name, AssocItemId)> {
308     if limit == 0 {
309         return Vec::new();
310     }
311 
312     let item_tree = tree_id.item_tree(db);
313     let crate_graph = db.crate_graph();
314     let cfg_options = &crate_graph[module.krate].cfg_options;
315 
316     let mut items = Vec::new();
317     for item in assoc_items {
318         let attrs = item_tree.attrs(db, module.krate, ModItem::from(item).into());
319         if !attrs.is_cfg_enabled(cfg_options) {
320             continue;
321         }
322 
323         match item {
324             AssocItem::Function(id) => {
325                 let item = &item_tree[id];
326                 let def = FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(db);
327                 items.push((item.name.clone(), def.into()));
328             }
329             AssocItem::Const(id) => {
330                 let item = &item_tree[id];
331                 let name = match item.name.clone() {
332                     Some(name) => name,
333                     None => continue,
334                 };
335                 let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(db);
336                 items.push((name, def.into()));
337             }
338             AssocItem::TypeAlias(id) => {
339                 let item = &item_tree[id];
340                 let def = TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(db);
341                 items.push((item.name.clone(), def.into()));
342             }
343             AssocItem::MacroCall(call) => {
344                 let call = &item_tree[call];
345                 let ast_id_map = db.ast_id_map(tree_id.file_id());
346                 let root = db.parse_or_expand(tree_id.file_id()).unwrap();
347                 let call = ast_id_map.get(call.ast_id).to_node(&root);
348                 let _cx = stdx::panic_context::enter(format!("collect_items MacroCall: {}", call));
349                 let res = expander.enter_expand(db, call);
350 
351                 if let Ok(res) = res {
352                     if let Some((mark, mac)) = res.value {
353                         let src: InFile<ast::MacroItems> = expander.to_source(mac);
354                         let tree_id = item_tree::TreeId::new(src.file_id, None);
355                         let item_tree = tree_id.item_tree(db);
356                         let iter =
357                             item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
358                         items.extend(collect_items(
359                             db,
360                             module,
361                             expander,
362                             iter,
363                             tree_id,
364                             container,
365                             limit - 1,
366                         ));
367 
368                         expander.exit(db, mark);
369                     }
370                 }
371             }
372         }
373     }
374 
375     items
376 }
377