mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
wip
This commit is contained in:
parent
6305d094ac
commit
0f3a54dd4d
5 changed files with 160 additions and 94 deletions
|
@ -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);
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(¯o_invocation.token_tree().unwrap()).unwrap();
|
ast_to_token_tree(¯o_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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue