aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand/src/lib.rs')
-rw-r--r--crates/ra_hir_expand/src/lib.rs70
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
19use ra_db::{salsa, CrateId, FileId}; 19use ra_db::{salsa, CrateId, FileId};
20use ra_syntax::{ 20use 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
25use crate::ast_id_map::FileAstId; 26use 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)]
152pub struct ExpansionInfo { 160pub 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
162impl ExpansionInfo { 170impl 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)]
227pub struct Source<T> { 252pub 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
232impl<T> Source<T> { 259impl<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")