This commit is contained in:
Florian Diebold 2020-03-14 20:24:18 +01:00
parent 6305d094ac
commit 0f3a54dd4d
5 changed files with 160 additions and 94 deletions

View file

@ -31,8 +31,12 @@ impl TokenExpander {
match self { match self {
TokenExpander::MacroRules(it) => it.expand(tt), TokenExpander::MacroRules(it) => it.expand(tt),
// FIXME switch these to ExpandResult as well // FIXME switch these to ExpandResult as well
TokenExpander::Builtin(it) => it.expand(db, id, tt).map_or_else(|e| (tt::Subtree::default(), Some(e)), |r| (r, None)), TokenExpander::Builtin(it) => it
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).map_or_else(|e| (tt::Subtree::default(), Some(e)), |r| (r, None)), .expand(db, id, tt)
.map_or_else(|e| (tt::Subtree::default(), Some(e)), |r| (r, None)),
TokenExpander::BuiltinDerive(it) => it
.expand(db, id, tt)
.map_or_else(|e| (tt::Subtree::default(), Some(e)), |r| (r, None)),
} }
} }
@ -182,7 +186,7 @@ fn macro_expand_with_arg(
if arg.is_some() { if arg.is_some() {
return ( return (
None, None,
Some("hypothetical macro expansion not implemented for eager macro".to_owned()) Some("hypothetical macro expansion not implemented for eager macro".to_owned()),
); );
} else { } else {
return (Some(db.lookup_intern_eager_expansion(id).subtree), None); return (Some(db.lookup_intern_eager_expansion(id).subtree), None);

View file

@ -777,8 +777,8 @@ mod tests {
[ [
CompletionItem { CompletionItem {
label: "the_field", label: "the_field",
source_range: [552; 553), source_range: [552; 552),
delete: [552; 553), delete: [552; 552),
insert: "the_field", insert: "the_field",
kind: Field, kind: Field,
detail: "u32", detail: "u32",

View file

@ -89,7 +89,6 @@ mod tests {
#[test] #[test]
fn completes_in_simple_macro_call() { fn completes_in_simple_macro_call() {
// FIXME: doesn't work yet because of missing error recovery in macro expansion
let completions = complete( let completions = complete(
r" r"
macro_rules! m { ($e:expr) => { $e } } macro_rules! m { ($e:expr) => { $e } }
@ -102,6 +101,16 @@ mod tests {
} }
", ",
); );
assert_debug_snapshot!(completions, @r###"[]"###); assert_debug_snapshot!(completions, @r###"
[
CompletionItem {
label: "E",
source_range: [151; 151),
delete: [151; 151),
insert: "E",
kind: Enum,
},
]
"###);
} }
} }

View file

@ -8,33 +8,44 @@ mod transcriber;
use ra_syntax::SmolStr; use ra_syntax::SmolStr;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::{ExpandResult, ExpandError}; use crate::{ExpandError, ExpandResult};
pub(crate) fn expand( pub(crate) fn expand(rules: &crate::MacroRules, input: &tt::Subtree) -> ExpandResult<tt::Subtree> {
rules: &crate::MacroRules, let (mut result, mut unmatched_tokens, mut unmatched_patterns, mut err) = (
input: &tt::Subtree, tt::Subtree::default(),
) -> ExpandResult<tt::Subtree> { usize::max_value(),
let (mut result, mut left_over, mut err) = (tt::Subtree::default(), usize::max_value(), Some(ExpandError::NoMatchingRule)); usize::max_value(),
Some(ExpandError::NoMatchingRule),
);
for rule in &rules.rules { for rule in &rules.rules {
let ((res, left), e) = expand_rule(rule, input); let ((res, tokens, patterns), e) = expand_rule(rule, input);
if e.is_none() { if e.is_none() {
// if we find a rule that applies without errors, we're done // if we find a rule that applies without errors, we're done
return (res, None); return (res, None);
} }
// use the rule if we matched more tokens // use the rule if we matched more tokens, or had fewer patterns left
if left < left_over { if tokens < unmatched_tokens || tokens == unmatched_tokens && patterns < unmatched_patterns
{
result = res; result = res;
err = e; err = e;
left_over = left; unmatched_tokens = tokens;
unmatched_patterns = patterns;
} }
} }
(result, err) (result, err)
} }
fn expand_rule(rule: &crate::Rule, input: &tt::Subtree) -> ExpandResult<(tt::Subtree, usize)> { fn expand_rule(
let ((bindings, left_over), bindings_err) = dbg!(matcher::match_(&rule.lhs, input)); rule: &crate::Rule,
let (res, transcribe_err) = dbg!(transcriber::transcribe(&rule.rhs, &bindings)); input: &tt::Subtree,
((res, left_over), bindings_err.or(transcribe_err)) ) -> ExpandResult<(tt::Subtree, usize, usize)> {
dbg!(&rule.lhs);
let (match_result, bindings_err) = dbg!(matcher::match_(&rule.lhs, input));
let (res, transcribe_err) = dbg!(transcriber::transcribe(&rule.rhs, &match_result.bindings));
(
(res, match_result.unmatched_tokens, match_result.unmatched_patterns),
bindings_err.or(transcribe_err),
)
} }
/// The actual algorithm for expansion is not too hard, but is pretty tricky. /// The actual algorithm for expansion is not too hard, but is pretty tricky.
@ -149,10 +160,7 @@ mod tests {
crate::MacroRules::parse(&definition_tt).unwrap() crate::MacroRules::parse(&definition_tt).unwrap()
} }
fn expand_first( fn expand_first(rules: &crate::MacroRules, invocation: &str) -> ExpandResult<tt::Subtree> {
rules: &crate::MacroRules,
invocation: &str,
) -> ExpandResult<tt::Subtree> {
let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); let source_file = ast::SourceFile::parse(invocation).ok().unwrap();
let macro_invocation = let macro_invocation =
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
@ -160,6 +168,7 @@ mod tests {
let (invocation_tt, _) = let (invocation_tt, _) =
ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap(); ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap();
expand_rule(&rules.rules[0], &invocation_tt) let expanded = expand_rule(&rules.rules[0], &invocation_tt);
((expanded.0).0, expanded.1)
} }
} }

View file

@ -8,10 +8,10 @@ use crate::{
ExpandError, ExpandError,
}; };
use super::ExpandResult;
use ra_parser::{FragmentKind::*, TreeSink}; use ra_parser::{FragmentKind::*, TreeSink};
use ra_syntax::{SmolStr, SyntaxKind}; use ra_syntax::{SmolStr, SyntaxKind};
use tt::buffer::{Cursor, TokenBuffer}; use tt::buffer::{Cursor, TokenBuffer};
use super::ExpandResult;
impl Bindings { impl Bindings {
fn push_optional(&mut self, name: &SmolStr) { fn push_optional(&mut self, name: &SmolStr) {
@ -59,36 +59,50 @@ macro_rules! err {
}; };
} }
macro_rules! bail { #[derive(Debug, Default)]
($($tt:tt)*) => { pub(super) struct Match {
return Err(err!($($tt)*)) pub bindings: Bindings,
}; pub unmatched_tokens: usize,
pub unmatched_patterns: usize,
} }
pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> ExpandResult<(Bindings, usize)> { pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> ExpandResult<Match> {
assert!(pattern.delimiter == None); assert!(pattern.delimiter == None);
let mut res = Bindings::default(); let mut res = Match::default();
let mut src = TtIter::new(src); let mut src = TtIter::new(src);
let mut err = match_subtree(&mut res, pattern, &mut src).err(); let mut err = match_subtree(&mut res, pattern, &mut src).err();
res.unmatched_tokens += src.len();
if src.len() > 0 && err.is_none() { if src.len() > 0 && err.is_none() {
err = Some(err!("leftover tokens")); err = Some(err!("leftover tokens"));
} }
((res, src.len()), err) (res, err)
} }
fn match_subtree( fn match_subtree(
bindings: &mut Bindings, res: &mut Match,
pattern: &tt::Subtree, pattern: &tt::Subtree,
src: &mut TtIter, src: &mut TtIter,
) -> Result<(), ExpandError> { ) -> Result<(), ExpandError> {
let mut result = Ok(());
for op in parse_pattern(pattern) { for op in parse_pattern(pattern) {
if result.is_err() {
// We're just going through the patterns to count how many we missed
res.unmatched_patterns += 1;
continue;
}
match op? { match op? {
Op::TokenTree(tt::TokenTree::Leaf(lhs)) => { Op::TokenTree(tt::TokenTree::Leaf(lhs)) => {
let rhs = src.expect_leaf().map_err(|()| err!("expected leaf: `{}`", lhs))?; let rhs = match src.expect_leaf() {
Ok(l) => l,
Err(()) => {
result = Err(err!("expected leaf: `{}`", lhs));
continue;
}
};
match (lhs, rhs) { match (lhs, rhs) {
( (
tt::Leaf::Punct(tt::Punct { char: lhs, .. }), tt::Leaf::Punct(tt::Punct { char: lhs, .. }),
@ -102,35 +116,54 @@ fn match_subtree(
tt::Leaf::Literal(tt::Literal { text: lhs, .. }), tt::Leaf::Literal(tt::Literal { text: lhs, .. }),
tt::Leaf::Literal(tt::Literal { text: rhs, .. }), tt::Leaf::Literal(tt::Literal { text: rhs, .. }),
) if lhs == rhs => (), ) if lhs == rhs => (),
_ => return Err(ExpandError::UnexpectedToken), _ => {
result = Err(ExpandError::UnexpectedToken);
}
} }
} }
Op::TokenTree(tt::TokenTree::Subtree(lhs)) => { Op::TokenTree(tt::TokenTree::Subtree(lhs)) => {
let rhs = src.expect_subtree().map_err(|()| err!("expected subtree"))?; let rhs = match src.expect_subtree() {
Ok(s) => s,
Err(()) => {
result = Err(err!("expected subtree"));
continue;
}
};
if lhs.delimiter_kind() != rhs.delimiter_kind() { if lhs.delimiter_kind() != rhs.delimiter_kind() {
bail!("mismatched delimiter") result = Err(err!("mismatched delimiter"));
continue;
} }
let mut src = TtIter::new(rhs); let mut src = TtIter::new(rhs);
match_subtree(bindings, lhs, &mut src)?; result = match_subtree(res, lhs, &mut src);
if src.len() > 0 { res.unmatched_tokens += src.len();
bail!("leftover tokens"); if src.len() > 0 && result.is_ok() {
result = Err(err!("leftover tokens"));
} }
} }
Op::Var { name, kind } => { Op::Var { name, kind } => {
let kind = kind.as_ref().ok_or(ExpandError::UnexpectedToken)?; let kind = match kind {
match match_meta_var(kind.as_str(), src)? { Some(k) => k,
None => {
result = Err(ExpandError::UnexpectedToken);
continue;
}
};
let (matched, match_err) = match_meta_var(kind.as_str(), src);
match matched {
Some(fragment) => { Some(fragment) => {
bindings.inner.insert(name.clone(), Binding::Fragment(fragment)); res.bindings.inner.insert(name.clone(), Binding::Fragment(fragment));
} }
None => bindings.push_optional(name), None if match_err.is_none() => res.bindings.push_optional(name),
_ => {}
} }
result = match_err.map_or(Ok(()), Err);
} }
Op::Repeat { subtree, kind, separator } => { Op::Repeat { subtree, kind, separator } => {
match_repeat(bindings, subtree, kind, separator, src)? result = match_repeat(res, subtree, kind, separator, src);
} }
} }
} }
Ok(()) result
} }
impl<'a> TtIter<'a> { impl<'a> TtIter<'a> {
@ -222,7 +255,7 @@ impl<'a> TtIter<'a> {
pub(crate) fn expect_fragment( pub(crate) fn expect_fragment(
&mut self, &mut self,
fragment_kind: ra_parser::FragmentKind, fragment_kind: ra_parser::FragmentKind,
) -> Result<tt::TokenTree, ()> { ) -> ExpandResult<tt::TokenTree> {
pub(crate) struct OffsetTokenSink<'a> { pub(crate) struct OffsetTokenSink<'a> {
pub(crate) cursor: Cursor<'a>, pub(crate) cursor: Cursor<'a>,
pub(crate) error: bool, pub(crate) error: bool,
@ -247,45 +280,47 @@ impl<'a> TtIter<'a> {
ra_parser::parse_fragment(&mut src, &mut sink, fragment_kind); ra_parser::parse_fragment(&mut src, &mut sink, fragment_kind);
let mut err = None;
if !sink.cursor.is_root() || sink.error { if !sink.cursor.is_root() || sink.error {
// FIXME better recovery in this case would help completion inside macros immensely err = Some(err!("expected {:?}", fragment_kind));
return Err(());
} }
let mut curr = buffer.begin(); let mut curr = buffer.begin();
let mut res = vec![]; let mut res = vec![];
if sink.cursor.is_root() {
while curr != sink.cursor { while curr != sink.cursor {
if let Some(token) = curr.token_tree() { if let Some(token) = curr.token_tree() {
res.push(token); res.push(token);
} }
curr = curr.bump(); curr = curr.bump();
} }
}
self.inner = self.inner.as_slice()[res.len()..].iter(); self.inner = self.inner.as_slice()[res.len()..].iter();
match res.len() { let res = match res.len() {
0 => Err(()), 1 => res[0].clone(),
1 => Ok(res[0].clone()), _ => tt::TokenTree::Subtree(tt::Subtree {
_ => Ok(tt::TokenTree::Subtree(tt::Subtree {
delimiter: None, delimiter: None,
token_trees: res.into_iter().cloned().collect(), token_trees: res.into_iter().cloned().collect(),
})), }),
} };
(res, err)
} }
pub(crate) fn eat_vis(&mut self) -> Option<tt::TokenTree> { pub(crate) fn eat_vis(&mut self) -> Option<tt::TokenTree> {
let mut fork = self.clone(); let mut fork = self.clone();
match fork.expect_fragment(Visibility) { match fork.expect_fragment(Visibility) {
Ok(tt) => { (tt, None) => {
*self = fork; *self = fork;
Some(tt) Some(tt)
} }
Err(()) => None, (_, Some(_)) => None,
} }
} }
} }
pub(super) fn match_repeat( pub(super) fn match_repeat(
bindings: &mut Bindings, res: &mut Match,
pattern: &tt::Subtree, pattern: &tt::Subtree,
kind: RepeatKind, kind: RepeatKind,
separator: Option<Separator>, separator: Option<Separator>,
@ -305,17 +340,23 @@ pub(super) fn match_repeat(
} }
} }
let mut nested = Bindings::default(); let mut nested = Match::default();
match match_subtree(&mut nested, pattern, &mut fork) { match match_subtree(&mut nested, pattern, &mut fork) {
Ok(()) => { Ok(()) => {
limit -= 1; limit -= 1;
if limit == 0 { if limit == 0 {
log::warn!("match_lhs excced in repeat pattern exceed limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", pattern, src, kind, separator); log::warn!(
"match_lhs exceeded repeat pattern limit => {:#?}\n{:#?}\n{:#?}\n{:#?}",
pattern,
src,
kind,
separator
);
break; break;
} }
*src = fork; *src = fork;
bindings.push_nested(counter, nested)?; res.bindings.push_nested(counter, nested.bindings)?;
counter += 1; counter += 1;
if counter == 1 { if counter == 1 {
if let RepeatKind::ZeroOrOne = kind { if let RepeatKind::ZeroOrOne = kind {
@ -334,7 +375,7 @@ pub(super) fn match_repeat(
let mut vars = Vec::new(); let mut vars = Vec::new();
collect_vars(&mut vars, pattern)?; collect_vars(&mut vars, pattern)?;
for var in vars { for var in vars {
bindings.push_empty(&var) res.bindings.push_empty(&var)
} }
} }
_ => (), _ => (),
@ -342,7 +383,7 @@ pub(super) fn match_repeat(
Ok(()) Ok(())
} }
fn match_meta_var(kind: &str, input: &mut TtIter) -> Result<Option<Fragment>, ExpandError> { fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragment>> {
let fragment = match kind { let fragment = match kind {
"path" => Path, "path" => Path,
"expr" => Expr, "expr" => Expr,
@ -353,34 +394,33 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> Result<Option<Fragment>, Ex
"meta" => MetaItem, "meta" => MetaItem,
"item" => Item, "item" => Item,
_ => { _ => {
let tt = match kind { let tt_result = match kind {
"ident" => { "ident" => input
let ident = input.expect_ident().map_err(|()| err!("expected ident"))?.clone(); .expect_ident()
tt::Leaf::from(ident).into() .map(|ident| Some(tt::Leaf::from(ident.clone()).into()))
} .map_err(|()| err!("expected ident")),
"tt" => input.expect_tt().map_err(|()| err!())?.clone(), "tt" => input.expect_tt().map(Some).map_err(|()| err!()),
"lifetime" => { "lifetime" => input
let ident = input.expect_lifetime().map_err(|()| err!())?; .expect_lifetime()
tt::Leaf::Ident(ident.clone()).into() .map(|ident| Some(tt::Leaf::Ident(ident.clone()).into()))
} .map_err(|()| err!("expected lifetime")),
"literal" => { "literal" => input
let literal = input.expect_literal().map_err(|()| err!())?.clone(); .expect_literal()
tt::Leaf::from(literal).into() .map(|literal| Some(tt::Leaf::from(literal.clone()).into()))
} .map_err(|()| err!()),
// `vis` is optional // `vis` is optional
"vis" => match input.eat_vis() { "vis" => match input.eat_vis() {
Some(vis) => vis, Some(vis) => Ok(Some(vis)),
None => return Ok(None), None => Ok(None),
}, },
_ => return Err(ExpandError::UnexpectedToken), _ => Err(ExpandError::UnexpectedToken),
}; };
return Ok(Some(Fragment::Tokens(tt))); return to_expand_result(tt_result.map(|it| it.map(Fragment::Tokens)));
} }
}; };
let tt = let (tt, err) = input.expect_fragment(fragment);
input.expect_fragment(fragment).map_err(|()| err!("fragment did not parse as {}", kind))?;
let fragment = if kind == "expr" { Fragment::Ast(tt) } else { Fragment::Tokens(tt) }; let fragment = if kind == "expr" { Fragment::Ast(tt) } else { Fragment::Tokens(tt) };
Ok(Some(fragment)) (Some(fragment), err)
} }
fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &tt::Subtree) -> Result<(), ExpandError> { fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &tt::Subtree) -> Result<(), ExpandError> {
@ -394,3 +434,7 @@ fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &tt::Subtree) -> Result<(), Exp
} }
Ok(()) Ok(())
} }
fn to_expand_result<T: Default>(result: Result<T, ExpandError>) -> ExpandResult<T> {
result.map_or_else(|e| (Default::default(), Some(e)), |it| (it, None))
}