aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand')
-rw-r--r--crates/ra_hir_expand/Cargo.toml1
-rw-r--r--crates/ra_hir_expand/src/db.rs46
-rw-r--r--crates/ra_hir_expand/src/lib.rs59
3 files changed, 89 insertions, 17 deletions
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml
index 9bf5b7918..8f29bf7d9 100644
--- a/crates/ra_hir_expand/Cargo.toml
+++ b/crates/ra_hir_expand/Cargo.toml
@@ -10,6 +10,7 @@ log = "0.4.5"
10ra_arena = { path = "../ra_arena" } 10ra_arena = { path = "../ra_arena" }
11ra_db = { path = "../ra_db" } 11ra_db = { path = "../ra_db" }
12ra_syntax = { path = "../ra_syntax" } 12ra_syntax = { path = "../ra_syntax" }
13ra_parser = { path = "../ra_parser" }
13ra_prof = { path = "../ra_prof" } 14ra_prof = { path = "../ra_prof" }
14tt = { path = "../ra_tt", package = "ra_tt" } 15tt = { path = "../ra_tt", package = "ra_tt" }
15mbe = { path = "../ra_mbe", package = "ra_mbe" } 16mbe = { path = "../ra_mbe", package = "ra_mbe" }
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index a4ee9a529..b4dafe1d8 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -4,6 +4,7 @@ use std::sync::Arc;
4 4
5use mbe::MacroRules; 5use mbe::MacroRules;
6use ra_db::{salsa, SourceDatabase}; 6use ra_db::{salsa, SourceDatabase};
7use ra_parser::FragmentKind;
7use ra_prof::profile; 8use ra_prof::profile;
8use ra_syntax::{AstNode, Parse, SyntaxNode}; 9use ra_syntax::{AstNode, Parse, SyntaxNode};
9 10
@@ -22,9 +23,12 @@ pub trait AstDatabase: SourceDatabase {
22 23
23 #[salsa::interned] 24 #[salsa::interned]
24 fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId; 25 fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId;
25 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<tt::Subtree>>; 26 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
26 fn macro_def(&self, id: MacroDefId) -> Option<Arc<mbe::MacroRules>>; 27 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>>;
27 fn parse_macro(&self, macro_file: MacroFile) -> Option<Parse<SyntaxNode>>; 28 fn parse_macro(
29 &self,
30 macro_file: MacroFile,
31 ) -> Option<(Parse<SyntaxNode>, Arc<mbe::RevTokenMap>)>;
28 fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>; 32 fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>;
29} 33}
30 34
@@ -34,10 +38,13 @@ pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdM
34 Arc::new(map) 38 Arc::new(map)
35} 39}
36 40
37pub(crate) fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> { 41pub(crate) fn macro_def(
42 db: &dyn AstDatabase,
43 id: MacroDefId,
44) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>> {
38 let macro_call = id.ast_id.to_node(db); 45 let macro_call = id.ast_id.to_node(db);
39 let arg = macro_call.token_tree()?; 46 let arg = macro_call.token_tree()?;
40 let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| { 47 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
41 log::warn!("fail on macro_def to token tree: {:#?}", arg); 48 log::warn!("fail on macro_def to token tree: {:#?}", arg);
42 None 49 None
43 })?; 50 })?;
@@ -45,15 +52,18 @@ pub(crate) fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<Macr
45 log::warn!("fail on macro_def parse: {:#?}", tt); 52 log::warn!("fail on macro_def parse: {:#?}", tt);
46 None 53 None
47 })?; 54 })?;
48 Some(Arc::new(rules)) 55 Some(Arc::new((rules, tmap)))
49} 56}
50 57
51pub(crate) fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> { 58pub(crate) fn macro_arg(
59 db: &dyn AstDatabase,
60 id: MacroCallId,
61) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
52 let loc = db.lookup_intern_macro(id); 62 let loc = db.lookup_intern_macro(id);
53 let macro_call = loc.ast_id.to_node(db); 63 let macro_call = loc.ast_id.to_node(db);
54 let arg = macro_call.token_tree()?; 64 let arg = macro_call.token_tree()?;
55 let (tt, _) = mbe::ast_to_token_tree(&arg)?; 65 let (tt, tmap) = mbe::ast_to_token_tree(&arg)?;
56 Some(Arc::new(tt)) 66 Some(Arc::new((tt, tmap)))
57} 67}
58 68
59pub(crate) fn macro_expand( 69pub(crate) fn macro_expand(
@@ -64,7 +74,7 @@ pub(crate) fn macro_expand(
64 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; 74 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
65 75
66 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; 76 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
67 let tt = macro_rules.expand(&macro_arg).map_err(|err| format!("{:?}", err))?; 77 let tt = macro_rules.0.expand(&macro_arg.0).map_err(|err| format!("{:?}", err))?;
68 // Set a hard limit for the expanded tt 78 // Set a hard limit for the expanded tt
69 let count = tt.count(); 79 let count = tt.count();
70 if count > 65536 { 80 if count > 65536 {
@@ -77,7 +87,7 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio
77 match file_id.0 { 87 match file_id.0 {
78 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), 88 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
79 HirFileIdRepr::MacroFile(macro_file) => { 89 HirFileIdRepr::MacroFile(macro_file) => {
80 db.parse_macro(macro_file).map(|it| it.syntax_node()) 90 db.parse_macro(macro_file).map(|(it, _)| it.syntax_node())
81 } 91 }
82 } 92 }
83} 93}
@@ -85,8 +95,9 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio
85pub(crate) fn parse_macro( 95pub(crate) fn parse_macro(
86 db: &dyn AstDatabase, 96 db: &dyn AstDatabase,
87 macro_file: MacroFile, 97 macro_file: MacroFile,
88) -> Option<Parse<SyntaxNode>> { 98) -> Option<(Parse<SyntaxNode>, Arc<mbe::RevTokenMap>)> {
89 let _p = profile("parse_macro_query"); 99 let _p = profile("parse_macro_query");
100
90 let macro_call_id = macro_file.macro_call_id; 101 let macro_call_id = macro_file.macro_call_id;
91 let tt = db 102 let tt = db
92 .macro_expand(macro_call_id) 103 .macro_expand(macro_call_id)
@@ -97,8 +108,11 @@ pub(crate) fn parse_macro(
97 log::warn!("fail on macro_parse: (reason: {})", err,); 108 log::warn!("fail on macro_parse: (reason: {})", err,);
98 }) 109 })
99 .ok()?; 110 .ok()?;
100 match macro_file.macro_file_kind { 111
101 MacroFileKind::Items => mbe::token_tree_to_items(&tt).ok().map(Parse::to_syntax), 112 let fragment_kind = match macro_file.macro_file_kind {
102 MacroFileKind::Expr => mbe::token_tree_to_expr(&tt).ok().map(Parse::to_syntax), 113 MacroFileKind::Items => FragmentKind::Items,
103 } 114 MacroFileKind::Expr => FragmentKind::Expr,
115 };
116 let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?;
117 Some((parse, Arc::new(rev_token_map)))
104} 118}
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index dd07a16b4..151d1d785 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,38 @@ 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 let token_id = look_in_rev_map(&self.exp_map, from)?;
155
156 let (token_map, (file_id, start_offset), token_id) = if token_id.0 >= self.shift {
157 (&self.macro_arg.1, self.arg_start, tt::TokenId(token_id.0 - self.shift).into())
158 } else {
159 (&self.macro_def.1, self.def_start, token_id)
160 };
161
162 let range = token_map.relative_range_of(token_id)?;
163
164 return Some((file_id, range + start_offset));
165
166 fn look_in_rev_map(exp_map: &mbe::RevTokenMap, from: TextRange) -> Option<tt::TokenId> {
167 exp_map.ranges.iter().find(|&it| it.0.is_subrange(&from)).map(|it| it.1)
168 }
169 }
170}
171
115/// `AstId` points to an AST node in any file. 172/// `AstId` points to an AST node in any file.
116/// 173///
117/// It is stable across reparses, and can be used as salsa key/value. 174/// It is stable across reparses, and can be used as salsa key/value.