diff options
author | Edwin Cheng <[email protected]> | 2019-11-03 14:46:12 +0000 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2019-11-04 17:38:20 +0000 |
commit | 159da285e9d8594a2cc4436b5cee7874b07806c9 (patch) | |
tree | 15630e1158cfb7d7305cedd93c1a644b28f9b776 /crates/ra_hir_expand | |
parent | 9fd546bec23ac817a45da28889e76118969db91e (diff) |
Add macro_expansion_info in hir_expand
Diffstat (limited to 'crates/ra_hir_expand')
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 93 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 35 |
2 files changed, 109 insertions, 19 deletions
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index a4ee9a529..8abfbb4ff 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -8,10 +8,16 @@ use ra_prof::profile; | |||
8 | use ra_syntax::{AstNode, Parse, SyntaxNode}; | 8 | use ra_syntax::{AstNode, Parse, SyntaxNode}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | ast_id_map::AstIdMap, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId, | 11 | ast_id_map::AstIdMap, ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, |
12 | MacroFile, MacroFileKind, | 12 | MacroDefId, MacroFile, MacroFileKind, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
16 | pub struct ParseMacroWithInfo { | ||
17 | pub parsed: Parse<SyntaxNode>, | ||
18 | pub expansion_info: Arc<ExpansionInfo>, | ||
19 | } | ||
20 | |||
15 | // FIXME: rename to ExpandDatabase | 21 | // FIXME: rename to ExpandDatabase |
16 | #[salsa::query_group(AstDatabaseStorage)] | 22 | #[salsa::query_group(AstDatabaseStorage)] |
17 | pub trait AstDatabase: SourceDatabase { | 23 | pub trait AstDatabase: SourceDatabase { |
@@ -22,10 +28,16 @@ pub trait AstDatabase: SourceDatabase { | |||
22 | 28 | ||
23 | #[salsa::interned] | 29 | #[salsa::interned] |
24 | fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId; | 30 | fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId; |
25 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<tt::Subtree>>; | 31 | fn macro_arg(&self, id: MacroCallId) -> Option<(Arc<tt::Subtree>, Arc<mbe::TokenMap>)>; |
26 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<mbe::MacroRules>>; | 32 | fn macro_def(&self, id: MacroDefId) -> Option<(Arc<mbe::MacroRules>, Arc<mbe::TokenMap>)>; |
27 | fn parse_macro(&self, macro_file: MacroFile) -> Option<Parse<SyntaxNode>>; | 33 | fn parse_macro(&self, macro_file: MacroFile) -> Option<Parse<SyntaxNode>>; |
28 | fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>; | 34 | fn parse_macro_with_info(&self, macro_file: MacroFile) -> Option<ParseMacroWithInfo>; |
35 | fn macro_expand( | ||
36 | &self, | ||
37 | macro_call: MacroCallId, | ||
38 | ) -> Result<(Arc<tt::Subtree>, (Arc<mbe::TokenMap>, Arc<mbe::TokenMap>)), String>; | ||
39 | |||
40 | fn macro_expansion_info(&self, macro_file: MacroFile) -> Option<Arc<ExpansionInfo>>; | ||
29 | } | 41 | } |
30 | 42 | ||
31 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 43 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
@@ -34,10 +46,13 @@ pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdM | |||
34 | Arc::new(map) | 46 | Arc::new(map) |
35 | } | 47 | } |
36 | 48 | ||
37 | pub(crate) fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> { | 49 | pub(crate) fn macro_def( |
50 | db: &dyn AstDatabase, | ||
51 | id: MacroDefId, | ||
52 | ) -> Option<(Arc<mbe::MacroRules>, Arc<mbe::TokenMap>)> { | ||
38 | let macro_call = id.ast_id.to_node(db); | 53 | let macro_call = id.ast_id.to_node(db); |
39 | let arg = macro_call.token_tree()?; | 54 | let arg = macro_call.token_tree()?; |
40 | let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| { | 55 | let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { |
41 | log::warn!("fail on macro_def to token tree: {:#?}", arg); | 56 | log::warn!("fail on macro_def to token tree: {:#?}", arg); |
42 | None | 57 | None |
43 | })?; | 58 | })?; |
@@ -45,32 +60,36 @@ pub(crate) fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<Macr | |||
45 | log::warn!("fail on macro_def parse: {:#?}", tt); | 60 | log::warn!("fail on macro_def parse: {:#?}", tt); |
46 | None | 61 | None |
47 | })?; | 62 | })?; |
48 | Some(Arc::new(rules)) | 63 | Some((Arc::new(rules), Arc::new(tmap))) |
49 | } | 64 | } |
50 | 65 | ||
51 | pub(crate) fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> { | 66 | pub(crate) fn macro_arg( |
67 | db: &dyn AstDatabase, | ||
68 | id: MacroCallId, | ||
69 | ) -> Option<(Arc<tt::Subtree>, Arc<mbe::TokenMap>)> { | ||
52 | let loc = db.lookup_intern_macro(id); | 70 | let loc = db.lookup_intern_macro(id); |
53 | let macro_call = loc.ast_id.to_node(db); | 71 | let macro_call = loc.ast_id.to_node(db); |
54 | let arg = macro_call.token_tree()?; | 72 | let arg = macro_call.token_tree()?; |
55 | let (tt, _) = mbe::ast_to_token_tree(&arg)?; | 73 | let (tt, tmap) = mbe::ast_to_token_tree(&arg)?; |
56 | Some(Arc::new(tt)) | 74 | Some((Arc::new(tt), Arc::new(tmap))) |
57 | } | 75 | } |
58 | 76 | ||
59 | pub(crate) fn macro_expand( | 77 | pub(crate) fn macro_expand( |
60 | db: &dyn AstDatabase, | 78 | db: &dyn AstDatabase, |
61 | id: MacroCallId, | 79 | id: MacroCallId, |
62 | ) -> Result<Arc<tt::Subtree>, String> { | 80 | ) -> Result<(Arc<tt::Subtree>, (Arc<mbe::TokenMap>, Arc<mbe::TokenMap>)), String> { |
63 | let loc = db.lookup_intern_macro(id); | 81 | let loc = db.lookup_intern_macro(id); |
64 | let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; | 82 | let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; |
65 | 83 | ||
66 | let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; | 84 | let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; |
67 | let tt = macro_rules.expand(¯o_arg).map_err(|err| format!("{:?}", err))?; | 85 | let tt = macro_rules.0.expand(¯o_arg.0).map_err(|err| format!("{:?}", err))?; |
68 | // Set a hard limit for the expanded tt | 86 | // Set a hard limit for the expanded tt |
69 | let count = tt.count(); | 87 | let count = tt.count(); |
70 | if count > 65536 { | 88 | if count > 65536 { |
71 | return Err(format!("Total tokens count exceed limit : count = {}", count)); | 89 | return Err(format!("Total tokens count exceed limit : count = {}", count)); |
72 | } | 90 | } |
73 | Ok(Arc::new(tt)) | 91 | |
92 | Ok((Arc::new(tt), (macro_arg.1.clone(), macro_rules.1.clone()))) | ||
74 | } | 93 | } |
75 | 94 | ||
76 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | 95 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { |
@@ -87,6 +106,13 @@ pub(crate) fn parse_macro( | |||
87 | macro_file: MacroFile, | 106 | macro_file: MacroFile, |
88 | ) -> Option<Parse<SyntaxNode>> { | 107 | ) -> Option<Parse<SyntaxNode>> { |
89 | let _p = profile("parse_macro_query"); | 108 | let _p = profile("parse_macro_query"); |
109 | db.parse_macro_with_info(macro_file).map(|r| r.parsed) | ||
110 | } | ||
111 | |||
112 | pub(crate) fn parse_macro_with_info( | ||
113 | db: &dyn AstDatabase, | ||
114 | macro_file: MacroFile, | ||
115 | ) -> Option<ParseMacroWithInfo> { | ||
90 | let macro_call_id = macro_file.macro_call_id; | 116 | let macro_call_id = macro_file.macro_call_id; |
91 | let tt = db | 117 | let tt = db |
92 | .macro_expand(macro_call_id) | 118 | .macro_expand(macro_call_id) |
@@ -97,8 +123,39 @@ pub(crate) fn parse_macro( | |||
97 | log::warn!("fail on macro_parse: (reason: {})", err,); | 123 | log::warn!("fail on macro_parse: (reason: {})", err,); |
98 | }) | 124 | }) |
99 | .ok()?; | 125 | .ok()?; |
100 | match macro_file.macro_file_kind { | 126 | let res = match macro_file.macro_file_kind { |
101 | MacroFileKind::Items => mbe::token_tree_to_items(&tt).ok().map(Parse::to_syntax), | 127 | MacroFileKind::Items => { |
102 | MacroFileKind::Expr => mbe::token_tree_to_expr(&tt).ok().map(Parse::to_syntax), | 128 | mbe::token_tree_to_items(&tt.0).ok().map(|(p, map)| (Parse::to_syntax(p), map)) |
103 | } | 129 | } |
130 | MacroFileKind::Expr => { | ||
131 | mbe::token_tree_to_expr(&tt.0).ok().map(|(p, map)| (Parse::to_syntax(p), map)) | ||
132 | } | ||
133 | }; | ||
134 | |||
135 | res.map(|(parsed, exp_map)| { | ||
136 | let (arg_map, def_map) = tt.1; | ||
137 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_call_id); | ||
138 | |||
139 | let def_start = | ||
140 | loc.def.ast_id.to_node(db).token_tree().map(|t| t.syntax().text_range().start()); | ||
141 | let arg_start = | ||
142 | loc.ast_id.to_node(db).token_tree().map(|t| t.syntax().text_range().start()); | ||
143 | |||
144 | let arg_map = | ||
145 | arg_start.map(|start| exp_map.ranges(&arg_map, start)).unwrap_or_else(|| Vec::new()); | ||
146 | |||
147 | let def_map = | ||
148 | def_start.map(|start| exp_map.ranges(&def_map, start)).unwrap_or_else(|| Vec::new()); | ||
149 | |||
150 | let info = ExpansionInfo { arg_map, def_map }; | ||
151 | |||
152 | ParseMacroWithInfo { parsed, expansion_info: Arc::new(info) } | ||
153 | }) | ||
154 | } | ||
155 | |||
156 | pub(crate) fn macro_expansion_info( | ||
157 | db: &dyn AstDatabase, | ||
158 | macro_file: MacroFile, | ||
159 | ) -> Option<Arc<ExpansionInfo>> { | ||
160 | db.parse_macro_with_info(macro_file).map(|res| res.expansion_info.clone()) | ||
104 | } | 161 | } |
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index dd07a16b4..194020b45 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -16,7 +16,7 @@ use std::hash::{Hash, Hasher}; | |||
16 | use ra_db::{salsa, CrateId, FileId}; | 16 | use ra_db::{salsa, CrateId, FileId}; |
17 | use ra_syntax::{ | 17 | use ra_syntax::{ |
18 | ast::{self, AstNode}, | 18 | ast::{self, AstNode}, |
19 | SyntaxNode, | 19 | SyntaxNode, TextRange, |
20 | }; | 20 | }; |
21 | 21 | ||
22 | use crate::ast_id_map::FileAstId; | 22 | use crate::ast_id_map::FileAstId; |
@@ -112,6 +112,39 @@ impl MacroCallId { | |||
112 | } | 112 | } |
113 | } | 113 | } |
114 | 114 | ||
115 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
116 | /// ExpansionInfo mainly describle how to map text range between src and expaned macro | ||
117 | pub struct ExpansionInfo { | ||
118 | pub arg_map: Vec<(TextRange, TextRange)>, | ||
119 | pub def_map: Vec<(TextRange, TextRange)>, | ||
120 | } | ||
121 | |||
122 | impl ExpansionInfo { | ||
123 | pub fn find_range( | ||
124 | &self, | ||
125 | from: TextRange, | ||
126 | (arg_file_id, def_file_id): (HirFileId, HirFileId), | ||
127 | ) -> Option<(HirFileId, TextRange)> { | ||
128 | for (src, dest) in &self.arg_map { | ||
129 | dbg!((src, *dest, "arg_map")); | ||
130 | if src.is_subrange(&from) { | ||
131 | dbg!((arg_file_id, *dest)); | ||
132 | return Some((arg_file_id, *dest)); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | for (src, dest) in &self.def_map { | ||
137 | dbg!((src, *dest, "def_map")); | ||
138 | if src.is_subrange(&from) { | ||
139 | dbg!((arg_file_id, *dest)); | ||
140 | return Some((def_file_id, *dest)); | ||
141 | } | ||
142 | } | ||
143 | |||
144 | None | ||
145 | } | ||
146 | } | ||
147 | |||
115 | /// `AstId` points to an AST node in any file. | 148 | /// `AstId` points to an AST node in any file. |
116 | /// | 149 | /// |
117 | /// It is stable across reparses, and can be used as salsa key/value. | 150 | /// It is stable across reparses, and can be used as salsa key/value. |