1 //! When *constructing* `hir`, we start at some parent syntax node and recursively
2 //! lower the children.
4 //! This modules allows one to go in the opposite direction: start with a syntax
5 //! node for a *child*, and get its hir.
8 use hir_expand::HirFileId;
9 use itertools::Itertools;
10 use syntax::ast::AttrsOwner;
15 item_scope::ItemScope,
17 src::{HasChildSource, HasSource},
18 AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, ModuleDefId,
19 ModuleId, TraitId, VariantId,
22 pub trait ChildBySource {
23 fn child_by_source(&self, db: &dyn DefDatabase, file_id: HirFileId) -> DynMap {
24 let mut res = DynMap::default();
25 self.child_by_source_to(db, &mut res, file_id);
28 fn child_by_source_to(&self, db: &dyn DefDatabase, map: &mut DynMap, file_id: HirFileId);
31 impl ChildBySource for TraitId {
32 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
33 let data = db.trait_data(*self);
34 for (_name, item) in data.items.iter() {
36 AssocItemId::FunctionId(func) => {
37 let loc = func.lookup(db);
38 if loc.id.file_id() == file_id {
39 let src = loc.source(db);
40 res[keys::FUNCTION].insert(src, func)
43 AssocItemId::ConstId(konst) => {
44 let loc = konst.lookup(db);
45 if loc.id.file_id() == file_id {
46 let src = loc.source(db);
47 res[keys::CONST].insert(src, konst)
50 AssocItemId::TypeAliasId(ty) => {
51 let loc = ty.lookup(db);
52 if loc.id.file_id() == file_id {
53 let src = loc.source(db);
54 res[keys::TYPE_ALIAS].insert(src, ty)
62 impl ChildBySource for ImplId {
63 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
64 let data = db.impl_data(*self);
65 for &item in data.items.iter() {
67 AssocItemId::FunctionId(func) => {
68 let loc = func.lookup(db);
69 if loc.id.file_id() == file_id {
70 let src = loc.source(db);
71 res[keys::FUNCTION].insert(src, func)
74 AssocItemId::ConstId(konst) => {
75 let loc = konst.lookup(db);
76 if loc.id.file_id() == file_id {
77 let src = loc.source(db);
78 res[keys::CONST].insert(src, konst)
81 AssocItemId::TypeAliasId(ty) => {
82 let loc = ty.lookup(db);
83 if loc.id.file_id() == file_id {
84 let src = loc.source(db);
85 res[keys::TYPE_ALIAS].insert(src, ty)
93 impl ChildBySource for ModuleId {
94 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
95 let def_map = self.def_map(db);
96 let module_data = &def_map[self.local_id];
97 module_data.scope.child_by_source_to(db, res, file_id);
101 impl ChildBySource for ItemScope {
102 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
103 self.declarations().for_each(|item| add_module_def(db, file_id, res, item));
104 self.unnamed_consts().for_each(|konst| {
105 let src = konst.lookup(db).source(db);
106 res[keys::CONST].insert(src, konst);
108 self.impls().for_each(|imp| add_impl(db, file_id, res, imp));
109 self.attr_macro_invocs().for_each(|(ast_id, call_id)| {
110 let item = ast_id.with_value(ast_id.to_node(db.upcast()));
111 res[keys::ATTR_MACRO].insert(item, call_id);
113 self.derive_macro_invocs().for_each(|(ast_id, calls)| {
114 let item = ast_id.to_node(db.upcast());
115 let grouped = calls.iter().copied().into_group_map();
116 for (attr_id, calls) in grouped {
117 if let Some(attr) = item.attrs().nth(attr_id.ast_index as usize) {
118 res[keys::DERIVE_MACRO].insert(ast_id.with_value(attr), calls.into());
124 db: &dyn DefDatabase,
130 ModuleDefId::FunctionId(func) => {
131 let loc = func.lookup(db);
132 if loc.id.file_id() == file_id {
133 let src = loc.source(db);
134 map[keys::FUNCTION].insert(src, func)
137 ModuleDefId::ConstId(konst) => {
138 let loc = konst.lookup(db);
139 if loc.id.file_id() == file_id {
140 let src = loc.source(db);
141 map[keys::CONST].insert(src, konst)
144 ModuleDefId::StaticId(statik) => {
145 let loc = statik.lookup(db);
146 if loc.id.file_id() == file_id {
147 let src = loc.source(db);
148 map[keys::STATIC].insert(src, statik)
151 ModuleDefId::TypeAliasId(ty) => {
152 let loc = ty.lookup(db);
153 if loc.id.file_id() == file_id {
154 let src = loc.source(db);
155 map[keys::TYPE_ALIAS].insert(src, ty)
158 ModuleDefId::TraitId(trait_) => {
159 let loc = trait_.lookup(db);
160 if loc.id.file_id() == file_id {
161 let src = loc.source(db);
162 map[keys::TRAIT].insert(src, trait_)
165 ModuleDefId::AdtId(adt) => match adt {
166 AdtId::StructId(strukt) => {
167 let loc = strukt.lookup(db);
168 if loc.id.file_id() == file_id {
169 let src = loc.source(db);
170 map[keys::STRUCT].insert(src, strukt)
173 AdtId::UnionId(union_) => {
174 let loc = union_.lookup(db);
175 if loc.id.file_id() == file_id {
176 let src = loc.source(db);
177 map[keys::UNION].insert(src, union_)
180 AdtId::EnumId(enum_) => {
181 let loc = enum_.lookup(db);
182 if loc.id.file_id() == file_id {
183 let src = loc.source(db);
184 map[keys::ENUM].insert(src, enum_)
191 fn add_impl(db: &dyn DefDatabase, file_id: HirFileId, map: &mut DynMap, imp: ImplId) {
192 let loc = imp.lookup(db);
193 if loc.id.file_id() == file_id {
194 let src = loc.source(db);
195 map[keys::IMPL].insert(src, imp)
201 impl ChildBySource for VariantId {
202 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
203 let arena_map = self.child_source(db);
204 let arena_map = arena_map.as_ref();
206 for (local_id, source) in arena_map.value.iter() {
207 let id = FieldId { parent, local_id };
208 match source.clone() {
209 Either::Left(source) => {
210 res[keys::TUPLE_FIELD].insert(arena_map.with_value(source), id)
212 Either::Right(source) => {
213 res[keys::RECORD_FIELD].insert(arena_map.with_value(source), id)
220 impl ChildBySource for EnumId {
221 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
222 let arena_map = self.child_source(db);
223 let arena_map = arena_map.as_ref();
224 for (local_id, source) in arena_map.value.iter() {
225 let id = EnumVariantId { parent: *self, local_id };
226 res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
231 impl ChildBySource for DefWithBodyId {
232 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
233 let body = db.body(*self);
234 for (_, def_map) in body.blocks(db) {
235 // All block expressions are merged into the same map, because they logically all add
236 // inner items to the containing `DefWithBodyId`.
237 def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);