1use either::Either;
4use hir_def::{
5 CallableDefId, Lookup, MacroId, VariantId,
6 nameres::{ModuleOrigin, ModuleSource},
7 src::{HasChildSource, HasSource as _},
8};
9use hir_expand::{EditionedFileId, HirFileId, InFile};
10use hir_ty::db::InternedClosure;
11use syntax::ast;
12use tt::TextRange;
13
14use crate::{
15 Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
16 InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
17 Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, db::HirDatabase,
18};
19
20pub trait HasSource {
21 type Ast;
22 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
30}
31
32impl Module {
35 pub fn definition_source(self, db: &dyn HirDatabase) -> InFile<ModuleSource> {
37 let def_map = self.id.def_map(db);
38 def_map[self.id.local_id].definition_source(db)
39 }
40
41 pub fn definition_source_range(self, db: &dyn HirDatabase) -> InFile<TextRange> {
43 let def_map = self.id.def_map(db);
44 def_map[self.id.local_id].definition_source_range(db)
45 }
46
47 pub fn definition_source_file_id(self, db: &dyn HirDatabase) -> HirFileId {
48 let def_map = self.id.def_map(db);
49 def_map[self.id.local_id].definition_source_file_id()
50 }
51
52 pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool {
53 let def_map = self.id.def_map(db);
54 match def_map[self.id.local_id].origin {
55 ModuleOrigin::File { is_mod_rs, .. } => is_mod_rs,
56 _ => false,
57 }
58 }
59
60 pub fn as_source_file_id(self, db: &dyn HirDatabase) -> Option<EditionedFileId> {
61 let def_map = self.id.def_map(db);
62 match def_map[self.id.local_id].origin {
63 ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition, .. } => {
64 Some(definition)
65 }
66 _ => None,
67 }
68 }
69
70 pub fn is_inline(self, db: &dyn HirDatabase) -> bool {
71 let def_map = self.id.def_map(db);
72 def_map[self.id.local_id].origin.is_inline()
73 }
74
75 pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> {
78 let def_map = self.id.def_map(db);
79 def_map[self.id.local_id].declaration_source(db)
80 }
81
82 pub fn declaration_source_range(self, db: &dyn HirDatabase) -> Option<InFile<TextRange>> {
85 let def_map = self.id.def_map(db);
86 def_map[self.id.local_id].declaration_source_range(db)
87 }
88}
89
90impl HasSource for Field {
91 type Ast = FieldSource;
92 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
93 let var = VariantId::from(self.parent);
94 let src = var.child_source(db);
95 let field_source = src.map(|it| match it[self.id].clone() {
96 Either::Left(it) => FieldSource::Pos(it),
97 Either::Right(it) => FieldSource::Named(it),
98 });
99 Some(field_source)
100 }
101}
102impl HasSource for Adt {
103 type Ast = ast::Adt;
104 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
105 match self {
106 Adt::Struct(s) => Some(s.source(db)?.map(ast::Adt::Struct)),
107 Adt::Union(u) => Some(u.source(db)?.map(ast::Adt::Union)),
108 Adt::Enum(e) => Some(e.source(db)?.map(ast::Adt::Enum)),
109 }
110 }
111}
112impl HasSource for VariantDef {
113 type Ast = ast::VariantDef;
114 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
115 match self {
116 VariantDef::Struct(s) => Some(s.source(db)?.map(ast::VariantDef::Struct)),
117 VariantDef::Union(u) => Some(u.source(db)?.map(ast::VariantDef::Union)),
118 VariantDef::Variant(v) => Some(v.source(db)?.map(ast::VariantDef::Variant)),
119 }
120 }
121}
122impl HasSource for Struct {
123 type Ast = ast::Struct;
124 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
125 Some(self.id.lookup(db).source(db))
126 }
127}
128impl HasSource for Union {
129 type Ast = ast::Union;
130 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
131 Some(self.id.lookup(db).source(db))
132 }
133}
134impl HasSource for Enum {
135 type Ast = ast::Enum;
136 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
137 Some(self.id.lookup(db).source(db))
138 }
139}
140impl HasSource for Variant {
141 type Ast = ast::Variant;
142 fn source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Variant>> {
143 Some(self.id.lookup(db).source(db))
144 }
145}
146impl HasSource for Function {
147 type Ast = ast::Fn;
148 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
149 Some(self.id.lookup(db).source(db))
150 }
151}
152impl HasSource for Const {
153 type Ast = ast::Const;
154 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
155 Some(self.id.lookup(db).source(db))
156 }
157}
158impl HasSource for Static {
159 type Ast = ast::Static;
160 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
161 Some(self.id.lookup(db).source(db))
162 }
163}
164impl HasSource for Trait {
165 type Ast = ast::Trait;
166 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
167 Some(self.id.lookup(db).source(db))
168 }
169}
170impl HasSource for TypeAlias {
171 type Ast = ast::TypeAlias;
172 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
173 Some(self.id.lookup(db).source(db))
174 }
175}
176impl HasSource for Macro {
177 type Ast = Either<ast::Macro, ast::Fn>;
178 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
179 match self.id {
180 MacroId::Macro2Id(it) => {
181 Some(it.lookup(db).source(db).map(ast::Macro::MacroDef).map(Either::Left))
182 }
183 MacroId::MacroRulesId(it) => {
184 Some(it.lookup(db).source(db).map(ast::Macro::MacroRules).map(Either::Left))
185 }
186 MacroId::ProcMacroId(it) => Some(it.lookup(db).source(db).map(Either::Right)),
187 }
188 }
189}
190impl HasSource for Impl {
191 type Ast = ast::Impl;
192 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
193 Some(self.id.lookup(db).source(db))
194 }
195}
196
197impl HasSource for TypeOrConstParam {
198 type Ast = Either<ast::TypeOrConstParam, ast::Trait>;
199 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
200 let child_source = self.id.parent.child_source(db);
201 child_source.map(|it| it.get(self.id.local_id).cloned()).transpose()
202 }
203}
204
205impl HasSource for LifetimeParam {
206 type Ast = ast::LifetimeParam;
207 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
208 let child_source = self.id.parent.child_source(db);
209 child_source.map(|it| it.get(self.id.local_id).cloned()).transpose()
210 }
211}
212
213impl HasSource for LocalSource {
214 type Ast = Either<ast::IdentPat, ast::SelfParam>;
215
216 fn source(self, _: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
217 Some(self.source)
218 }
219}
220
221impl HasSource for Param<'_> {
222 type Ast = Either<ast::SelfParam, ast::Param>;
223
224 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
225 match self.func {
226 Callee::Def(CallableDefId::FunctionId(func)) => {
227 let InFile { file_id, value } = Function { id: func }.source(db)?;
228 let params = value.param_list()?;
229 if let Some(self_param) = params.self_param() {
230 if let Some(idx) = self.idx.checked_sub(1) {
231 params.params().nth(idx).map(Either::Right)
232 } else {
233 Some(Either::Left(self_param))
234 }
235 } else {
236 params.params().nth(self.idx).map(Either::Right)
237 }
238 .map(|value| InFile { file_id, value })
239 }
240 Callee::Closure(closure, _) => {
241 let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
242 let (_, source_map) = db.body_with_source_map(owner);
243 let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
244 let root = db.parse_or_expand(file_id);
245 match value.to_node(&root) {
246 Either::Left(ast::Expr::ClosureExpr(it)) => it
247 .param_list()?
248 .params()
249 .nth(self.idx)
250 .map(Either::Right)
251 .map(|value| InFile { file_id: ast.file_id, value }),
252 _ => None,
253 }
254 }
255 _ => None,
256 }
257 }
258}
259
260impl HasSource for SelfParam {
261 type Ast = ast::SelfParam;
262
263 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
264 let InFile { file_id, value } = Function::from(self.func).source(db)?;
265 value
266 .param_list()
267 .and_then(|params| params.self_param())
268 .map(|value| InFile { file_id, value })
269 }
270}
271
272impl HasSource for Label {
273 type Ast = ast::Label;
274
275 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
276 let (_body, source_map) = db.body_with_source_map(self.parent);
277 let src = source_map.label_syntax(self.label_id);
278 let root = src.file_syntax(db);
279 Some(src.map(|ast| ast.to_node(&root)))
280 }
281}
282
283impl HasSource for ExternCrateDecl {
284 type Ast = ast::ExternCrate;
285
286 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
287 Some(self.id.lookup(db).source(db))
288 }
289}
290
291impl HasSource for InlineAsmOperand {
292 type Ast = ast::AsmOperandNamed;
293 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
294 let source_map = db.body_with_source_map(self.owner).1;
295 if let Ok(src) = source_map.expr_syntax(self.expr) {
296 let root = src.file_syntax(db);
297 return src
298 .map(|ast| match ast.to_node(&root) {
299 Either::Left(ast::Expr::AsmExpr(asm)) => asm
300 .asm_pieces()
301 .filter_map(|it| match it {
302 ast::AsmPiece::AsmOperandNamed(it) => Some(it),
303 _ => None,
304 })
305 .nth(self.index),
306 _ => None,
307 })
308 .transpose();
309 }
310 None
311 }
312}