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.rs57
1 files changed, 56 insertions, 1 deletions
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index dd07a16b4..b219b8fbf 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -12,11 +12,12 @@ pub mod hygiene;
12pub mod diagnostics; 12pub mod diagnostics;
13 13
14use std::hash::{Hash, Hasher}; 14use std::hash::{Hash, Hasher};
15use std::sync::Arc;
15 16
16use ra_db::{salsa, CrateId, FileId}; 17use ra_db::{salsa, CrateId, FileId};
17use ra_syntax::{ 18use ra_syntax::{
18 ast::{self, AstNode}, 19 ast::{self, AstNode},
19 SyntaxNode, 20 SyntaxNode, TextRange, TextUnit,
20}; 21};
21 22
22use crate::ast_id_map::FileAstId; 23use crate::ast_id_map::FileAstId;
@@ -66,6 +67,30 @@ impl HirFileId {
66 } 67 }
67 } 68 }
68 } 69 }
70
71 /// Return expansion information if it is a macro-expansion file
72 pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
73 match self.0 {
74 HirFileIdRepr::FileId(_) => None,
75 HirFileIdRepr::MacroFile(macro_file) => {
76 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
77
78 let arg_start = loc.ast_id.to_node(db).token_tree()?.syntax().text_range().start();
79 let def_start =
80 loc.def.ast_id.to_node(db).token_tree()?.syntax().text_range().start();
81
82 let macro_def = db.macro_def(loc.def)?;
83 let shift = macro_def.0.shift();
84 let exp_map = db.parse_macro(macro_file)?.1;
85 let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
86
87 let arg_start = (loc.ast_id.file_id, arg_start);
88 let def_start = (loc.def.ast_id.file_id, def_start);
89
90 Some(ExpansionInfo { arg_start, def_start, macro_arg, macro_def, exp_map, shift })
91 }
92 }
93 }
69} 94}
70 95
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -112,6 +137,36 @@ impl MacroCallId {
112 } 137 }
113} 138}
114 139
140#[derive(Debug, Clone, PartialEq, Eq)]
141/// ExpansionInfo mainly describes how to map text range between src and expanded macro
142pub struct ExpansionInfo {
143 pub(crate) arg_start: (HirFileId, TextUnit),
144 pub(crate) def_start: (HirFileId, TextUnit),
145 pub(crate) shift: u32,
146
147 pub(crate) macro_def: Arc<(mbe::MacroRules, mbe::TokenMap)>,
148 pub(crate) macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
149 pub(crate) exp_map: Arc<mbe::RevTokenMap>,
150}
151
152impl ExpansionInfo {
153 pub fn find_range(&self, from: TextRange) -> Option<(HirFileId, TextRange)> {
154 fn look_in_rev_map(exp_map: &mbe::RevTokenMap, from: TextRange) -> Option<tt::TokenId> {
155 exp_map.ranges.iter().find(|&it| it.0.is_subrange(&from)).map(|it| it.1)
156 }
157
158 let token_id = look_in_rev_map(&self.exp_map, from)?;
159 let (token_map, file_offset, token_id) = if token_id.0 >= self.shift {
160 (&self.macro_arg.1, self.arg_start, tt::TokenId(token_id.0 - self.shift).into())
161 } else {
162 (&self.macro_def.1, self.def_start, token_id)
163 };
164
165 let range = token_map.relative_range_of(token_id)?;
166 Some((file_offset.0, TextRange::offset_len(range.start() + file_offset.1, range.len())))
167 }
168}
169
115/// `AstId` points to an AST node in any file. 170/// `AstId` points to an AST node in any file.
116/// 171///
117/// It is stable across reparses, and can be used as salsa key/value. 172/// It is stable across reparses, and can be used as salsa key/value.