1use base_db::SourceDatabase;
4use either::Either;
5use hir_expand::{AstId, InFile};
6use la_arena::{Arena, ArenaMap, Idx};
7use syntax::{AstNode, AstPtr, ast};
8
9use crate::{
10 AstIdLoc, GenericDefId, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
11 UseId, VariantId, attrs::AttrFlags, hir::generics::GenericParams,
12};
13
14pub trait HasSource {
15 type Value: AstNode;
16 fn source(&self, db: &dyn SourceDatabase) -> InFile<Self::Value> {
17 let InFile { file_id, value } = self.ast_ptr(db);
18 InFile::new(file_id, value.to_node(&file_id.parse_or_expand(db)))
19 }
20 fn ast_ptr(&self, db: &dyn SourceDatabase) -> InFile<AstPtr<Self::Value>>;
21}
22
23impl<T> HasSource for T
24where
25 T: AstIdLoc,
26{
27 type Value = T::Ast;
28 fn ast_ptr(&self, db: &dyn SourceDatabase) -> InFile<AstPtr<Self::Value>> {
29 let id = self.ast_id();
30 let ast_id_map = id.file_id.ast_id_map(db);
31 InFile::new(id.file_id, ast_id_map.get(id.value))
32 }
33}
34
35pub trait HasChildSource<ChildId> {
36 type Value;
37 fn child_source(&self, db: &dyn SourceDatabase) -> InFile<ArenaMap<ChildId, Self::Value>>;
38}
39
40pub fn use_tree_to_ast(
42 db: &dyn SourceDatabase,
43 use_ast_id: AstId<ast::Use>,
44 index: Idx<ast::UseTree>,
45) -> ast::UseTree {
46 use_tree_source_map(db, use_ast_id)[index].clone()
47}
48
49fn use_tree_source_map(
51 db: &dyn SourceDatabase,
52 use_ast_id: AstId<ast::Use>,
53) -> Arena<ast::UseTree> {
54 let ast = use_ast_id.to_node(db);
57 let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
58 let mut span_map = None;
59 crate::item_tree::lower_use_tree(db, ast_use_tree, &mut |range| {
60 span_map.get_or_insert_with(|| use_ast_id.file_id.span_map(db)).span_for_range(range).ctx
61 })
62 .expect("failed to lower use tree")
63 .1
64}
65
66impl HasChildSource<la_arena::Idx<ast::UseTree>> for UseId {
67 type Value = ast::UseTree;
68 fn child_source(
69 &self,
70 db: &dyn SourceDatabase,
71 ) -> InFile<ArenaMap<la_arena::Idx<ast::UseTree>, Self::Value>> {
72 let loc = self.lookup(db);
73 InFile::new(loc.id.file_id, use_tree_source_map(db, loc.id).into_iter().collect())
74 }
75}
76
77impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
78 type Value = Either<ast::TypeOrConstParam, ast::Trait>;
79 fn child_source(
80 &self,
81 db: &dyn SourceDatabase,
82 ) -> InFile<ArenaMap<LocalTypeOrConstParamId, Self::Value>> {
83 let generic_params = GenericParams::of(db, *self);
84 let mut idx_iter = generic_params.iter_type_or_consts().map(|(idx, _)| idx);
85
86 let (file_id, generic_params_list) = self.file_id_and_params_of(db);
87
88 let mut params = ArenaMap::default();
89
90 match *self {
93 GenericDefId::TraitId(id) => {
94 let trait_ref = id.lookup(db).source(db).value;
95 let idx = idx_iter.next().unwrap();
96 params.insert(idx, Either::Right(trait_ref));
97 }
98 _ => {}
99 }
100
101 if let Some(generic_params_list) = generic_params_list {
102 for (idx, ast_param) in idx_iter.zip(generic_params_list.type_or_const_params()) {
103 params.insert(idx, Either::Left(ast_param));
104 }
105 }
106
107 InFile::new(file_id, params)
108 }
109}
110
111impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
112 type Value = ast::LifetimeParam;
113 fn child_source(
114 &self,
115 db: &dyn SourceDatabase,
116 ) -> InFile<ArenaMap<LocalLifetimeParamId, Self::Value>> {
117 let generic_params = GenericParams::of(db, *self);
118 let idx_iter = generic_params.iter_lt().map(|(idx, _)| idx);
119
120 let (file_id, generic_params_list) = self.file_id_and_params_of(db);
121
122 let mut params = ArenaMap::default();
123
124 if let Some(generic_params_list) = generic_params_list {
125 for (idx, ast_param) in idx_iter.zip(generic_params_list.lifetime_params()) {
126 params.insert(idx, ast_param);
127 }
128 }
129
130 InFile::new(file_id, params)
131 }
132}
133
134impl HasChildSource<LocalFieldId> for VariantId {
135 type Value = Either<ast::TupleField, ast::RecordField>;
136
137 fn child_source(&self, db: &dyn SourceDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
138 let (src, container) = match *self {
139 VariantId::EnumVariantId(it) => {
140 let lookup = it.lookup(db);
141 (lookup.source(db).map(|it| it.kind()), lookup.parent.lookup(db).container)
142 }
143 VariantId::StructId(it) => {
144 let lookup = it.lookup(db);
145 (lookup.source(db).map(|it| it.kind()), lookup.container)
146 }
147 VariantId::UnionId(it) => {
148 let lookup = it.lookup(db);
149 (lookup.source(db).map(|it| it.kind()), lookup.container)
150 }
151 };
152 let mut map = ArenaMap::new();
153 match &src.value {
154 ast::StructKind::Tuple(fl) => {
155 let cfg_options = container.krate(db).cfg_options(db);
156 let mut idx = 0;
157 for fd in fl.fields() {
158 let enabled = AttrFlags::is_cfg_enabled_for(&fd, cfg_options).is_ok();
159 if !enabled {
160 continue;
161 }
162 map.insert(
163 LocalFieldId::from_raw(la_arena::RawIdx::from(idx)),
164 Either::Left(fd.clone()),
165 );
166 idx += 1;
167 }
168 }
169 ast::StructKind::Record(fl) => {
170 let cfg_options = container.krate(db).cfg_options(db);
171 let mut idx = 0;
172 for fd in fl.fields() {
173 let enabled = AttrFlags::is_cfg_enabled_for(&fd, cfg_options).is_ok();
174 if !enabled {
175 continue;
176 }
177 map.insert(
178 LocalFieldId::from_raw(la_arena::RawIdx::from(idx)),
179 Either::Right(fd.clone()),
180 );
181 idx += 1;
182 }
183 }
184 ast::StructKind::Unit => (),
185 }
186 InFile::new(src.file_id, map)
187 }
188}