]> git.lizzy.rs Git - rust.git/blob - crates/hir-def/src/data.rs
Auto merge of #12549 - bitgaoshu:goto_where_trait_m_impl, r=Veykril
[rust.git] / crates / hir-def / src / data.rs
1 //! Contains basic data about various HIR declarations.
2
3 use std::{mem, sync::Arc};
4
5 use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
6 use syntax::ast;
7
8 use crate::{
9     attr::Attrs,
10     body::{Expander, Mark},
11     db::DefDatabase,
12     intern::Interned,
13     item_tree::{self, AssocItem, FnFlags, ItemTreeId, ModItem, Param, TreeId},
14     nameres::{attr_resolution::ResolvedAttr, DefMap},
15     type_ref::{TraitRef, TypeBound, TypeRef},
16     visibility::RawVisibility,
17     AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
18     Intern, ItemContainerId, Lookup, Macro2Id, MacroRulesId, ModuleId, ProcMacroId, StaticId,
19     TraitId, TypeAliasId, TypeAliasLoc,
20 };
21
22 #[derive(Debug, Clone, PartialEq, Eq)]
23 pub struct FunctionData {
24     pub name: Name,
25     pub params: Vec<(Option<Name>, Interned<TypeRef>)>,
26     pub ret_type: Interned<TypeRef>,
27     pub async_ret_type: Option<Interned<TypeRef>>,
28     pub attrs: Attrs,
29     pub visibility: RawVisibility,
30     pub abi: Option<Interned<str>>,
31     pub legacy_const_generics_indices: Box<[u32]>,
32     flags: FnFlags,
33 }
34
35 impl FunctionData {
36     pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
37         let loc = func.lookup(db);
38         let krate = loc.container.module(db).krate;
39         let crate_graph = db.crate_graph();
40         let cfg_options = &crate_graph[krate].cfg_options;
41         let item_tree = loc.id.item_tree(db);
42         let func = &item_tree[loc.id.value];
43         let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
44             db.trait_data(trait_id).visibility.clone()
45         } else {
46             item_tree[func.visibility].clone()
47         };
48
49         let enabled_params = func
50             .params
51             .clone()
52             .filter(|&param| item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options));
53
54         // If last cfg-enabled param is a `...` param, it's a varargs function.
55         let is_varargs = enabled_params
56             .clone()
57             .next_back()
58             .map_or(false, |param| matches!(item_tree[param], Param::Varargs));
59
60         let mut flags = func.flags;
61         if is_varargs {
62             flags |= FnFlags::IS_VARARGS;
63         }
64         if flags.contains(FnFlags::HAS_SELF_PARAM) {
65             // If there's a self param in the syntax, but it is cfg'd out, remove the flag.
66             let is_cfgd_out = match func.params.clone().next() {
67                 Some(param) => {
68                     !item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options)
69                 }
70                 None => {
71                     stdx::never!("fn HAS_SELF_PARAM but no parameters allocated");
72                     true
73                 }
74             };
75             if is_cfgd_out {
76                 cov_mark::hit!(cfgd_out_self_param);
77                 flags.remove(FnFlags::HAS_SELF_PARAM);
78             }
79         }
80
81         let legacy_const_generics_indices = item_tree
82             .attrs(db, krate, ModItem::from(loc.id.value).into())
83             .by_key("rustc_legacy_const_generics")
84             .tt_values()
85             .next()
86             .map(parse_rustc_legacy_const_generics)
87             .unwrap_or_default();
88
89         Arc::new(FunctionData {
90             name: func.name.clone(),
91             params: enabled_params
92                 .clone()
93                 .filter_map(|id| match &item_tree[id] {
94                     Param::Normal(name, ty) => Some((name.clone(), ty.clone())),
95                     Param::Varargs => None,
96                 })
97                 .collect(),
98             ret_type: func.ret_type.clone(),
99             async_ret_type: func.async_ret_type.clone(),
100             attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
101             visibility,
102             abi: func.abi.clone(),
103             legacy_const_generics_indices,
104             flags,
105         })
106     }
107
108     pub fn has_body(&self) -> bool {
109         self.flags.contains(FnFlags::HAS_BODY)
110     }
111
112     /// True if the first param is `self`. This is relevant to decide whether this
113     /// can be called as a method.
114     pub fn has_self_param(&self) -> bool {
115         self.flags.contains(FnFlags::HAS_SELF_PARAM)
116     }
117
118     pub fn has_default_kw(&self) -> bool {
119         self.flags.contains(FnFlags::HAS_DEFAULT_KW)
120     }
121
122     pub fn has_const_kw(&self) -> bool {
123         self.flags.contains(FnFlags::HAS_CONST_KW)
124     }
125
126     pub fn has_async_kw(&self) -> bool {
127         self.flags.contains(FnFlags::HAS_ASYNC_KW)
128     }
129
130     pub fn has_unsafe_kw(&self) -> bool {
131         self.flags.contains(FnFlags::HAS_UNSAFE_KW)
132     }
133
134     pub fn is_varargs(&self) -> bool {
135         self.flags.contains(FnFlags::IS_VARARGS)
136     }
137 }
138
139 fn parse_rustc_legacy_const_generics(tt: &tt::Subtree) -> Box<[u32]> {
140     let mut indices = Vec::new();
141     for args in tt.token_trees.chunks(2) {
142         match &args[0] {
143             tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => match lit.text.parse() {
144                 Ok(index) => indices.push(index),
145                 Err(_) => break,
146             },
147             _ => break,
148         }
149
150         if let Some(comma) = args.get(1) {
151             match comma {
152                 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.char == ',' => {}
153                 _ => break,
154             }
155         }
156     }
157
158     indices.into_boxed_slice()
159 }
160
161 #[derive(Debug, Clone, PartialEq, Eq)]
162 pub struct TypeAliasData {
163     pub name: Name,
164     pub type_ref: Option<Interned<TypeRef>>,
165     pub visibility: RawVisibility,
166     pub is_extern: bool,
167     /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
168     pub bounds: Vec<Interned<TypeBound>>,
169 }
170
171 impl TypeAliasData {
172     pub(crate) fn type_alias_data_query(
173         db: &dyn DefDatabase,
174         typ: TypeAliasId,
175     ) -> Arc<TypeAliasData> {
176         let loc = typ.lookup(db);
177         let item_tree = loc.id.item_tree(db);
178         let typ = &item_tree[loc.id.value];
179         let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
180             db.trait_data(trait_id).visibility.clone()
181         } else {
182             item_tree[typ.visibility].clone()
183         };
184
185         Arc::new(TypeAliasData {
186             name: typ.name.clone(),
187             type_ref: typ.type_ref.clone(),
188             visibility,
189             is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
190             bounds: typ.bounds.to_vec(),
191         })
192     }
193 }
194
195 #[derive(Debug, Clone, PartialEq, Eq)]
196 pub struct TraitData {
197     pub name: Name,
198     pub items: Vec<(Name, AssocItemId)>,
199     pub is_auto: bool,
200     pub is_unsafe: bool,
201     pub visibility: RawVisibility,
202     /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
203     /// method calls to this trait's methods when the receiver is an array and the crate edition is
204     /// 2015 or 2018.
205     pub skip_array_during_method_dispatch: bool,
206     // box it as the vec is usually empty anyways
207     pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
208 }
209
210 impl TraitData {
211     pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
212         let tr_loc = tr.lookup(db);
213         let item_tree = tr_loc.id.item_tree(db);
214         let tr_def = &item_tree[tr_loc.id.value];
215         let _cx = stdx::panic_context::enter(format!(
216             "trait_data_query({:?} -> {:?} -> {:?})",
217             tr, tr_loc, tr_def
218         ));
219         let name = tr_def.name.clone();
220         let is_auto = tr_def.is_auto;
221         let is_unsafe = tr_def.is_unsafe;
222         let module_id = tr_loc.container;
223         let visibility = item_tree[tr_def.visibility].clone();
224         let skip_array_during_method_dispatch = item_tree
225             .attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into())
226             .by_key("rustc_skip_array_during_method_dispatch")
227             .exists();
228
229         let mut collector = AssocItemCollector::new(
230             db,
231             module_id,
232             tr_loc.id.file_id(),
233             ItemContainerId::TraitId(tr),
234         );
235         collector.collect(tr_loc.id.tree_id(), &tr_def.items);
236
237         Arc::new(TraitData {
238             name,
239             attribute_calls: collector.take_attr_calls(),
240             items: collector.items,
241             is_auto,
242             is_unsafe,
243             visibility,
244             skip_array_during_method_dispatch,
245         })
246     }
247
248     pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
249         self.items.iter().filter_map(|(_name, item)| match item {
250             AssocItemId::TypeAliasId(t) => Some(*t),
251             _ => None,
252         })
253     }
254
255     pub fn associated_type_by_name(&self, name: &Name) -> Option<TypeAliasId> {
256         self.items.iter().find_map(|(item_name, item)| match item {
257             AssocItemId::TypeAliasId(t) if item_name == name => Some(*t),
258             _ => None,
259         })
260     }
261
262     pub fn method_by_name(&self, name: &Name) -> Option<FunctionId> {
263         self.items.iter().find_map(|(item_name, item)| match item {
264             AssocItemId::FunctionId(t) if item_name == name => Some(*t),
265             _ => None,
266         })
267     }
268
269     pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
270         self.attribute_calls.iter().flat_map(|it| it.iter()).copied()
271     }
272 }
273
274 #[derive(Debug, Clone, PartialEq, Eq)]
275 pub struct ImplData {
276     pub target_trait: Option<Interned<TraitRef>>,
277     pub self_ty: Interned<TypeRef>,
278     pub items: Vec<AssocItemId>,
279     pub is_negative: bool,
280     // box it as the vec is usually empty anyways
281     pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
282 }
283
284 impl ImplData {
285     pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
286         let _p = profile::span("impl_data_query");
287         let impl_loc = id.lookup(db);
288
289         let item_tree = impl_loc.id.item_tree(db);
290         let impl_def = &item_tree[impl_loc.id.value];
291         let target_trait = impl_def.target_trait.clone();
292         let self_ty = impl_def.self_ty.clone();
293         let is_negative = impl_def.is_negative;
294         let module_id = impl_loc.container;
295
296         let mut collector = AssocItemCollector::new(
297             db,
298             module_id,
299             impl_loc.id.file_id(),
300             ItemContainerId::ImplId(id),
301         );
302         collector.collect(impl_loc.id.tree_id(), &impl_def.items);
303
304         let attribute_calls = collector.take_attr_calls();
305         let items = collector.items.into_iter().map(|(_, item)| item).collect();
306
307         Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls })
308     }
309
310     pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
311         self.attribute_calls.iter().flat_map(|it| it.iter()).copied()
312     }
313 }
314
315 #[derive(Debug, Clone, PartialEq, Eq)]
316 pub struct Macro2Data {
317     pub name: Name,
318     pub visibility: RawVisibility,
319 }
320
321 impl Macro2Data {
322     pub(crate) fn macro2_data_query(db: &dyn DefDatabase, makro: Macro2Id) -> Arc<Macro2Data> {
323         let loc = makro.lookup(db);
324         let item_tree = loc.id.item_tree(db);
325         let makro = &item_tree[loc.id.value];
326
327         Arc::new(Macro2Data {
328             name: makro.name.clone(),
329             visibility: item_tree[makro.visibility].clone(),
330         })
331     }
332 }
333 #[derive(Debug, Clone, PartialEq, Eq)]
334 pub struct MacroRulesData {
335     pub name: Name,
336     pub macro_export: bool,
337 }
338
339 impl MacroRulesData {
340     pub(crate) fn macro_rules_data_query(
341         db: &dyn DefDatabase,
342         makro: MacroRulesId,
343     ) -> Arc<MacroRulesData> {
344         let loc = makro.lookup(db);
345         let item_tree = loc.id.item_tree(db);
346         let makro = &item_tree[loc.id.value];
347
348         let macro_export = item_tree
349             .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
350             .by_key("macro_export")
351             .exists();
352
353         Arc::new(MacroRulesData { name: makro.name.clone(), macro_export })
354     }
355 }
356 #[derive(Debug, Clone, PartialEq, Eq)]
357 pub struct ProcMacroData {
358     pub name: Name,
359     // FIXME: Record deriver helper here?
360 }
361
362 impl ProcMacroData {
363     pub(crate) fn proc_macro_data_query(
364         db: &dyn DefDatabase,
365         makro: ProcMacroId,
366     ) -> Arc<ProcMacroData> {
367         let loc = makro.lookup(db);
368         let item_tree = loc.id.item_tree(db);
369         let makro = &item_tree[loc.id.value];
370
371         let name = if let Some(def) = item_tree
372             .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
373             .parse_proc_macro_decl(&makro.name)
374         {
375             def.name
376         } else {
377             // eeeh...
378             stdx::never!("proc macro declaration is not a proc macro");
379             makro.name.clone()
380         };
381         Arc::new(ProcMacroData { name })
382     }
383 }
384
385 #[derive(Debug, Clone, PartialEq, Eq)]
386 pub struct ConstData {
387     /// `None` for `const _: () = ();`
388     pub name: Option<Name>,
389     pub type_ref: Interned<TypeRef>,
390     pub visibility: RawVisibility,
391 }
392
393 impl ConstData {
394     pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
395         let loc = konst.lookup(db);
396         let item_tree = loc.id.item_tree(db);
397         let konst = &item_tree[loc.id.value];
398         let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
399             db.trait_data(trait_id).visibility.clone()
400         } else {
401             item_tree[konst.visibility].clone()
402         };
403
404         Arc::new(ConstData {
405             name: konst.name.clone(),
406             type_ref: konst.type_ref.clone(),
407             visibility,
408         })
409     }
410 }
411
412 #[derive(Debug, Clone, PartialEq, Eq)]
413 pub struct StaticData {
414     pub name: Name,
415     pub type_ref: Interned<TypeRef>,
416     pub visibility: RawVisibility,
417     pub mutable: bool,
418     pub is_extern: bool,
419 }
420
421 impl StaticData {
422     pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
423         let loc = konst.lookup(db);
424         let item_tree = loc.id.item_tree(db);
425         let statik = &item_tree[loc.id.value];
426
427         Arc::new(StaticData {
428             name: statik.name.clone(),
429             type_ref: statik.type_ref.clone(),
430             visibility: item_tree[statik.visibility].clone(),
431             mutable: statik.mutable,
432             is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
433         })
434     }
435 }
436
437 struct AssocItemCollector<'a> {
438     db: &'a dyn DefDatabase,
439     module_id: ModuleId,
440     def_map: Arc<DefMap>,
441     container: ItemContainerId,
442     expander: Expander,
443
444     items: Vec<(Name, AssocItemId)>,
445     attr_calls: Vec<(AstId<ast::Item>, MacroCallId)>,
446 }
447
448 impl<'a> AssocItemCollector<'a> {
449     fn new(
450         db: &'a dyn DefDatabase,
451         module_id: ModuleId,
452         file_id: HirFileId,
453         container: ItemContainerId,
454     ) -> Self {
455         Self {
456             db,
457             module_id,
458             def_map: module_id.def_map(db),
459             container,
460             expander: Expander::new(db, file_id, module_id),
461             items: Vec::new(),
462             attr_calls: Vec::new(),
463         }
464     }
465
466     fn take_attr_calls(&mut self) -> Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>> {
467         let attribute_calls = mem::take(&mut self.attr_calls);
468         if attribute_calls.is_empty() {
469             None
470         } else {
471             Some(Box::new(attribute_calls))
472         }
473     }
474
475     // FIXME: proc-macro diagnostics
476     fn collect(&mut self, tree_id: TreeId, assoc_items: &[AssocItem]) {
477         let item_tree = tree_id.item_tree(self.db);
478
479         'items: for &item in assoc_items {
480             let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
481             if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
482                 continue;
483             }
484
485             'attrs: for attr in &*attrs {
486                 let ast_id =
487                     AstId::new(self.expander.current_file_id(), item.ast_id(&item_tree).upcast());
488                 let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id };
489
490                 if let Ok(ResolvedAttr::Macro(call_id)) = self.def_map.resolve_attr_macro(
491                     self.db,
492                     self.module_id.local_id,
493                     ast_id_with_path,
494                     attr,
495                 ) {
496                     self.attr_calls.push((ast_id, call_id));
497                     // If proc attribute macro expansion is disabled, skip expanding it here
498                     if !self.db.enable_proc_attr_macros() {
499                         continue 'attrs;
500                     }
501                     let loc = self.db.lookup_intern_macro_call(call_id);
502                     if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
503                         // If there's no expander for the proc macro (e.g. the
504                         // proc macro is ignored, or building the proc macro
505                         // crate failed), skip expansion like we would if it was
506                         // disabled. This is analogous to the handling in
507                         // `DefCollector::collect_macros`.
508                         if exp.is_dummy() {
509                             continue 'attrs;
510                         }
511                     }
512                     match self.expander.enter_expand_id(self.db, call_id) {
513                         ExpandResult { value: Some((mark, mac)), .. } => {
514                             self.collect_macro_items(mark, mac);
515                             continue 'items;
516                         }
517                         ExpandResult { .. } => {}
518                     }
519                 }
520             }
521
522             match item {
523                 AssocItem::Function(id) => {
524                     let item = &item_tree[id];
525                     let def =
526                         FunctionLoc { container: self.container, id: ItemTreeId::new(tree_id, id) }
527                             .intern(self.db);
528                     self.items.push((item.name.clone(), def.into()));
529                 }
530                 AssocItem::Const(id) => {
531                     let item = &item_tree[id];
532                     let name = match item.name.clone() {
533                         Some(name) => name,
534                         None => continue,
535                     };
536                     let def =
537                         ConstLoc { container: self.container, id: ItemTreeId::new(tree_id, id) }
538                             .intern(self.db);
539                     self.items.push((name, def.into()));
540                 }
541                 AssocItem::TypeAlias(id) => {
542                     let item = &item_tree[id];
543                     let def = TypeAliasLoc {
544                         container: self.container,
545                         id: ItemTreeId::new(tree_id, id),
546                     }
547                     .intern(self.db);
548                     self.items.push((item.name.clone(), def.into()));
549                 }
550                 AssocItem::MacroCall(call) => {
551                     let call = &item_tree[call];
552                     let ast_id_map = self.db.ast_id_map(self.expander.current_file_id());
553                     let root = self.db.parse_or_expand(self.expander.current_file_id()).unwrap();
554                     let call = ast_id_map.get(call.ast_id).to_node(&root);
555                     let _cx =
556                         stdx::panic_context::enter(format!("collect_items MacroCall: {}", call));
557                     let res = self.expander.enter_expand(self.db, call);
558
559                     if let Ok(ExpandResult { value: Some((mark, mac)), .. }) = res {
560                         self.collect_macro_items(mark, mac);
561                     }
562                 }
563             }
564         }
565     }
566
567     fn collect_macro_items(&mut self, mark: Mark, mac: ast::MacroItems) {
568         let src: InFile<ast::MacroItems> = self.expander.to_source(mac);
569         let tree_id = item_tree::TreeId::new(src.file_id, None);
570         let item_tree = tree_id.item_tree(self.db);
571         let iter: Vec<_> =
572             item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item).collect();
573
574         self.collect(tree_id, &iter);
575
576         self.expander.exit(self.db, mark);
577     }
578 }