]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! Collects lang items: items marked with `#[lang = "..."]` attribute. |
2 | //! | |
3 | //! This attribute to tell the compiler about semi built-in std library | |
4 | //! features, such as Fn family of traits. | |
5 | use std::sync::Arc; | |
6 | ||
7 | use rustc_hash::FxHashMap; | |
8 | use syntax::SmolStr; | |
9 | ||
10 | use crate::{ | |
11 | db::DefDatabase, AdtId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId, ImplId, | |
12 | ModuleDefId, StaticId, StructId, TraitId, | |
13 | }; | |
14 | ||
15 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | |
16 | pub enum LangItemTarget { | |
17 | EnumId(EnumId), | |
18 | FunctionId(FunctionId), | |
19 | ImplDefId(ImplId), | |
20 | StaticId(StaticId), | |
21 | StructId(StructId), | |
22 | TraitId(TraitId), | |
23 | EnumVariantId(EnumVariantId), | |
24 | } | |
25 | ||
26 | impl LangItemTarget { | |
27 | pub fn as_enum(self) -> Option<EnumId> { | |
28 | match self { | |
29 | LangItemTarget::EnumId(id) => Some(id), | |
30 | _ => None, | |
31 | } | |
32 | } | |
33 | ||
34 | pub fn as_function(self) -> Option<FunctionId> { | |
35 | match self { | |
36 | LangItemTarget::FunctionId(id) => Some(id), | |
37 | _ => None, | |
38 | } | |
39 | } | |
40 | ||
41 | pub fn as_impl_def(self) -> Option<ImplId> { | |
42 | match self { | |
43 | LangItemTarget::ImplDefId(id) => Some(id), | |
44 | _ => None, | |
45 | } | |
46 | } | |
47 | ||
48 | pub fn as_static(self) -> Option<StaticId> { | |
49 | match self { | |
50 | LangItemTarget::StaticId(id) => Some(id), | |
51 | _ => None, | |
52 | } | |
53 | } | |
54 | ||
55 | pub fn as_struct(self) -> Option<StructId> { | |
56 | match self { | |
57 | LangItemTarget::StructId(id) => Some(id), | |
58 | _ => None, | |
59 | } | |
60 | } | |
61 | ||
62 | pub fn as_trait(self) -> Option<TraitId> { | |
63 | match self { | |
64 | LangItemTarget::TraitId(id) => Some(id), | |
65 | _ => None, | |
66 | } | |
67 | } | |
68 | ||
69 | pub fn as_enum_variant(self) -> Option<EnumVariantId> { | |
70 | match self { | |
71 | LangItemTarget::EnumVariantId(id) => Some(id), | |
72 | _ => None, | |
73 | } | |
74 | } | |
75 | } | |
76 | ||
77 | #[derive(Default, Debug, Clone, PartialEq, Eq)] | |
78 | pub struct LangItems { | |
79 | items: FxHashMap<SmolStr, LangItemTarget>, | |
80 | } | |
81 | ||
82 | impl LangItems { | |
83 | pub fn target(&self, item: &str) -> Option<LangItemTarget> { | |
84 | self.items.get(item).copied() | |
85 | } | |
86 | ||
87 | /// Salsa query. This will look for lang items in a specific crate. | |
88 | pub(crate) fn crate_lang_items_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<LangItems> { | |
89 | let _p = profile::span("crate_lang_items_query"); | |
90 | ||
91 | let mut lang_items = LangItems::default(); | |
92 | ||
93 | let crate_def_map = db.crate_def_map(krate); | |
94 | ||
95 | for (_, module_data) in crate_def_map.modules() { | |
96 | for impl_def in module_data.scope.impls() { | |
97 | lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDefId) | |
98 | } | |
99 | ||
100 | for def in module_data.scope.declarations() { | |
101 | match def { | |
102 | ModuleDefId::TraitId(trait_) => { | |
103 | lang_items.collect_lang_item(db, trait_, LangItemTarget::TraitId); | |
104 | db.trait_data(trait_).items.iter().for_each(|&(_, assoc_id)| { | |
105 | if let crate::AssocItemId::FunctionId(f) = assoc_id { | |
106 | lang_items.collect_lang_item(db, f, LangItemTarget::FunctionId); | |
107 | } | |
108 | }); | |
109 | } | |
110 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { | |
111 | lang_items.collect_lang_item(db, e, LangItemTarget::EnumId); | |
112 | db.enum_data(e).variants.iter().for_each(|(local_id, _)| { | |
113 | lang_items.collect_lang_item( | |
114 | db, | |
115 | EnumVariantId { parent: e, local_id }, | |
116 | LangItemTarget::EnumVariantId, | |
117 | ); | |
118 | }); | |
119 | } | |
120 | ModuleDefId::AdtId(AdtId::StructId(s)) => { | |
121 | lang_items.collect_lang_item(db, s, LangItemTarget::StructId); | |
122 | } | |
123 | ModuleDefId::FunctionId(f) => { | |
124 | lang_items.collect_lang_item(db, f, LangItemTarget::FunctionId); | |
125 | } | |
126 | ModuleDefId::StaticId(s) => { | |
127 | lang_items.collect_lang_item(db, s, LangItemTarget::StaticId); | |
128 | } | |
129 | _ => {} | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
134 | Arc::new(lang_items) | |
135 | } | |
136 | ||
137 | /// Salsa query. Look for a lang item, starting from the specified crate and recursively | |
138 | /// traversing its dependencies. | |
139 | pub(crate) fn lang_item_query( | |
140 | db: &dyn DefDatabase, | |
141 | start_crate: CrateId, | |
142 | item: SmolStr, | |
143 | ) -> Option<LangItemTarget> { | |
144 | let _p = profile::span("lang_item_query"); | |
145 | let lang_items = db.crate_lang_items(start_crate); | |
146 | let start_crate_target = lang_items.items.get(&item); | |
147 | if let Some(&target) = start_crate_target { | |
148 | return Some(target); | |
149 | } | |
150 | db.crate_graph()[start_crate] | |
151 | .dependencies | |
152 | .iter() | |
153 | .find_map(|dep| db.lang_item(dep.crate_id, item.clone())) | |
154 | } | |
155 | ||
156 | fn collect_lang_item<T>( | |
157 | &mut self, | |
158 | db: &dyn DefDatabase, | |
159 | item: T, | |
160 | constructor: fn(T) -> LangItemTarget, | |
161 | ) where | |
162 | T: Into<AttrDefId> + Copy, | |
163 | { | |
164 | let _p = profile::span("collect_lang_item"); | |
165 | if let Some(lang_item_name) = lang_attr(db, item) { | |
166 | self.items.entry(lang_item_name).or_insert_with(|| constructor(item)); | |
167 | } | |
168 | } | |
169 | } | |
170 | ||
171 | pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<SmolStr> { | |
172 | let attrs = db.attrs(item.into()); | |
173 | attrs.by_key("lang").string_value().cloned() | |
174 | } |