diff options
Diffstat (limited to 'crates/ra_hir_expand/src/lib.rs')
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 70 |
1 files changed, 53 insertions, 17 deletions
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 930789b0f..57e2e6cb1 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -18,8 +18,9 @@ use std::sync::Arc; | |||
18 | 18 | ||
19 | use ra_db::{salsa, CrateId, FileId}; | 19 | use ra_db::{salsa, CrateId, FileId}; |
20 | use ra_syntax::{ | 20 | use ra_syntax::{ |
21 | algo, | ||
21 | ast::{self, AstNode}, | 22 | ast::{self, AstNode}, |
22 | SyntaxNode, TextRange, TextUnit, | 23 | SyntaxNode, SyntaxToken, TextRange, TextUnit, |
23 | }; | 24 | }; |
24 | 25 | ||
25 | use crate::ast_id_map::FileAstId; | 26 | use crate::ast_id_map::FileAstId; |
@@ -83,14 +84,21 @@ impl HirFileId { | |||
83 | loc.def.ast_id.to_node(db).token_tree()?.syntax().text_range().start(); | 84 | loc.def.ast_id.to_node(db).token_tree()?.syntax().text_range().start(); |
84 | 85 | ||
85 | let macro_def = db.macro_def(loc.def)?; | 86 | let macro_def = db.macro_def(loc.def)?; |
86 | let shift = macro_def.0.shift(); | 87 | let (parse, exp_map) = db.parse_macro(macro_file)?; |
87 | let exp_map = db.parse_macro(macro_file)?.1; | 88 | let expanded = Source::new(self, parse.syntax_node()); |
88 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; | 89 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; |
89 | 90 | ||
90 | let arg_start = (loc.ast_id.file_id, arg_start); | 91 | let arg_start = (loc.ast_id.file_id, arg_start); |
91 | let def_start = (loc.def.ast_id.file_id, def_start); | 92 | let def_start = (loc.def.ast_id.file_id, def_start); |
92 | 93 | ||
93 | Some(ExpansionInfo { arg_start, def_start, macro_arg, macro_def, exp_map, shift }) | 94 | Some(ExpansionInfo { |
95 | expanded, | ||
96 | arg_start, | ||
97 | def_start, | ||
98 | macro_arg, | ||
99 | macro_def, | ||
100 | exp_map, | ||
101 | }) | ||
94 | } | 102 | } |
95 | } | 103 | } |
96 | } | 104 | } |
@@ -147,26 +155,42 @@ impl MacroCallId { | |||
147 | } | 155 | } |
148 | } | 156 | } |
149 | 157 | ||
150 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
151 | /// ExpansionInfo mainly describes how to map text range between src and expanded macro | 158 | /// ExpansionInfo mainly describes how to map text range between src and expanded macro |
159 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
152 | pub struct ExpansionInfo { | 160 | pub struct ExpansionInfo { |
153 | pub(crate) arg_start: (HirFileId, TextUnit), | 161 | expanded: Source<SyntaxNode>, |
154 | pub(crate) def_start: (HirFileId, TextUnit), | 162 | arg_start: (HirFileId, TextUnit), |
155 | pub(crate) shift: u32, | 163 | def_start: (HirFileId, TextUnit), |
156 | 164 | ||
157 | pub(crate) macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | 165 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, |
158 | pub(crate) macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, | 166 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, |
159 | pub(crate) exp_map: Arc<mbe::RevTokenMap>, | 167 | exp_map: Arc<mbe::RevTokenMap>, |
160 | } | 168 | } |
161 | 169 | ||
162 | impl ExpansionInfo { | 170 | impl ExpansionInfo { |
171 | pub fn map_token_down(&self, token: Source<&SyntaxToken>) -> Option<Source<SyntaxToken>> { | ||
172 | assert_eq!(token.file_id, self.arg_start.0); | ||
173 | let range = token.ast.text_range().checked_sub(self.arg_start.1)?; | ||
174 | let token_id = self.macro_arg.1.token_by_range(range)?; | ||
175 | let token_id = self.macro_def.0.map_id_down(token_id); | ||
176 | |||
177 | let range = self.exp_map.range_by_token(token_id)?; | ||
178 | |||
179 | let token = algo::find_covering_element(&self.expanded.ast, range).into_token()?; | ||
180 | |||
181 | Some(self.expanded.with_ast(token)) | ||
182 | } | ||
183 | |||
184 | // FIXME: a more correct signature would be | ||
185 | // `pub fn map_token_up(&self, token: Source<&SyntaxToken>) -> Option<Source<SyntaxToken>>` | ||
163 | pub fn find_range(&self, from: TextRange) -> Option<(HirFileId, TextRange)> { | 186 | pub fn find_range(&self, from: TextRange) -> Option<(HirFileId, TextRange)> { |
164 | let token_id = look_in_rev_map(&self.exp_map, from)?; | 187 | let token_id = look_in_rev_map(&self.exp_map, from)?; |
165 | 188 | ||
166 | let (token_map, (file_id, start_offset), token_id) = if token_id.0 >= self.shift { | 189 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); |
167 | (&self.macro_arg.1, self.arg_start, tt::TokenId(token_id.0 - self.shift).into()) | 190 | |
168 | } else { | 191 | let (token_map, (file_id, start_offset)) = match origin { |
169 | (&self.macro_def.1, self.def_start, token_id) | 192 | mbe::Origin::Call => (&self.macro_arg.1, self.arg_start), |
193 | mbe::Origin::Def => (&self.macro_def.1, self.def_start), | ||
170 | }; | 194 | }; |
171 | 195 | ||
172 | let range = token_map.relative_range_of(token_id)?; | 196 | let range = token_map.relative_range_of(token_id)?; |
@@ -223,18 +247,30 @@ impl<N: AstNode> AstId<N> { | |||
223 | } | 247 | } |
224 | } | 248 | } |
225 | 249 | ||
250 | /// FIXME: https://github.com/matklad/with ? | ||
226 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] | 251 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] |
227 | pub struct Source<T> { | 252 | pub struct Source<T> { |
228 | pub file_id: HirFileId, | 253 | pub file_id: HirFileId, |
254 | // FIXME: this stores all kind of things, not only `ast`. | ||
255 | // There should be a better name... | ||
229 | pub ast: T, | 256 | pub ast: T, |
230 | } | 257 | } |
231 | 258 | ||
232 | impl<T> Source<T> { | 259 | impl<T> Source<T> { |
260 | pub fn new(file_id: HirFileId, ast: T) -> Source<T> { | ||
261 | Source { file_id, ast } | ||
262 | } | ||
263 | |||
264 | // Similarly, naming here is stupid... | ||
265 | pub fn with_ast<U>(&self, ast: U) -> Source<U> { | ||
266 | Source::new(self.file_id, ast) | ||
267 | } | ||
268 | |||
233 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 269 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
234 | Source { file_id: self.file_id, ast: f(self.ast) } | 270 | Source::new(self.file_id, f(self.ast)) |
235 | } | 271 | } |
236 | pub fn as_ref(&self) -> Source<&T> { | 272 | pub fn as_ref(&self) -> Source<&T> { |
237 | Source { file_id: self.file_id, ast: &self.ast } | 273 | self.with_ast(&self.ast) |
238 | } | 274 | } |
239 | pub fn file_syntax(&self, db: &impl db::AstDatabase) -> SyntaxNode { | 275 | pub fn file_syntax(&self, db: &impl db::AstDatabase) -> SyntaxNode { |
240 | db.parse_or_expand(self.file_id).expect("source created from invalid file") | 276 | db.parse_or_expand(self.file_id).expect("source created from invalid file") |