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::HasAttrs;
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.macros().for_each(|(_, makro)| {
105 let ast_id = makro.ast_id();
106 if ast_id.either(|it| it.file_id, |it| it.file_id) == file_id {
107 let src = match ast_id {
108 Either::Left(ast_id) => ast_id.with_value(ast_id.to_node(db.upcast())),
109 // FIXME: Do we need to add proc-macros into a PROCMACRO dynmap here?
110 Either::Right(_fn) => return,
112 res[keys::MACRO].insert(src, makro);
115 self.unnamed_consts().for_each(|konst| {
116 let src = konst.lookup(db).source(db);
117 res[keys::CONST].insert(src, konst);
119 self.impls().for_each(|imp| add_impl(db, file_id, res, imp));
120 self.attr_macro_invocs().for_each(|(ast_id, call_id)| {
121 let item = ast_id.with_value(ast_id.to_node(db.upcast()));
122 res[keys::ATTR_MACRO].insert(item, call_id);
124 self.derive_macro_invocs().for_each(|(ast_id, calls)| {
125 let item = ast_id.to_node(db.upcast());
126 let grouped = calls.iter().copied().into_group_map();
127 for (attr_id, calls) in grouped {
128 if let Some(attr) = item.attrs().nth(attr_id.ast_index as usize) {
129 res[keys::DERIVE_MACRO].insert(ast_id.with_value(attr), calls.into());
135 db: &dyn DefDatabase,
141 ModuleDefId::FunctionId(func) => {
142 let loc = func.lookup(db);
143 if loc.id.file_id() == file_id {
144 let src = loc.source(db);
145 map[keys::FUNCTION].insert(src, func)
148 ModuleDefId::ConstId(konst) => {
149 let loc = konst.lookup(db);
150 if loc.id.file_id() == file_id {
151 let src = loc.source(db);
152 map[keys::CONST].insert(src, konst)
155 ModuleDefId::StaticId(statik) => {
156 let loc = statik.lookup(db);
157 if loc.id.file_id() == file_id {
158 let src = loc.source(db);
159 map[keys::STATIC].insert(src, statik)
162 ModuleDefId::TypeAliasId(ty) => {
163 let loc = ty.lookup(db);
164 if loc.id.file_id() == file_id {
165 let src = loc.source(db);
166 map[keys::TYPE_ALIAS].insert(src, ty)
169 ModuleDefId::TraitId(trait_) => {
170 let loc = trait_.lookup(db);
171 if loc.id.file_id() == file_id {
172 let src = loc.source(db);
173 map[keys::TRAIT].insert(src, trait_)
176 ModuleDefId::AdtId(adt) => match adt {
177 AdtId::StructId(strukt) => {
178 let loc = strukt.lookup(db);
179 if loc.id.file_id() == file_id {
180 let src = loc.source(db);
181 map[keys::STRUCT].insert(src, strukt)
184 AdtId::UnionId(union_) => {
185 let loc = union_.lookup(db);
186 if loc.id.file_id() == file_id {
187 let src = loc.source(db);
188 map[keys::UNION].insert(src, union_)
191 AdtId::EnumId(enum_) => {
192 let loc = enum_.lookup(db);
193 if loc.id.file_id() == file_id {
194 let src = loc.source(db);
195 map[keys::ENUM].insert(src, enum_)
202 fn add_impl(db: &dyn DefDatabase, file_id: HirFileId, map: &mut DynMap, imp: ImplId) {
203 let loc = imp.lookup(db);
204 if loc.id.file_id() == file_id {
205 let src = loc.source(db);
206 map[keys::IMPL].insert(src, imp)
212 impl ChildBySource for VariantId {
213 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
214 let arena_map = self.child_source(db);
215 let arena_map = arena_map.as_ref();
217 for (local_id, source) in arena_map.value.iter() {
218 let id = FieldId { parent, local_id };
219 match source.clone() {
220 Either::Left(source) => {
221 res[keys::TUPLE_FIELD].insert(arena_map.with_value(source), id)
223 Either::Right(source) => {
224 res[keys::RECORD_FIELD].insert(arena_map.with_value(source), id)
231 impl ChildBySource for EnumId {
232 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, _: HirFileId) {
233 let arena_map = self.child_source(db);
234 let arena_map = arena_map.as_ref();
235 for (local_id, source) in arena_map.value.iter() {
236 let id = EnumVariantId { parent: *self, local_id };
237 res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
242 impl ChildBySource for DefWithBodyId {
243 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
244 let body = db.body(*self);
245 for (_, def_map) in body.blocks(db) {
246 // All block expressions are merged into the same map, because they logically all add
247 // inner items to the containing `DefWithBodyId`.
248 def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);