1use std::borrow::Borrow;
3
4use either::Either;
5use span::{AstIdNode, ErasedFileAstId, FileAstId, FileId, SyntaxContext};
6use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize};
7
8use crate::{
9 EditionedFileId, HirFileId, MacroCallId, MacroKind,
10 db::{self, ExpandDatabase},
11 map_node_range_up, map_node_range_up_rooted, span_for_offset,
12};
13
14#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
22pub struct InFileWrapper<FileKind, T> {
23 pub file_id: FileKind,
24 pub value: T,
25}
26pub type InFile<T> = InFileWrapper<HirFileId, T>;
27pub type InMacroFile<T> = InFileWrapper<MacroCallId, T>;
28pub type InRealFile<T> = InFileWrapper<EditionedFileId, T>;
29
30#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
31pub struct FilePositionWrapper<FileKind> {
32 pub file_id: FileKind,
33 pub offset: TextSize,
34}
35pub type HirFilePosition = FilePositionWrapper<HirFileId>;
36pub type MacroFilePosition = FilePositionWrapper<MacroCallId>;
37pub type FilePosition = FilePositionWrapper<EditionedFileId>;
38
39impl FilePosition {
40 #[inline]
41 pub fn into_file_id(self, db: &dyn ExpandDatabase) -> FilePositionWrapper<FileId> {
42 FilePositionWrapper { file_id: self.file_id.file_id(db), offset: self.offset }
43 }
44}
45
46impl From<FileRange> for HirFileRange {
47 fn from(value: FileRange) -> Self {
48 HirFileRange { file_id: value.file_id.into(), range: value.range }
49 }
50}
51
52impl From<FilePosition> for HirFilePosition {
53 fn from(value: FilePosition) -> Self {
54 HirFilePosition { file_id: value.file_id.into(), offset: value.offset }
55 }
56}
57
58impl HirFileRange {
59 pub fn file_range(self) -> Option<FileRange> {
60 Some(FileRange { file_id: self.file_id.file_id()?, range: self.range })
61 }
62}
63
64#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
65pub struct FileRangeWrapper<FileKind> {
66 pub file_id: FileKind,
67 pub range: TextRange,
68}
69pub type HirFileRange = FileRangeWrapper<HirFileId>;
70pub type MacroFileRange = FileRangeWrapper<MacroCallId>;
71pub type FileRange = FileRangeWrapper<EditionedFileId>;
72
73impl FileRange {
74 #[inline]
75 pub fn into_file_id(self, db: &dyn ExpandDatabase) -> FileRangeWrapper<FileId> {
76 FileRangeWrapper { file_id: self.file_id.file_id(db), range: self.range }
77 }
78
79 #[inline]
80 pub fn file_text(self, db: &dyn ExpandDatabase) -> &triomphe::Arc<str> {
81 db.file_text(self.file_id.file_id(db)).text(db)
82 }
83
84 #[inline]
85 pub fn text(self, db: &dyn ExpandDatabase) -> &str {
86 &self.file_text(db)[self.range]
87 }
88}
89
90pub type AstId<N> = crate::InFile<FileAstId<N>>;
94
95impl<N: AstNode> AstId<N> {
96 pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
97 self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
98 }
99 pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange {
100 self.to_ptr(db).text_range()
101 }
102 pub fn to_in_file_node(&self, db: &dyn ExpandDatabase) -> crate::InFile<N> {
103 crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)))
104 }
105 pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> AstPtr<N> {
106 db.ast_id_map(self.file_id).get(self.value)
107 }
108 pub fn erase(&self) -> ErasedAstId {
109 crate::InFile::new(self.file_id, self.value.erase())
110 }
111 #[inline]
112 pub fn upcast<M: AstIdNode>(self) -> AstId<M>
113 where
114 N: Into<M>,
115 {
116 self.map(|it| it.upcast())
117 }
118}
119
120pub type ErasedAstId = crate::InFile<ErasedFileAstId>;
121
122impl ErasedAstId {
123 pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange {
124 self.to_ptr(db).text_range()
125 }
126 pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> SyntaxNodePtr {
127 db.ast_id_map(self.file_id).get_erased(self.value)
128 }
129}
130
131impl<FileKind, T> InFileWrapper<FileKind, T> {
132 pub fn new(file_id: FileKind, value: T) -> Self {
133 Self { file_id, value }
134 }
135
136 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFileWrapper<FileKind, U> {
137 InFileWrapper::new(self.file_id, f(self.value))
138 }
139}
140
141impl<FileKind: Copy, T> InFileWrapper<FileKind, T> {
142 pub fn with_value<U>(&self, value: U) -> InFileWrapper<FileKind, U> {
143 InFileWrapper::new(self.file_id, value)
144 }
145
146 pub fn as_ref(&self) -> InFileWrapper<FileKind, &T> {
147 self.with_value(&self.value)
148 }
149
150 pub fn borrow<U>(&self) -> InFileWrapper<FileKind, &U>
151 where
152 T: Borrow<U>,
153 {
154 self.with_value(self.value.borrow())
155 }
156}
157
158impl<FileKind: Copy, T: Clone> InFileWrapper<FileKind, &T> {
159 pub fn cloned(&self) -> InFileWrapper<FileKind, T> {
160 self.with_value(self.value.clone())
161 }
162}
163
164impl<T> From<InMacroFile<T>> for InFile<T> {
165 fn from(InMacroFile { file_id, value }: InMacroFile<T>) -> Self {
166 InFile { file_id: file_id.into(), value }
167 }
168}
169
170impl<T> From<InRealFile<T>> for InFile<T> {
171 fn from(InRealFile { file_id, value }: InRealFile<T>) -> Self {
172 InFile { file_id: file_id.into(), value }
173 }
174}
175
176impl<FileKind, T> InFileWrapper<FileKind, Option<T>> {
179 pub fn transpose(self) -> Option<InFileWrapper<FileKind, T>> {
180 Some(InFileWrapper::new(self.file_id, self.value?))
181 }
182}
183
184impl<FileKind, L, R> InFileWrapper<FileKind, Either<L, R>> {
185 pub fn transpose(self) -> Either<InFileWrapper<FileKind, L>, InFileWrapper<FileKind, R>> {
186 match self.value {
187 Either::Left(l) => Either::Left(InFileWrapper::new(self.file_id, l)),
188 Either::Right(r) => Either::Right(InFileWrapper::new(self.file_id, r)),
189 }
190 }
191}
192
193trait FileIdToSyntax: Copy {
196 fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode;
197}
198
199impl FileIdToSyntax for EditionedFileId {
200 fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
201 db.parse(self).syntax_node()
202 }
203}
204impl FileIdToSyntax for MacroCallId {
205 fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
206 db.parse_macro_expansion(self).value.0.syntax_node()
207 }
208}
209impl FileIdToSyntax for HirFileId {
210 fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
211 db.parse_or_expand(self)
212 }
213}
214
215#[allow(private_bounds)]
216impl<FileId: FileIdToSyntax, T> InFileWrapper<FileId, T> {
217 pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
218 FileIdToSyntax::file_syntax(self.file_id, db)
219 }
220}
221
222#[allow(private_bounds)]
223impl<FileId: FileIdToSyntax, N: AstNode> InFileWrapper<FileId, AstPtr<N>> {
224 pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
225 self.value.to_node(&self.file_syntax(db))
226 }
227}
228
229impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, N> {
230 pub fn syntax(&self) -> InFileWrapper<FileId, &SyntaxNode> {
231 self.with_value(self.value.syntax())
232 }
233 pub fn node_file_range(&self) -> FileRangeWrapper<FileId> {
234 FileRangeWrapper { file_id: self.file_id, range: self.value.syntax().text_range() }
235 }
236}
237
238impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, &N> {
239 pub fn syntax_ref(&self) -> InFileWrapper<FileId, &SyntaxNode> {
241 self.with_value(self.value.syntax())
242 }
243}
244
245impl<FileId: Copy, SN: Borrow<SyntaxNode>> InFileWrapper<FileId, SN> {
247 pub fn file_range(&self) -> FileRangeWrapper<FileId> {
248 FileRangeWrapper { file_id: self.file_id, range: self.value.borrow().text_range() }
249 }
250}
251
252impl<SN: Borrow<SyntaxNode>> InFile<SN> {
253 pub fn parent_ancestors_with_macros(
254 self,
255 db: &dyn db::ExpandDatabase,
256 ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
257 let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
258 Some(parent) => Some(node.with_value(parent)),
259 None => db
260 .lookup_intern_macro_call(node.file_id.macro_file()?)
261 .to_node_item(db)
262 .syntax()
263 .cloned()
264 .map(|node| node.parent())
265 .transpose(),
266 };
267 std::iter::successors(succ(&self.borrow().cloned()), succ)
268 }
269
270 pub fn ancestors_with_macros(
271 self,
272 db: &dyn db::ExpandDatabase,
273 ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
274 let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
275 Some(parent) => Some(node.with_value(parent)),
276 None => db
277 .lookup_intern_macro_call(node.file_id.macro_file()?)
278 .to_node_item(db)
279 .syntax()
280 .cloned()
281 .map(|node| node.parent())
282 .transpose(),
283 };
284 std::iter::successors(Some(self.borrow().cloned()), succ)
285 }
286
287 pub fn kind(&self) -> parser::SyntaxKind {
288 self.value.borrow().kind()
289 }
290
291 pub fn text_range(&self) -> TextRange {
292 self.value.borrow().text_range()
293 }
294
295 pub fn original_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange {
300 self.borrow().map(SyntaxNode::text_range).original_node_file_range_rooted(db)
301 }
302
303 pub fn original_file_range_with_macro_call_input(
305 self,
306 db: &dyn db::ExpandDatabase,
307 ) -> FileRange {
308 self.borrow().map(SyntaxNode::text_range).original_node_file_range_with_macro_call_input(db)
309 }
310
311 pub fn original_syntax_node_rooted(
312 self,
313 db: &dyn db::ExpandDatabase,
314 ) -> Option<InRealFile<SyntaxNode>> {
315 let file_id = match self.file_id {
318 HirFileId::FileId(file_id) => {
319 return Some(InRealFile { file_id, value: self.value.borrow().clone() });
320 }
321 HirFileId::MacroFile(m)
322 if matches!(m.kind(db), MacroKind::Attr | MacroKind::AttrBuiltIn) =>
323 {
324 m
325 }
326 _ => return None,
327 };
328
329 let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted(
330 db,
331 &db.expansion_span_map(file_id),
332 self.value.borrow().text_range(),
333 )?;
334
335 let kind = self.kind();
336 let value = db
337 .parse(editioned_file_id)
338 .syntax_node()
339 .covering_element(range)
340 .ancestors()
341 .take_while(|it| it.text_range() == range)
342 .find(|it| it.kind() == kind)?;
343 Some(InRealFile::new(editioned_file_id, value))
344 }
345}
346
347impl InFile<&SyntaxNode> {
348 pub fn original_file_range_opt(
350 self,
351 db: &dyn db::ExpandDatabase,
352 ) -> Option<(FileRange, SyntaxContext)> {
353 self.borrow().map(SyntaxNode::text_range).original_node_file_range_opt(db)
354 }
355}
356
357impl InMacroFile<SyntaxToken> {
358 pub fn upmap_once(
359 self,
360 db: &dyn db::ExpandDatabase,
361 ) -> InFile<smallvec::SmallVec<[TextRange; 1]>> {
362 self.file_id.expansion_info(db).map_range_up_once(db, self.value.text_range())
363 }
364}
365
366impl InFile<SyntaxToken> {
367 pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange {
369 match self.file_id {
370 HirFileId::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
371 HirFileId::MacroFile(mac_file) => {
372 let (range, ctxt) = span_for_offset(
373 db,
374 &db.expansion_span_map(mac_file),
375 self.value.text_range().start(),
376 );
377
378 if ctxt.is_root() {
381 return range;
382 }
383
384 let loc = db.lookup_intern_macro_call(mac_file);
386 loc.kind.original_call_range(db, loc.krate)
387 }
388 }
389 }
390
391 pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option<FileRange> {
393 match self.file_id {
394 HirFileId::FileId(file_id) => {
395 Some(FileRange { file_id, range: self.value.text_range() })
396 }
397 HirFileId::MacroFile(mac_file) => {
398 let (range, ctxt) = span_for_offset(
399 db,
400 &db.expansion_span_map(mac_file),
401 self.value.text_range().start(),
402 );
403
404 if ctxt.is_root() { Some(range) } else { None }
407 }
408 }
409 }
410}
411
412impl InMacroFile<TextSize> {
413 pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> (FileRange, SyntaxContext) {
414 span_for_offset(db, &db.expansion_span_map(self.file_id), self.value)
415 }
416}
417
418impl InFile<TextRange> {
419 pub fn original_node_file_range(
420 self,
421 db: &dyn db::ExpandDatabase,
422 ) -> (FileRange, SyntaxContext) {
423 match self.file_id {
424 HirFileId::FileId(file_id) => {
425 (FileRange { file_id, range: self.value }, SyntaxContext::root(file_id.edition(db)))
426 }
427 HirFileId::MacroFile(mac_file) => {
428 match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) {
429 Some(it) => it,
430 None => {
431 let loc = db.lookup_intern_macro_call(mac_file);
432 (
433 loc.kind.original_call_range(db, loc.krate),
434 SyntaxContext::root(loc.def.edition),
435 )
436 }
437 }
438 }
439 }
440 }
441
442 pub fn original_node_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange {
443 match self.file_id {
444 HirFileId::FileId(file_id) => FileRange { file_id, range: self.value },
445 HirFileId::MacroFile(mac_file) => {
446 match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
447 Some(it) => it,
448 _ => {
449 let loc = db.lookup_intern_macro_call(mac_file);
450 loc.kind.original_call_range(db, loc.krate)
451 }
452 }
453 }
454 }
455 }
456
457 pub fn original_node_file_range_with_macro_call_input(
458 self,
459 db: &dyn db::ExpandDatabase,
460 ) -> FileRange {
461 match self.file_id {
462 HirFileId::FileId(file_id) => FileRange { file_id, range: self.value },
463 HirFileId::MacroFile(mac_file) => {
464 match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
465 Some(it) => it,
466 _ => {
467 let loc = db.lookup_intern_macro_call(mac_file);
468 loc.kind.original_call_range_with_input(db)
469 }
470 }
471 }
472 }
473 }
474
475 pub fn original_node_file_range_opt(
476 self,
477 db: &dyn db::ExpandDatabase,
478 ) -> Option<(FileRange, SyntaxContext)> {
479 match self.file_id {
480 HirFileId::FileId(file_id) => Some((
481 FileRange { file_id, range: self.value },
482 SyntaxContext::root(file_id.edition(db)),
483 )),
484 HirFileId::MacroFile(mac_file) => {
485 map_node_range_up(db, &db.expansion_span_map(mac_file), self.value)
486 }
487 }
488 }
489
490 pub fn original_node_file_range_rooted_opt(
491 self,
492 db: &dyn db::ExpandDatabase,
493 ) -> Option<FileRange> {
494 match self.file_id {
495 HirFileId::FileId(file_id) => Some(FileRange { file_id, range: self.value }),
496 HirFileId::MacroFile(mac_file) => {
497 map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value)
498 }
499 }
500 }
501}
502
503impl<N: AstNode> InFile<N> {
504 pub fn original_ast_node_rooted(self, db: &dyn db::ExpandDatabase) -> Option<InRealFile<N>> {
505 let file_id = match self.file_id {
508 HirFileId::FileId(file_id) => {
509 return Some(InRealFile { file_id, value: self.value });
510 }
511 HirFileId::MacroFile(m) => m,
512 };
513 if !matches!(file_id.kind(db), MacroKind::Attr | MacroKind::AttrBuiltIn) {
514 return None;
515 }
516
517 let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted(
518 db,
519 &db.expansion_span_map(file_id),
520 self.value.syntax().text_range(),
521 )?;
522
523 let anc = db.parse(editioned_file_id).syntax_node().covering_element(range);
525 let value = anc.ancestors().find_map(N::cast)?;
526 Some(InRealFile::new(editioned_file_id, value))
527 }
528}
529
530impl<T> InFile<T> {
531 pub fn into_real_file(self) -> Result<InRealFile<T>, InFile<T>> {
532 match self.file_id {
533 HirFileId::FileId(file_id) => Ok(InRealFile { file_id, value: self.value }),
534 HirFileId::MacroFile(_) => Err(self),
535 }
536 }
537}