diff options
Diffstat (limited to 'crates/ra_mbe')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 73 |
1 files changed, 69 insertions, 4 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index d4ce3bfe3..2dd97b665 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -85,12 +85,12 @@ impl Bindings { | |||
85 | let mut b = self | 85 | let mut b = self |
86 | .inner | 86 | .inner |
87 | .get(name) | 87 | .get(name) |
88 | .ok_or(ExpandError::BindingError(format!("could not find binding {}", name)))?; | 88 | .ok_or(ExpandError::BindingError(format!("could not find binding `{}`", name)))?; |
89 | for &idx in nesting.iter() { | 89 | for &idx in nesting.iter() { |
90 | b = match b { | 90 | b = match b { |
91 | Binding::Simple(_) => break, | 91 | Binding::Simple(_) => break, |
92 | Binding::Nested(bs) => bs.get(idx).ok_or(ExpandError::BindingError(format!( | 92 | Binding::Nested(bs) => bs.get(idx).ok_or(ExpandError::BindingError(format!( |
93 | "could not find nested binding {}", | 93 | "could not find nested binding `{}`", |
94 | name | 94 | name |
95 | )))?, | 95 | )))?, |
96 | }; | 96 | }; |
@@ -98,7 +98,7 @@ impl Bindings { | |||
98 | match b { | 98 | match b { |
99 | Binding::Simple(it) => Ok(it), | 99 | Binding::Simple(it) => Ok(it), |
100 | Binding::Nested(_) => Err(ExpandError::BindingError(format!( | 100 | Binding::Nested(_) => Err(ExpandError::BindingError(format!( |
101 | "expected simple binding, found nested binding {}", | 101 | "expected simple binding, found nested binding `{}`", |
102 | name | 102 | name |
103 | ))), | 103 | ))), |
104 | } | 104 | } |
@@ -113,7 +113,7 @@ impl Bindings { | |||
113 | Some(Binding::Nested(it)) => it.push(value), | 113 | Some(Binding::Nested(it)) => it.push(value), |
114 | _ => { | 114 | _ => { |
115 | return Err(ExpandError::BindingError(format!( | 115 | return Err(ExpandError::BindingError(format!( |
116 | "nested binding for {} not found", | 116 | "could not find binding `{}`", |
117 | key | 117 | key |
118 | ))); | 118 | ))); |
119 | } | 119 | } |
@@ -216,3 +216,68 @@ fn expand_tt( | |||
216 | }; | 216 | }; |
217 | Ok(res) | 217 | Ok(res) |
218 | } | 218 | } |
219 | |||
220 | #[cfg(test)] | ||
221 | mod tests { | ||
222 | use ra_syntax::{ast, AstNode}; | ||
223 | |||
224 | use super::*; | ||
225 | use crate::ast_to_token_tree; | ||
226 | |||
227 | #[test] | ||
228 | fn test_expand_rule() { | ||
229 | assert_err( | ||
230 | "($i:ident) => ($j)", | ||
231 | "foo!{a}", | ||
232 | ExpandError::BindingError(String::from("could not find binding `j`")), | ||
233 | ); | ||
234 | |||
235 | assert_err( | ||
236 | "($($i:ident);*) => ($i)", | ||
237 | "foo!{a}", | ||
238 | ExpandError::BindingError(String::from( | ||
239 | "expected simple binding, found nested binding `i`", | ||
240 | )), | ||
241 | ); | ||
242 | |||
243 | assert_err("($i) => ($i)", "foo!{a}", ExpandError::UnexpectedToken); | ||
244 | assert_err("($i:) => ($i)", "foo!{a}", ExpandError::UnexpectedToken); | ||
245 | } | ||
246 | |||
247 | fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) { | ||
248 | assert_eq!(expand_first(&create_rules(&format_macro(macro_body)), invocation), Err(err)); | ||
249 | } | ||
250 | |||
251 | fn format_macro(macro_body: &str) -> String { | ||
252 | format!( | ||
253 | " | ||
254 | macro_rules! foo {{ | ||
255 | {} | ||
256 | }} | ||
257 | ", | ||
258 | macro_body | ||
259 | ) | ||
260 | } | ||
261 | |||
262 | fn create_rules(macro_definition: &str) -> crate::MacroRules { | ||
263 | let source_file = ast::SourceFile::parse(macro_definition); | ||
264 | let macro_definition = | ||
265 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
266 | |||
267 | let (definition_tt, _) = ast_to_token_tree(macro_definition.token_tree().unwrap()).unwrap(); | ||
268 | crate::MacroRules::parse(&definition_tt).unwrap() | ||
269 | } | ||
270 | |||
271 | fn expand_first( | ||
272 | rules: &crate::MacroRules, | ||
273 | invocation: &str, | ||
274 | ) -> Result<tt::Subtree, ExpandError> { | ||
275 | let source_file = ast::SourceFile::parse(invocation); | ||
276 | let macro_invocation = | ||
277 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
278 | |||
279 | let (invocation_tt, _) = ast_to_token_tree(macro_invocation.token_tree().unwrap()).unwrap(); | ||
280 | |||
281 | expand_rule(&rules.rules[0], &invocation_tt) | ||
282 | } | ||
283 | } | ||