aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_mbe/src/mbe_expander.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander.rs')
-rw-r--r--crates/ra_mbe/src/mbe_expander.rs61
1 files changed, 45 insertions, 16 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs
index b455b7321..b1eacf124 100644
--- a/crates/ra_mbe/src/mbe_expander.rs
+++ b/crates/ra_mbe/src/mbe_expander.rs
@@ -8,19 +8,51 @@ mod transcriber;
8use ra_syntax::SmolStr; 8use ra_syntax::SmolStr;
9use rustc_hash::FxHashMap; 9use rustc_hash::FxHashMap;
10 10
11use crate::ExpandError; 11use crate::{ExpandError, ExpandResult};
12 12
13pub(crate) fn expand( 13pub(crate) fn expand(rules: &crate::MacroRules, input: &tt::Subtree) -> ExpandResult<tt::Subtree> {
14 rules: &crate::MacroRules, 14 expand_rules(&rules.rules, input)
15 input: &tt::Subtree,
16) -> Result<tt::Subtree, ExpandError> {
17 rules.rules.iter().find_map(|it| expand_rule(it, input).ok()).ok_or(ExpandError::NoMatchingRule)
18} 15}
19 16
20fn expand_rule(rule: &crate::Rule, input: &tt::Subtree) -> Result<tt::Subtree, ExpandError> { 17fn expand_rules(rules: &[crate::Rule], input: &tt::Subtree) -> ExpandResult<tt::Subtree> {
21 let bindings = matcher::match_(&rule.lhs, input)?; 18 let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
22 let res = transcriber::transcribe(&rule.rhs, &bindings)?; 19 for rule in rules {
23 Ok(res) 20 let new_match = match matcher::match_(&rule.lhs, input) {
21 Ok(m) => m,
22 Err(_e) => {
23 // error in pattern parsing
24 continue;
25 }
26 };
27 if new_match.err.is_none() {
28 // If we find a rule that applies without errors, we're done.
29 // Unconditionally returning the transcription here makes the
30 // `test_repeat_bad_var` test fail.
31 let ExpandResult(res, transcribe_err) =
32 transcriber::transcribe(&rule.rhs, &new_match.bindings);
33 if transcribe_err.is_none() {
34 return ExpandResult::ok(res);
35 }
36 }
37 // Use the rule if we matched more tokens, or had fewer errors
38 if let Some((prev_match, _)) = &match_ {
39 if (new_match.unmatched_tts, new_match.err_count)
40 < (prev_match.unmatched_tts, prev_match.err_count)
41 {
42 match_ = Some((new_match, rule));
43 }
44 } else {
45 match_ = Some((new_match, rule));
46 }
47 }
48 if let Some((match_, rule)) = match_ {
49 // if we got here, there was no match without errors
50 let ExpandResult(result, transcribe_err) =
51 transcriber::transcribe(&rule.rhs, &match_.bindings);
52 ExpandResult(result, match_.err.or(transcribe_err))
53 } else {
54 ExpandResult(tt::Subtree::default(), Some(ExpandError::NoMatchingRule))
55 }
24} 56}
25 57
26/// The actual algorithm for expansion is not too hard, but is pretty tricky. 58/// The actual algorithm for expansion is not too hard, but is pretty tricky.
@@ -111,7 +143,7 @@ mod tests {
111 } 143 }
112 144
113 fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) { 145 fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) {
114 assert_eq!(expand_first(&create_rules(&format_macro(macro_body)), invocation), Err(err)); 146 assert_eq!(expand_first(&create_rules(&format_macro(macro_body)), invocation).1, Some(err));
115 } 147 }
116 148
117 fn format_macro(macro_body: &str) -> String { 149 fn format_macro(macro_body: &str) -> String {
@@ -135,10 +167,7 @@ mod tests {
135 crate::MacroRules::parse(&definition_tt).unwrap() 167 crate::MacroRules::parse(&definition_tt).unwrap()
136 } 168 }
137 169
138 fn expand_first( 170 fn expand_first(rules: &crate::MacroRules, invocation: &str) -> ExpandResult<tt::Subtree> {
139 rules: &crate::MacroRules,
140 invocation: &str,
141 ) -> Result<tt::Subtree, ExpandError> {
142 let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); 171 let source_file = ast::SourceFile::parse(invocation).ok().unwrap();
143 let macro_invocation = 172 let macro_invocation =
144 source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); 173 source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
@@ -146,6 +175,6 @@ mod tests {
146 let (invocation_tt, _) = 175 let (invocation_tt, _) =
147 ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap(); 176 ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap();
148 177
149 expand_rule(&rules.rules[0], &invocation_tt) 178 expand_rules(&rules.rules, &invocation_tt)
150 } 179 }
151} 180}