diff options
Diffstat (limited to 'crates/hir_expand/src')
-rw-r--r-- | crates/hir_expand/src/db.rs | 397 | ||||
-rw-r--r-- | crates/hir_expand/src/hygiene.rs | 16 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 21 |
3 files changed, 222 insertions, 212 deletions
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 1e4b0cc19..3e9abd8a1 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -3,14 +3,14 @@ | |||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use base_db::{salsa, SourceDatabase}; | 5 | use base_db::{salsa, SourceDatabase}; |
6 | use mbe::{ExpandError, ExpandResult, MacroDef, MacroRules}; | 6 | use mbe::{ExpandError, ExpandResult}; |
7 | use parser::FragmentKind; | 7 | use parser::FragmentKind; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | algo::diff, | 9 | algo::diff, |
10 | ast::{MacroStmts, NameOwner}, | 10 | ast::{self, NameOwner}, |
11 | AstNode, GreenNode, Parse, | 11 | AstNode, GreenNode, Parse, |
12 | SyntaxKind::*, | 12 | SyntaxKind::*, |
13 | SyntaxNode, | 13 | SyntaxNode, SyntaxToken, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | use crate::{ | 16 | use crate::{ |
@@ -27,23 +27,28 @@ const TOKEN_LIMIT: usize = 524288; | |||
27 | 27 | ||
28 | #[derive(Debug, Clone, Eq, PartialEq)] | 28 | #[derive(Debug, Clone, Eq, PartialEq)] |
29 | pub enum TokenExpander { | 29 | pub enum TokenExpander { |
30 | MacroRules(mbe::MacroRules), | 30 | /// Old-style `macro_rules`. |
31 | MacroDef(mbe::MacroDef), | 31 | MacroRules { mac: mbe::MacroRules, def_site_token_map: mbe::TokenMap }, |
32 | /// AKA macros 2.0. | ||
33 | MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap }, | ||
34 | /// Stuff like `line!` and `file!`. | ||
32 | Builtin(BuiltinFnLikeExpander), | 35 | Builtin(BuiltinFnLikeExpander), |
36 | /// `derive(Copy)` and such. | ||
33 | BuiltinDerive(BuiltinDeriveExpander), | 37 | BuiltinDerive(BuiltinDeriveExpander), |
38 | /// The thing we love the most here in rust-analyzer -- procedural macros. | ||
34 | ProcMacro(ProcMacroExpander), | 39 | ProcMacro(ProcMacroExpander), |
35 | } | 40 | } |
36 | 41 | ||
37 | impl TokenExpander { | 42 | impl TokenExpander { |
38 | pub fn expand( | 43 | fn expand( |
39 | &self, | 44 | &self, |
40 | db: &dyn AstDatabase, | 45 | db: &dyn AstDatabase, |
41 | id: LazyMacroId, | 46 | id: LazyMacroId, |
42 | tt: &tt::Subtree, | 47 | tt: &tt::Subtree, |
43 | ) -> mbe::ExpandResult<tt::Subtree> { | 48 | ) -> mbe::ExpandResult<tt::Subtree> { |
44 | match self { | 49 | match self { |
45 | TokenExpander::MacroRules(it) => it.expand(tt), | 50 | TokenExpander::MacroRules { mac, .. } => mac.expand(tt), |
46 | TokenExpander::MacroDef(it) => it.expand(tt), | 51 | TokenExpander::MacroDef { mac, .. } => mac.expand(tt), |
47 | TokenExpander::Builtin(it) => it.expand(db, id, tt), | 52 | TokenExpander::Builtin(it) => it.expand(db, id, tt), |
48 | // FIXME switch these to ExpandResult as well | 53 | // FIXME switch these to ExpandResult as well |
49 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), | 54 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), |
@@ -56,23 +61,23 @@ impl TokenExpander { | |||
56 | } | 61 | } |
57 | } | 62 | } |
58 | 63 | ||
59 | pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { | 64 | pub(crate) fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { |
60 | match self { | 65 | match self { |
61 | TokenExpander::MacroRules(it) => it.map_id_down(id), | 66 | TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id), |
62 | TokenExpander::MacroDef(it) => it.map_id_down(id), | 67 | TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id), |
63 | TokenExpander::Builtin(..) => id, | 68 | TokenExpander::Builtin(..) |
64 | TokenExpander::BuiltinDerive(..) => id, | 69 | | TokenExpander::BuiltinDerive(..) |
65 | TokenExpander::ProcMacro(..) => id, | 70 | | TokenExpander::ProcMacro(..) => id, |
66 | } | 71 | } |
67 | } | 72 | } |
68 | 73 | ||
69 | pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { | 74 | pub(crate) fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { |
70 | match self { | 75 | match self { |
71 | TokenExpander::MacroRules(it) => it.map_id_up(id), | 76 | TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id), |
72 | TokenExpander::MacroDef(it) => it.map_id_up(id), | 77 | TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id), |
73 | TokenExpander::Builtin(..) => (id, mbe::Origin::Call), | 78 | TokenExpander::Builtin(..) |
74 | TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), | 79 | | TokenExpander::BuiltinDerive(..) |
75 | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), | 80 | | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), |
76 | } | 81 | } |
77 | } | 82 | } |
78 | } | 83 | } |
@@ -82,28 +87,48 @@ impl TokenExpander { | |||
82 | pub trait AstDatabase: SourceDatabase { | 87 | pub trait AstDatabase: SourceDatabase { |
83 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; | 88 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; |
84 | 89 | ||
90 | /// Main public API -- parsis a hir file, not caring whether it's a real | ||
91 | /// file or a macro expansion. | ||
85 | #[salsa::transparent] | 92 | #[salsa::transparent] |
86 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; | 93 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; |
87 | 94 | /// Implementation for the macro case. | |
88 | #[salsa::interned] | ||
89 | fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId; | ||
90 | fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>; | ||
91 | #[salsa::transparent] | ||
92 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; | ||
93 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; | ||
94 | fn parse_macro_expansion( | 95 | fn parse_macro_expansion( |
95 | &self, | 96 | &self, |
96 | macro_file: MacroFile, | 97 | macro_file: MacroFile, |
97 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; | 98 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; |
98 | fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; | ||
99 | |||
100 | /// Firewall query that returns the error from the `macro_expand` query. | ||
101 | fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>; | ||
102 | 99 | ||
100 | /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the | ||
101 | /// reason why we use salsa at all. | ||
102 | /// | ||
103 | /// We encode macro definitions into ids of macro calls, this what allows us | ||
104 | /// to be incremental. | ||
105 | #[salsa::interned] | ||
106 | fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId; | ||
107 | /// Certain built-in macros are eager (`format!(concat!("file: ", file!(), "{}"")), 92`). | ||
108 | /// For them, we actually want to encode the whole token tree as an argument. | ||
103 | #[salsa::interned] | 109 | #[salsa::interned] |
104 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 110 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
105 | 111 | ||
112 | /// Lowers syntactic macro call to a token tree representation. | ||
113 | #[salsa::transparent] | ||
114 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; | ||
115 | /// Extracts syntax node, corresponding to a macro call. That's a firewall | ||
116 | /// query, only typing in the macro call itself changes the returned | ||
117 | /// subtree. | ||
118 | fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>; | ||
119 | /// Gets the expander for this macro. This compiles declarative macros, and | ||
120 | /// just fetches procedural ones. | ||
121 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<TokenExpander>>; | ||
122 | |||
123 | /// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory) | ||
124 | fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; | ||
125 | /// Special case of the previous query for procedural macros. We can't LRU | ||
126 | /// proc macros, since they are not deterministic in general, and | ||
127 | /// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng | ||
128 | /// heroically debugged this once! | ||
106 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; | 129 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; |
130 | /// Firewall query that returns the error from the `macro_expand` query. | ||
131 | fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>; | ||
107 | 132 | ||
108 | fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>; | 133 | fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>; |
109 | } | 134 | } |
@@ -115,36 +140,159 @@ pub trait AstDatabase: SourceDatabase { | |||
115 | pub fn expand_hypothetical( | 140 | pub fn expand_hypothetical( |
116 | db: &dyn AstDatabase, | 141 | db: &dyn AstDatabase, |
117 | actual_macro_call: MacroCallId, | 142 | actual_macro_call: MacroCallId, |
118 | hypothetical_args: &syntax::ast::TokenTree, | 143 | hypothetical_args: &ast::TokenTree, |
119 | token_to_map: syntax::SyntaxToken, | 144 | token_to_map: SyntaxToken, |
120 | ) -> Option<(SyntaxNode, syntax::SyntaxToken)> { | 145 | ) -> Option<(SyntaxNode, SyntaxToken)> { |
121 | let macro_file = MacroFile { macro_call_id: actual_macro_call }; | ||
122 | let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()); | 146 | let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()); |
123 | let range = | 147 | let range = |
124 | token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?; | 148 | token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?; |
125 | let token_id = tmap_1.token_by_range(range)?; | 149 | let token_id = tmap_1.token_by_range(range)?; |
126 | let macro_def = expander(db, actual_macro_call)?; | 150 | |
151 | let lazy_id = match actual_macro_call { | ||
152 | MacroCallId::LazyMacro(id) => id, | ||
153 | MacroCallId::EagerMacro(_) => return None, | ||
154 | }; | ||
155 | |||
156 | let macro_def = { | ||
157 | let loc = db.lookup_intern_macro(lazy_id); | ||
158 | db.macro_def(loc.def)? | ||
159 | }; | ||
160 | |||
161 | let hypothetical_expansion = macro_def.expand(db, lazy_id, &tt); | ||
162 | |||
163 | let fragment_kind = to_fragment_kind(db, actual_macro_call); | ||
164 | |||
127 | let (node, tmap_2) = | 165 | let (node, tmap_2) = |
128 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?; | 166 | mbe::token_tree_to_syntax_node(&hypothetical_expansion.value, fragment_kind).ok()?; |
129 | let token_id = macro_def.0.map_id_down(token_id); | 167 | |
168 | let token_id = macro_def.map_id_down(token_id); | ||
130 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; | 169 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; |
131 | let token = node.syntax_node().covering_element(range).into_token()?; | 170 | let token = node.syntax_node().covering_element(range).into_token()?; |
132 | Some((node.syntax_node(), token)) | 171 | Some((node.syntax_node(), token)) |
133 | } | 172 | } |
134 | 173 | ||
135 | fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 174 | fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
136 | let map = | 175 | let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default(); |
137 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); | ||
138 | Arc::new(map) | 176 | Arc::new(map) |
139 | } | 177 | } |
140 | 178 | ||
141 | fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | 179 | fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { |
180 | match file_id.0 { | ||
181 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | ||
182 | HirFileIdRepr::MacroFile(macro_file) => { | ||
183 | db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node()) | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | fn parse_macro_expansion( | ||
189 | db: &dyn AstDatabase, | ||
190 | macro_file: MacroFile, | ||
191 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { | ||
192 | let _p = profile::span("parse_macro_expansion"); | ||
193 | let result = db.macro_expand(macro_file.macro_call_id); | ||
194 | |||
195 | if let Some(err) = &result.err { | ||
196 | // Note: | ||
197 | // The final goal we would like to make all parse_macro success, | ||
198 | // such that the following log will not call anyway. | ||
199 | match macro_file.macro_call_id { | ||
200 | MacroCallId::LazyMacro(id) => { | ||
201 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | ||
202 | let node = loc.kind.node(db); | ||
203 | |||
204 | // collect parent information for warning log | ||
205 | let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| { | ||
206 | it.file_id.call_node(db) | ||
207 | }) | ||
208 | .map(|n| format!("{:#}", n.value)) | ||
209 | .collect::<Vec<_>>() | ||
210 | .join("\n"); | ||
211 | |||
212 | log::warn!( | ||
213 | "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}", | ||
214 | err, | ||
215 | node.value, | ||
216 | parents | ||
217 | ); | ||
218 | } | ||
219 | _ => { | ||
220 | log::warn!("fail on macro_parse: (reason: {:?})", err); | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | let tt = match result.value { | ||
225 | Some(tt) => tt, | ||
226 | None => return ExpandResult { value: None, err: result.err }, | ||
227 | }; | ||
228 | |||
229 | let fragment_kind = to_fragment_kind(db, macro_file.macro_call_id); | ||
230 | |||
231 | log::debug!("expanded = {}", tt.as_debug_string()); | ||
232 | log::debug!("kind = {:?}", fragment_kind); | ||
233 | |||
234 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { | ||
235 | Ok(it) => it, | ||
236 | Err(err) => { | ||
237 | log::debug!( | ||
238 | "failed to parse expanstion to {:?} = {}", | ||
239 | fragment_kind, | ||
240 | tt.as_debug_string() | ||
241 | ); | ||
242 | return ExpandResult::only_err(err); | ||
243 | } | ||
244 | }; | ||
245 | |||
246 | match result.err { | ||
247 | Some(err) => { | ||
248 | // Safety check for recursive identity macro. | ||
249 | let node = parse.syntax_node(); | ||
250 | let file: HirFileId = macro_file.into(); | ||
251 | let call_node = match file.call_node(db) { | ||
252 | Some(it) => it, | ||
253 | None => { | ||
254 | return ExpandResult::only_err(err); | ||
255 | } | ||
256 | }; | ||
257 | if is_self_replicating(&node, &call_node.value) { | ||
258 | return ExpandResult::only_err(err); | ||
259 | } else { | ||
260 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } | ||
261 | } | ||
262 | } | ||
263 | None => { | ||
264 | log::debug!("parse = {:?}", parse.syntax_node().kind()); | ||
265 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None } | ||
266 | } | ||
267 | } | ||
268 | } | ||
269 | |||
270 | fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | ||
271 | let arg = db.macro_arg_text(id)?; | ||
272 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg)); | ||
273 | Some(Arc::new((tt, tmap))) | ||
274 | } | ||
275 | |||
276 | fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | ||
277 | let id = match id { | ||
278 | MacroCallId::LazyMacro(id) => id, | ||
279 | MacroCallId::EagerMacro(_id) => { | ||
280 | // FIXME: support macro_arg for eager macro | ||
281 | return None; | ||
282 | } | ||
283 | }; | ||
284 | let loc = db.lookup_intern_macro(id); | ||
285 | let arg = loc.kind.arg(db)?; | ||
286 | Some(arg.green()) | ||
287 | } | ||
288 | |||
289 | fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>> { | ||
142 | match id.kind { | 290 | match id.kind { |
143 | MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) { | 291 | MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) { |
144 | syntax::ast::Macro::MacroRules(macro_rules) => { | 292 | ast::Macro::MacroRules(macro_rules) => { |
145 | let arg = macro_rules.token_tree()?; | 293 | let arg = macro_rules.token_tree()?; |
146 | let (tt, tmap) = mbe::ast_to_token_tree(&arg); | 294 | let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg); |
147 | let rules = match MacroRules::parse(&tt) { | 295 | let mac = match mbe::MacroRules::parse(&tt) { |
148 | Ok(it) => it, | 296 | Ok(it) => it, |
149 | Err(err) => { | 297 | Err(err) => { |
150 | let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); | 298 | let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default(); |
@@ -152,12 +300,12 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, | |||
152 | return None; | 300 | return None; |
153 | } | 301 | } |
154 | }; | 302 | }; |
155 | Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) | 303 | Some(Arc::new(TokenExpander::MacroRules { mac, def_site_token_map })) |
156 | } | 304 | } |
157 | syntax::ast::Macro::MacroDef(macro_def) => { | 305 | ast::Macro::MacroDef(macro_def) => { |
158 | let arg = macro_def.body()?; | 306 | let arg = macro_def.body()?; |
159 | let (tt, tmap) = mbe::ast_to_token_tree(&arg); | 307 | let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg); |
160 | let rules = match MacroDef::parse(&tt) { | 308 | let mac = match mbe::MacroDef::parse(&tt) { |
161 | Ok(it) => it, | 309 | Ok(it) => it, |
162 | Err(err) => { | 310 | Err(err) => { |
163 | let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default(); | 311 | let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default(); |
@@ -165,41 +313,18 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, | |||
165 | return None; | 313 | return None; |
166 | } | 314 | } |
167 | }; | 315 | }; |
168 | Some(Arc::new((TokenExpander::MacroDef(rules), tmap))) | 316 | Some(Arc::new(TokenExpander::MacroDef { mac, def_site_token_map })) |
169 | } | 317 | } |
170 | }, | 318 | }, |
171 | MacroDefKind::BuiltIn(expander, _) => { | 319 | MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))), |
172 | Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default()))) | ||
173 | } | ||
174 | MacroDefKind::BuiltInDerive(expander, _) => { | 320 | MacroDefKind::BuiltInDerive(expander, _) => { |
175 | Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) | 321 | Some(Arc::new(TokenExpander::BuiltinDerive(expander))) |
176 | } | 322 | } |
177 | MacroDefKind::BuiltInEager(..) => None, | 323 | MacroDefKind::BuiltInEager(..) => None, |
178 | MacroDefKind::ProcMacro(expander, ..) => { | 324 | MacroDefKind::ProcMacro(expander, ..) => Some(Arc::new(TokenExpander::ProcMacro(expander))), |
179 | Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default()))) | ||
180 | } | ||
181 | } | 325 | } |
182 | } | 326 | } |
183 | 327 | ||
184 | fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | ||
185 | let id = match id { | ||
186 | MacroCallId::LazyMacro(id) => id, | ||
187 | MacroCallId::EagerMacro(_id) => { | ||
188 | // FIXME: support macro_arg for eager macro | ||
189 | return None; | ||
190 | } | ||
191 | }; | ||
192 | let loc = db.lookup_intern_macro(id); | ||
193 | let arg = loc.kind.arg(db)?; | ||
194 | Some(arg.green()) | ||
195 | } | ||
196 | |||
197 | fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | ||
198 | let arg = db.macro_arg_text(id)?; | ||
199 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg)); | ||
200 | Some(Arc::new((tt, tmap))) | ||
201 | } | ||
202 | |||
203 | fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>> { | 328 | fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>> { |
204 | macro_expand_with_arg(db, id, None) | 329 | macro_expand_with_arg(db, id, None) |
205 | } | 330 | } |
@@ -208,19 +333,6 @@ fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<E | |||
208 | db.macro_expand(macro_call).err | 333 | db.macro_expand(macro_call).err |
209 | } | 334 | } |
210 | 335 | ||
211 | fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
212 | let lazy_id = match id { | ||
213 | MacroCallId::LazyMacro(id) => id, | ||
214 | MacroCallId::EagerMacro(_id) => { | ||
215 | return None; | ||
216 | } | ||
217 | }; | ||
218 | |||
219 | let loc = db.lookup_intern_macro(lazy_id); | ||
220 | let macro_rules = db.macro_def(loc.def)?; | ||
221 | Some(macro_rules) | ||
222 | } | ||
223 | |||
224 | fn macro_expand_with_arg( | 336 | fn macro_expand_with_arg( |
225 | db: &dyn AstDatabase, | 337 | db: &dyn AstDatabase, |
226 | id: MacroCallId, | 338 | id: MacroCallId, |
@@ -254,7 +366,7 @@ fn macro_expand_with_arg( | |||
254 | Some(it) => it, | 366 | Some(it) => it, |
255 | None => return ExpandResult::str_err("Fail to find macro definition".into()), | 367 | None => return ExpandResult::str_err("Fail to find macro definition".into()), |
256 | }; | 368 | }; |
257 | let ExpandResult { value: tt, err } = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); | 369 | let ExpandResult { value: tt, err } = macro_rules.expand(db, lazy_id, ¯o_arg.0); |
258 | // Set a hard limit for the expanded tt | 370 | // Set a hard limit for the expanded tt |
259 | let count = tt.count(); | 371 | let count = tt.count(); |
260 | if count > TOKEN_LIMIT { | 372 | if count > TOKEN_LIMIT { |
@@ -294,116 +406,11 @@ fn expand_proc_macro( | |||
294 | expander.expand(db, loc.krate, ¯o_arg.0) | 406 | expander.expand(db, loc.krate, ¯o_arg.0) |
295 | } | 407 | } |
296 | 408 | ||
297 | fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | ||
298 | match file_id.0 { | ||
299 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | ||
300 | HirFileIdRepr::MacroFile(macro_file) => { | ||
301 | db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node()) | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | fn parse_macro_expansion( | ||
307 | db: &dyn AstDatabase, | ||
308 | macro_file: MacroFile, | ||
309 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { | ||
310 | parse_macro_with_arg(db, macro_file, None) | ||
311 | } | ||
312 | |||
313 | fn parse_macro_with_arg( | ||
314 | db: &dyn AstDatabase, | ||
315 | macro_file: MacroFile, | ||
316 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
317 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { | ||
318 | let macro_call_id = macro_file.macro_call_id; | ||
319 | let result = if let Some(arg) = arg { | ||
320 | macro_expand_with_arg(db, macro_call_id, Some(arg)) | ||
321 | } else { | ||
322 | db.macro_expand(macro_call_id) | ||
323 | }; | ||
324 | |||
325 | let _p = profile::span("parse_macro_expansion"); | ||
326 | |||
327 | if let Some(err) = &result.err { | ||
328 | // Note: | ||
329 | // The final goal we would like to make all parse_macro success, | ||
330 | // such that the following log will not call anyway. | ||
331 | match macro_call_id { | ||
332 | MacroCallId::LazyMacro(id) => { | ||
333 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | ||
334 | let node = loc.kind.node(db); | ||
335 | |||
336 | // collect parent information for warning log | ||
337 | let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| { | ||
338 | it.file_id.call_node(db) | ||
339 | }) | ||
340 | .map(|n| format!("{:#}", n.value)) | ||
341 | .collect::<Vec<_>>() | ||
342 | .join("\n"); | ||
343 | |||
344 | log::warn!( | ||
345 | "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}", | ||
346 | err, | ||
347 | node.value, | ||
348 | parents | ||
349 | ); | ||
350 | } | ||
351 | _ => { | ||
352 | log::warn!("fail on macro_parse: (reason: {:?})", err); | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | let tt = match result.value { | ||
357 | Some(tt) => tt, | ||
358 | None => return ExpandResult { value: None, err: result.err }, | ||
359 | }; | ||
360 | |||
361 | let fragment_kind = to_fragment_kind(db, macro_call_id); | ||
362 | |||
363 | log::debug!("expanded = {}", tt.as_debug_string()); | ||
364 | log::debug!("kind = {:?}", fragment_kind); | ||
365 | |||
366 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { | ||
367 | Ok(it) => it, | ||
368 | Err(err) => { | ||
369 | log::debug!( | ||
370 | "failed to parse expanstion to {:?} = {}", | ||
371 | fragment_kind, | ||
372 | tt.as_debug_string() | ||
373 | ); | ||
374 | return ExpandResult::only_err(err); | ||
375 | } | ||
376 | }; | ||
377 | |||
378 | match result.err { | ||
379 | Some(err) => { | ||
380 | // Safety check for recursive identity macro. | ||
381 | let node = parse.syntax_node(); | ||
382 | let file: HirFileId = macro_file.into(); | ||
383 | let call_node = match file.call_node(db) { | ||
384 | Some(it) => it, | ||
385 | None => { | ||
386 | return ExpandResult::only_err(err); | ||
387 | } | ||
388 | }; | ||
389 | if is_self_replicating(&node, &call_node.value) { | ||
390 | return ExpandResult::only_err(err); | ||
391 | } else { | ||
392 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } | ||
393 | } | ||
394 | } | ||
395 | None => { | ||
396 | log::debug!("parse = {:?}", parse.syntax_node().kind()); | ||
397 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None } | ||
398 | } | ||
399 | } | ||
400 | } | ||
401 | |||
402 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { | 409 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { |
403 | if diff(from, to).is_empty() { | 410 | if diff(from, to).is_empty() { |
404 | return true; | 411 | return true; |
405 | } | 412 | } |
406 | if let Some(stmts) = MacroStmts::cast(from.clone()) { | 413 | if let Some(stmts) = ast::MacroStmts::cast(from.clone()) { |
407 | if stmts.statements().any(|stmt| diff(stmt.syntax(), to).is_empty()) { | 414 | if stmts.statements().any(|stmt| diff(stmt.syntax(), to).is_empty()) { |
408 | return true; | 415 | return true; |
409 | } | 416 | } |
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 779725629..ed61ebca3 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs | |||
@@ -5,6 +5,7 @@ | |||
5 | use std::sync::Arc; | 5 | use std::sync::Arc; |
6 | 6 | ||
7 | use base_db::CrateId; | 7 | use base_db::CrateId; |
8 | use db::TokenExpander; | ||
8 | use either::Either; | 9 | use either::Either; |
9 | use mbe::Origin; | 10 | use mbe::Origin; |
10 | use parser::SyntaxKind; | 11 | use parser::SyntaxKind; |
@@ -115,7 +116,7 @@ struct HygieneInfo { | |||
115 | /// The `macro_rules!` arguments. | 116 | /// The `macro_rules!` arguments. |
116 | def_start: Option<InFile<TextSize>>, | 117 | def_start: Option<InFile<TextSize>>, |
117 | 118 | ||
118 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | 119 | macro_def: Arc<TokenExpander>, |
119 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, | 120 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, |
120 | exp_map: Arc<mbe::TokenMap>, | 121 | exp_map: Arc<mbe::TokenMap>, |
121 | } | 122 | } |
@@ -124,13 +125,16 @@ impl HygieneInfo { | |||
124 | fn map_ident_up(&self, token: TextRange) -> Option<(InFile<TextRange>, Origin)> { | 125 | fn map_ident_up(&self, token: TextRange) -> Option<(InFile<TextRange>, Origin)> { |
125 | let token_id = self.exp_map.token_by_range(token)?; | 126 | let token_id = self.exp_map.token_by_range(token)?; |
126 | 127 | ||
127 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 128 | let (token_id, origin) = self.macro_def.map_id_up(token_id); |
128 | let (token_map, tt) = match origin { | 129 | let (token_map, tt) = match origin { |
129 | mbe::Origin::Call => (&self.macro_arg.1, self.arg_start), | 130 | mbe::Origin::Call => (&self.macro_arg.1, self.arg_start), |
130 | mbe::Origin::Def => ( | 131 | mbe::Origin::Def => match (&*self.macro_def, self.def_start) { |
131 | &self.macro_def.1, | 132 | (TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt)) |
132 | *self.def_start.as_ref().expect("`Origin::Def` used with non-`macro_rules!` macro"), | 133 | | (TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt)) => { |
133 | ), | 134 | (def_site_token_map, tt) |
135 | } | ||
136 | _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"), | ||
137 | }, | ||
134 | }; | 138 | }; |
135 | 139 | ||
136 | let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?; | 140 | let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?; |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index a0e6aec62..0402640de 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -351,7 +351,7 @@ pub struct ExpansionInfo { | |||
351 | /// The `macro_rules!` arguments. | 351 | /// The `macro_rules!` arguments. |
352 | def: Option<InFile<ast::TokenTree>>, | 352 | def: Option<InFile<ast::TokenTree>>, |
353 | 353 | ||
354 | macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>, | 354 | macro_def: Arc<db::TokenExpander>, |
355 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, | 355 | macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>, |
356 | exp_map: Arc<mbe::TokenMap>, | 356 | exp_map: Arc<mbe::TokenMap>, |
357 | } | 357 | } |
@@ -368,7 +368,7 @@ impl ExpansionInfo { | |||
368 | assert_eq!(token.file_id, self.arg.file_id); | 368 | assert_eq!(token.file_id, self.arg.file_id); |
369 | let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; | 369 | let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; |
370 | let token_id = self.macro_arg.1.token_by_range(range)?; | 370 | let token_id = self.macro_arg.1.token_by_range(range)?; |
371 | let token_id = self.macro_def.0.map_id_down(token_id); | 371 | let token_id = self.macro_def.map_id_down(token_id); |
372 | 372 | ||
373 | let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?; | 373 | let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |
374 | 374 | ||
@@ -383,17 +383,16 @@ impl ExpansionInfo { | |||
383 | ) -> Option<(InFile<SyntaxToken>, Origin)> { | 383 | ) -> Option<(InFile<SyntaxToken>, Origin)> { |
384 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; | 384 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; |
385 | 385 | ||
386 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 386 | let (token_id, origin) = self.macro_def.map_id_up(token_id); |
387 | let (token_map, tt) = match origin { | 387 | let (token_map, tt) = match origin { |
388 | mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), | 388 | mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()), |
389 | mbe::Origin::Def => ( | 389 | mbe::Origin::Def => match (&*self.macro_def, self.def.as_ref()) { |
390 | &self.macro_def.1, | 390 | (db::TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt)) |
391 | self.def | 391 | | (db::TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt)) => { |
392 | .as_ref() | 392 | (def_site_token_map, tt.as_ref().map(|tt| tt.syntax().clone())) |
393 | .expect("`Origin::Def` used with non-`macro_rules!` macro") | 393 | } |
394 | .as_ref() | 394 | _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"), |
395 | .map(|tt| tt.syntax().clone()), | 395 | }, |
396 | ), | ||
397 | }; | 396 | }; |
398 | 397 | ||
399 | let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; | 398 | let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; |