1use span::{Span, SyntaxContext};
4use stdx::TupleExt;
5use syntax::{AstNode, TextRange, ast};
6use triomphe::Arc;
7
8pub use span::RealSpanMap;
9
10use crate::{HirFileId, MacroCallId, attrs::collect_attrs, db::ExpandDatabase};
11
12pub type ExpansionSpanMap = span::SpanMap<SyntaxContext>;
13
14#[derive(Clone, Debug, PartialEq, Eq)]
16pub enum SpanMap {
17 ExpansionSpanMap(Arc<ExpansionSpanMap>),
19 RealSpanMap(Arc<RealSpanMap>),
21}
22
23#[derive(Copy, Clone)]
24pub enum SpanMapRef<'a> {
25 ExpansionSpanMap(&'a ExpansionSpanMap),
27 RealSpanMap(&'a RealSpanMap),
29}
30
31impl syntax_bridge::SpanMapper<Span> for SpanMap {
32 fn span_for(&self, range: TextRange) -> Span {
33 self.span_for_range(range)
34 }
35}
36
37impl syntax_bridge::SpanMapper<Span> for SpanMapRef<'_> {
38 fn span_for(&self, range: TextRange) -> Span {
39 self.span_for_range(range)
40 }
41}
42
43impl SpanMap {
44 pub fn span_for_range(&self, range: TextRange) -> Span {
45 match self {
46 Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()),
51 Self::RealSpanMap(span_map) => span_map.span_for_range(range),
52 }
53 }
54
55 pub fn as_ref(&self) -> SpanMapRef<'_> {
56 match self {
57 Self::ExpansionSpanMap(span_map) => SpanMapRef::ExpansionSpanMap(span_map),
58 Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map),
59 }
60 }
61
62 #[inline]
63 pub(crate) fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap {
64 match file_id {
65 HirFileId::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)),
66 HirFileId::MacroFile(m) => {
67 SpanMap::ExpansionSpanMap(db.parse_macro_expansion(m).value.1)
68 }
69 }
70 }
71}
72
73impl SpanMapRef<'_> {
74 pub fn span_for_range(self, range: TextRange) -> Span {
75 match self {
76 Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()),
77 Self::RealSpanMap(span_map) => span_map.span_for_range(range),
78 }
79 }
80}
81
82pub(crate) fn real_span_map(
83 db: &dyn ExpandDatabase,
84 editioned_file_id: base_db::EditionedFileId,
85) -> Arc<RealSpanMap> {
86 use syntax::ast::HasModuleItem;
87 let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)];
88 let ast_id_map = db.ast_id_map(editioned_file_id.into());
89
90 let tree = db.parse(editioned_file_id).tree();
91 let item_to_entry =
106 |item: ast::Item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase());
107 pairs.extend(tree.items().map(item_to_entry));
109 tree.items().for_each(|item| match &item {
113 ast::Item::ExternBlock(it)
114 if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) =>
115 {
116 if let Some(extern_item_list) = it.extern_item_list() {
117 pairs.extend(
118 extern_item_list.extern_items().map(ast::Item::from).map(item_to_entry),
119 );
120 }
121 }
122 ast::Item::Impl(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => {
123 if let Some(assoc_item_list) = it.assoc_item_list() {
124 pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry));
125 }
126 }
127 ast::Item::Module(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => {
128 if let Some(item_list) = it.item_list() {
129 pairs.extend(item_list.items().map(item_to_entry));
130 }
131 }
132 ast::Item::Trait(it) if !collect_attrs(it).map(TupleExt::tail).any(|it| it.is_left()) => {
133 if let Some(assoc_item_list) = it.assoc_item_list() {
134 pairs.extend(assoc_item_list.assoc_items().map(ast::Item::from).map(item_to_entry));
135 }
136 }
137 _ => (),
138 });
139
140 Arc::new(RealSpanMap::from_file(
141 editioned_file_id.editioned_file_id(db),
142 pairs.into_boxed_slice(),
143 tree.syntax().text_range().end(),
144 ))
145}
146
147pub(crate) fn expansion_span_map(
148 db: &dyn ExpandDatabase,
149 file_id: MacroCallId,
150) -> Arc<ExpansionSpanMap> {
151 db.parse_macro_expansion(file_id).value.1
152}