Merge #1157
1157: Add mbe stmt matcher r=matklad a=edwin0cheng Add `stmt` matcher in `ra_mbe` , and added corresponding `stmt()` parser in `ra_syntax`. This PR also help PR #1148 for `MarcoKind::Items` parsing. Note: * According [the book](https://doc.rust-lang.org/reference/macros-by-example.html), mbe `stmt` matcher will only match statement without the trailing semicolon * `item` is a valid statement. Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
commit
0e35603069
@ -582,4 +582,19 @@ macro_rules! foo {
|
||||
);
|
||||
assert_expansion(&rules, "foo! { (a, b) }", "fn foo () {let (a , b) ;}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stmt() {
|
||||
let rules = create_rules(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
($ i:stmt) => (
|
||||
fn bar() { $ i; }
|
||||
)
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_expansion(&rules, "foo! { 2 }", "fn bar () {2 ;}");
|
||||
assert_expansion(&rules, "foo! { let a = 0 }", "fn bar () {let a = 0 ;}");
|
||||
}
|
||||
}
|
||||
|
@ -157,6 +157,10 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
|
||||
let pat = input.eat_pat().ok_or(ExpandError::UnexpectedToken)?.clone();
|
||||
res.inner.insert(text.clone(), Binding::Simple(pat.into()));
|
||||
}
|
||||
"stmt" => {
|
||||
let pat = input.eat_stmt().ok_or(ExpandError::UnexpectedToken)?.clone();
|
||||
res.inner.insert(text.clone(), Binding::Simple(pat.into()));
|
||||
}
|
||||
_ => return Err(ExpandError::UnexpectedToken),
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,10 @@ pub fn parse_pat(self) -> Option<tt::TokenTree> {
|
||||
self.parse(ra_parser::parse_pat)
|
||||
}
|
||||
|
||||
pub fn parse_stmt(self) -> Option<tt::TokenTree> {
|
||||
self.parse(|src, sink| ra_parser::parse_stmt(src, sink, false))
|
||||
}
|
||||
|
||||
fn parse<F>(self, f: F) -> Option<tt::TokenTree>
|
||||
where
|
||||
F: FnOnce(&dyn TokenSource, &mut dyn TreeSink),
|
||||
|
@ -99,6 +99,11 @@ pub(crate) fn eat_pat(&mut self) -> Option<tt::TokenTree> {
|
||||
parser.parse_pat()
|
||||
}
|
||||
|
||||
pub(crate) fn eat_stmt(&mut self) -> Option<tt::TokenTree> {
|
||||
let parser = Parser::new(&mut self.pos, self.subtree);
|
||||
parser.parse_stmt()
|
||||
}
|
||||
|
||||
pub(crate) fn expect_char(&mut self, char: char) -> Result<(), ParseError> {
|
||||
if self.at_char(char) {
|
||||
self.bump();
|
||||
|
@ -65,6 +65,10 @@ pub(crate) fn pattern(p: &mut Parser) {
|
||||
patterns::pattern(p)
|
||||
}
|
||||
|
||||
pub(crate) fn stmt(p: &mut Parser, with_semi: bool) {
|
||||
expressions::stmt(p, with_semi)
|
||||
}
|
||||
|
||||
pub(crate) fn reparser(
|
||||
node: SyntaxKind,
|
||||
first_child: Option<SyntaxKind>,
|
||||
|
@ -48,6 +48,104 @@ fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn stmt(p: &mut Parser, with_semi: bool) {
|
||||
// test block_items
|
||||
// fn a() { fn b() {} }
|
||||
let m = p.start();
|
||||
// test attr_on_expr_stmt
|
||||
// fn foo() {
|
||||
// #[A] foo();
|
||||
// #[B] bar!{}
|
||||
// #[C] #[D] {}
|
||||
// #[D] return ();
|
||||
// }
|
||||
let has_attrs = p.at(POUND);
|
||||
attributes::outer_attributes(p);
|
||||
|
||||
if p.at(LET_KW) {
|
||||
let_stmt(p, m, with_semi);
|
||||
return;
|
||||
}
|
||||
|
||||
let m = match items::maybe_item(p, m, items::ItemFlavor::Mod) {
|
||||
Ok(()) => return,
|
||||
Err(m) => m,
|
||||
};
|
||||
|
||||
let (cm, blocklike) = expr_stmt(p);
|
||||
let kind = cm.as_ref().map(|cm| cm.kind()).unwrap_or(ERROR);
|
||||
|
||||
if has_attrs && !is_expr_stmt_attr_allowed(kind) {
|
||||
// test_err attr_on_expr_not_allowed
|
||||
// fn foo() {
|
||||
// #[A] 1 + 2;
|
||||
// #[B] if true {};
|
||||
// }
|
||||
p.error(format!("attributes are not allowed on {:?}", kind));
|
||||
}
|
||||
|
||||
if p.at(R_CURLY) {
|
||||
// test attr_on_last_expr_in_block
|
||||
// fn foo() {
|
||||
// { #[A] bar!()? }
|
||||
// #[B] &()
|
||||
// }
|
||||
if let Some(cm) = cm {
|
||||
cm.undo_completion(p).abandon(p);
|
||||
m.complete(p, kind);
|
||||
} else {
|
||||
m.abandon(p);
|
||||
}
|
||||
} else {
|
||||
// test no_semi_after_block
|
||||
// fn foo() {
|
||||
// if true {}
|
||||
// loop {}
|
||||
// match () {}
|
||||
// while true {}
|
||||
// for _ in () {}
|
||||
// {}
|
||||
// {}
|
||||
// macro_rules! test {
|
||||
// () => {}
|
||||
// }
|
||||
// test!{}
|
||||
// }
|
||||
if with_semi {
|
||||
if blocklike.is_block() {
|
||||
p.eat(SEMI);
|
||||
} else {
|
||||
p.expect(SEMI);
|
||||
}
|
||||
}
|
||||
m.complete(p, EXPR_STMT);
|
||||
}
|
||||
|
||||
// test let_stmt;
|
||||
// fn foo() {
|
||||
// let a;
|
||||
// let b: i32;
|
||||
// let c = 92;
|
||||
// let d: i32 = 92;
|
||||
// }
|
||||
fn let_stmt(p: &mut Parser, m: Marker, with_semi: bool) {
|
||||
assert!(p.at(LET_KW));
|
||||
p.bump();
|
||||
patterns::pattern(p);
|
||||
if p.at(COLON) {
|
||||
types::ascription(p);
|
||||
}
|
||||
if p.eat(EQ) {
|
||||
expressions::expr(p);
|
||||
}
|
||||
|
||||
if with_semi {
|
||||
p.expect(SEMI);
|
||||
}
|
||||
m.complete(p, LET_STMT);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn expr_block_contents(p: &mut Parser) {
|
||||
// This is checked by a validator
|
||||
attributes::inner_attributes(p);
|
||||
@ -62,95 +160,7 @@ pub(crate) fn expr_block_contents(p: &mut Parser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// test block_items
|
||||
// fn a() { fn b() {} }
|
||||
let m = p.start();
|
||||
// test attr_on_expr_stmt
|
||||
// fn foo() {
|
||||
// #[A] foo();
|
||||
// #[B] bar!{}
|
||||
// #[C] #[D] {}
|
||||
// #[D] return ();
|
||||
// }
|
||||
let has_attrs = p.at(POUND);
|
||||
attributes::outer_attributes(p);
|
||||
if p.at(LET_KW) {
|
||||
let_stmt(p, m);
|
||||
continue;
|
||||
}
|
||||
|
||||
let m = match items::maybe_item(p, m, items::ItemFlavor::Mod) {
|
||||
Ok(()) => continue,
|
||||
Err(m) => m,
|
||||
};
|
||||
|
||||
let (cm, blocklike) = expr_stmt(p);
|
||||
let kind = cm.as_ref().map(|cm| cm.kind()).unwrap_or(ERROR);
|
||||
|
||||
if has_attrs && !is_expr_stmt_attr_allowed(kind) {
|
||||
// test_err attr_on_expr_not_allowed
|
||||
// fn foo() {
|
||||
// #[A] 1 + 2;
|
||||
// #[B] if true {};
|
||||
// }
|
||||
p.error(format!("attributes are not allowed on {:?}", kind));
|
||||
}
|
||||
|
||||
if p.at(R_CURLY) {
|
||||
// test attr_on_last_expr_in_block
|
||||
// fn foo() {
|
||||
// { #[A] bar!()? }
|
||||
// #[B] &()
|
||||
// }
|
||||
if let Some(cm) = cm {
|
||||
cm.undo_completion(p).abandon(p);
|
||||
m.complete(p, kind);
|
||||
} else {
|
||||
m.abandon(p);
|
||||
}
|
||||
} else {
|
||||
// test no_semi_after_block
|
||||
// fn foo() {
|
||||
// if true {}
|
||||
// loop {}
|
||||
// match () {}
|
||||
// while true {}
|
||||
// for _ in () {}
|
||||
// {}
|
||||
// {}
|
||||
// macro_rules! test {
|
||||
// () => {}
|
||||
// }
|
||||
// test!{}
|
||||
// }
|
||||
if blocklike.is_block() {
|
||||
p.eat(SEMI);
|
||||
} else {
|
||||
p.expect(SEMI);
|
||||
}
|
||||
m.complete(p, EXPR_STMT);
|
||||
}
|
||||
}
|
||||
|
||||
// test let_stmt;
|
||||
// fn foo() {
|
||||
// let a;
|
||||
// let b: i32;
|
||||
// let c = 92;
|
||||
// let d: i32 = 92;
|
||||
// }
|
||||
fn let_stmt(p: &mut Parser, m: Marker) {
|
||||
assert!(p.at(LET_KW));
|
||||
p.bump();
|
||||
patterns::pattern(p);
|
||||
if p.at(COLON) {
|
||||
types::ascription(p);
|
||||
}
|
||||
if p.eat(EQ) {
|
||||
expressions::expr(p);
|
||||
}
|
||||
p.expect(SEMI);
|
||||
m.complete(p, LET_STMT);
|
||||
stmt(p, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,11 @@ pub fn parse_pat(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink) {
|
||||
parse_from_tokens(token_source, tree_sink, grammar::pattern);
|
||||
}
|
||||
|
||||
/// Parse given tokens into the given sink as a statement
|
||||
pub fn parse_stmt(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink, with_semi: bool) {
|
||||
parse_from_tokens(token_source, tree_sink, |p| grammar::stmt(p, with_semi));
|
||||
}
|
||||
|
||||
/// A parsing function for a specific braced-block.
|
||||
pub struct Reparser(fn(&mut parser::Parser));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user