From ff17d237a1fc77b422cecb9e88ce1b8c33d440f4 Mon Sep 17 00:00:00 2001 From: Jay Bosamiya Date: Fri, 2 Feb 2024 16:25:46 -0500 Subject: [PATCH] Handle macro-call statements Some macros, such as `calc!{}` can sit in statement position and need not have a terminating semicolon. This would previously be rejected by the parser, requiring the extra semicolon to not misparse whatever came _after_ such a statement-position macro-call. By convention, such macro calls use curly braces (while expr ones tend to use square or parens), so we can use this as a disambiguating factor. More details in parser comment. --- src/lib.rs | 1 + src/verus.pest | 15 +++++++++++ tests/verus-consistency.rs | 53 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4371ac4..1a99749 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -727,6 +727,7 @@ fn to_doc<'a>( Rule::lifetime_arg => map_to_doc(ctx, arena, pair), Rule::const_arg => unsupported(pair), Rule::macro_call => s, + Rule::macro_call_stmt => s, Rule::punctuation => map_to_doc(ctx, arena, pair), Rule::token => map_to_doc(ctx, arena, pair), Rule::delim_token_tree => map_to_doc(ctx, arena, pair), diff --git a/src/verus.pest b/src/verus.pest index 782c525..5ffc4ab 100644 --- a/src/verus.pest +++ b/src/verus.pest @@ -464,6 +464,20 @@ macro_call = { attr* ~ path ~ bang_str ~ !"=" ~ token_tree } +// NOTE: By convention, stmt-like or item-like macros (e.g., calc!{}) tend to +// use curly braces; this is only a convention though, and _technically_ one +// could use parens or square brackets for the same. However, we use this to +// disambiguate unknown macros when used in statement lists _without_ +// semi-colons; if it is intended as an expr, the user better be using parens. +// +// Another alternative is to use `!(expr ~ "}") ~ macro_call` rather than +// `macro_call_stmt`, but as of now, I believe this to be a better choice; we +// can switch to the alternative if we find situations where it might be better +// suited. +macro_call_stmt = { + attr* ~ path ~ bang_str ~ !"=" ~ lbrace_str ~ token_tree* ~ rbrace_str +} + // See https://doc.rust-lang.org/beta/reference/tokens.html#punctuation punctuation = { bin_expr_ops @@ -869,6 +883,7 @@ stmt = { | expr_with_block ~ semi_str? | expr ~ semi_str | item_no_macro_call + | macro_call_stmt } let_stmt = { diff --git a/tests/verus-consistency.rs b/tests/verus-consistency.rs index 52f148d..39929b5 100644 --- a/tests/verus-consistency.rs +++ b/tests/verus-consistency.rs @@ -457,6 +457,59 @@ verus!{ "###); } +#[test] +fn verus_macro_statements() { + let file = r#" +verus! { + +// Notice that the semicolon is optional +proof fn foo() { + calc! { + (==) + 1int; {} + (0int + 1int); {} + }; + calc! { + (==) + 1int; {} + (0int + 1int); {} + } + calc! { + (==) + 1int; {} + (0int + 1int); {} + } +} + +} +"#; + + assert_snapshot!(parse_and_format(file).unwrap(), @r###" + verus! { + + // Notice that the semicolon is optional + proof fn foo() { + calc! { + (==) + 1int; {} + (0int + 1int); {} + }; + calc! { + (==) + 1int; {} + (0int + 1int); {} + } + calc! { + (==) + 1int; {} + (0int + 1int); {} + } + } + + } // verus! + "###); +} + #[test] fn verus_impl() { let file = r#"