diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-17 09:41:30 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-17 09:41:30 +0000 |
commit | 6aa432d86b7e4fb691600032ebdf6f2301152447 (patch) | |
tree | aa012212c11e43a95547f553b5116922631f9896 /crates/ra_mbe/src/mbe_expander.rs | |
parent | cf4ae9aa591729dde25a7df3fa5c22ca0fd94145 (diff) | |
parent | 6c20d7e979b967eb20207414c0a0bf875bbcb98d (diff) |
Merge #3580
3580: More error-resilient MBE expansion r=matklad a=flodiebold
This is the beginning of an attempt to make macro-by-example expansion more resilient, so that we still get an expansion even if no rule exactly matches, with the goal to make completion work better in macro calls.
The general idea is to make everything return `(T, Option<ExpandError>)` instead of `Result<T, ExpandError>`; and then to try each macro arm in turn, and somehow choose the 'best' matching rule if none matches without errors. Finding that 'best' match isn't done yet; I'm currently counting how many tokens were consumed from the args before an error, but it also needs to take into account whether there were further patterns that had nothing to match.
I'll continue this later, but I'm interested whether you think this is the right path, @matklad & @edwin0cheng.
Co-authored-by: Florian Diebold <[email protected]>
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander.rs')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 61 |
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; | |||
8 | use ra_syntax::SmolStr; | 8 | use ra_syntax::SmolStr; |
9 | use rustc_hash::FxHashMap; | 9 | use rustc_hash::FxHashMap; |
10 | 10 | ||
11 | use crate::ExpandError; | 11 | use crate::{ExpandError, ExpandResult}; |
12 | 12 | ||
13 | pub(crate) fn expand( | 13 | pub(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 | ||
20 | fn expand_rule(rule: &crate::Rule, input: &tt::Subtree) -> Result<tt::Subtree, ExpandError> { | 17 | fn 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(¯o_invocation.token_tree().unwrap()).unwrap(); | 176 | ast_to_token_tree(¯o_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 | } |