From ad451ec18c11443bc7320e3cc6c40629ff9d80a1 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sat, 27 Apr 2024 22:08:29 +0200 Subject: [PATCH] feat: add support for unqualified path expressions (#40) Part of #29. --------- Signed-off-by: Sasha Pourcelot --- README.md | 2 +- expandable-impl/src/expansion.rs | 9 ++ rust-grammar-dpdfa/grammar.rs | 46 ++++-- rust-grammar-dpdfa/src/generated.rs | 161 ++++++++++++++++++--- tests/ui/fail/invalid_fn_calls.stderr | 4 +- tests/ui/fail/js_concat.stderr | 2 +- tests/ui/fail/path.rs | 24 +++ tests/ui/fail/path.stderr | 17 +++ tests/ui/fail/python_power_operator.stderr | 2 +- tests/ui/pass/expr_path.rs | 13 ++ 10 files changed, 242 insertions(+), 38 deletions(-) create mode 100644 tests/ui/fail/path.rs create mode 100644 tests/ui/fail/path.stderr create mode 100644 tests/ui/pass/expr_path.rs diff --git a/README.md b/README.md index cfe8497..8426b45 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ macro_rules! js_concat { This emits the following error [^error-message]: ```none -error: Potentially invalid expansion. Expected `break`, `if`, `return`, a `[`, a `{`, a literal, an identifier. +error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `[`, a `{`, a literal, an identifier. --> tests/ui/fail/js_concat.rs:5:16 | 5 | $left ++ $right diff --git a/expandable-impl/src/expansion.rs b/expandable-impl/src/expansion.rs index 6d1c909..5557513 100644 --- a/expandable-impl/src/expansion.rs +++ b/expandable-impl/src/expansion.rs @@ -546,4 +546,13 @@ mod tests { } } } + + assert_valid_arm! { + expr_path_full_fragment { + #[expr] + ( #ident:ident ) => { + #( :: #ident )* + } + } + } } diff --git a/rust-grammar-dpdfa/grammar.rs b/rust-grammar-dpdfa/grammar.rs index a9edc92..2d506a2 100644 --- a/rust-grammar-dpdfa/grammar.rs +++ b/rust-grammar-dpdfa/grammar.rs @@ -191,9 +191,8 @@ fn expr_after_atom() { fn expr_atom() { if peek(Return) || peek(Break) { expr_return_or_break(); - } else if peek(Ident) || peek(FragmentIdent) { - // Maybe beginning of a path expression? - expr_ident(); + } else if peek(Ident) || peek(FragmentIdent) || peek(ColonColon) { + expr_path(); } else if peek(FragmentExpr) || peek(Literal) { bump(); } else if peek(If) { @@ -256,17 +255,42 @@ fn expr_return_or_break() { } } -fn expr_ident() { - // TODO: call path() or sth - if peek(Ident) || peek(FragmentIdent) { - bump(); - } else { - error(); +fn expr_path() { + if peek(ColonColon) { + bump(ColonColon); } + expr_path_segment(); if peek(ColonColon) { - bump(ColonColon); - expr_angle_bracketed_generic_arguments(); + expr_path_(); + } +} + +fn expr_path_() { + bump(ColonColon); + expr_path_segment(); + + if peek(ColonColon) { + expr_path_(); + } +} + +fn expr_path_segment() { + path_segment(); + + if peek(ColonColon) { + if peek2(LessThan) { + bump(ColonColon); + expr_angle_bracketed_generic_arguments(); + } + } +} + +fn path_segment() { + if peek(Ident) || peek(FragmentIdent) || peek(SelfUpper) { + bump(); + } else { + error(); } } diff --git a/rust-grammar-dpdfa/src/generated.rs b/rust-grammar-dpdfa/src/generated.rs index 103fcc0..8260235 100644 --- a/rust-grammar-dpdfa/src/generated.rs +++ b/rust-grammar-dpdfa/src/generated.rs @@ -1267,14 +1267,17 @@ fn expr_atom_1(input: &mut RustParser) -> Result( input: &mut RustParser, ) -> Result, Option> { - if cond![input, peek, Ident] || cond![input, peek, FragmentIdent] { + if cond![input, peek, Ident] + || cond![input, peek, FragmentIdent] + || cond![input, peek, ColonColon] + { call_now![input, expr_atom_3] } else { call_now![input, expr_atom_17] } } fn expr_atom_2(input: &mut RustParser) -> Result, Option> { - call_now![input, expr_ident] + call_now![input, expr_path] } fn expr_atom_3(input: &mut RustParser) -> Result, Option> { call_now![input, expr_atom_2] @@ -1483,21 +1486,110 @@ fn expr_return_or_break( ) -> Result, Option> { call_now![input, expr_return_or_break_11] } -fn expr_ident_3( +fn expr_path_2(input: &mut RustParser) -> Result, Option> { + if cond![input, peek, ColonColon] { + call_now![input, expr_path_1] + } else { + call_now![input,] + } +} +fn expr_path_0(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_path_] +} +fn expr_path_1(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_path_0] +} +fn expr_path_3(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_path_segment] +} +fn expr_path_6(input: &mut RustParser) -> Result, Option> { + if cond![input, peek, ColonColon] { + call_now![input, expr_path_5] + } else { + call_now![input,] + } +} +fn expr_path_4(input: &mut RustParser) -> Result, Option> { + if bump![input, ColonColon] { + call_then![input,] + } else { + error![input] + } +} +fn expr_path_5(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_path_4] +} +fn expr_path_7(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_path_2, expr_path_3, expr_path_6] +} +fn expr_path(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_path_7] +} +fn expr_path__2( + input: &mut RustParser, +) -> Result, Option> { + if cond![input, peek, ColonColon] { + call_now![input, expr_path__1] + } else { + call_now![input,] + } +} +fn expr_path__0( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_path_] +} +fn expr_path__1( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_path__0] +} +fn expr_path__3( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_path_segment] +} +fn expr_path__4( + input: &mut RustParser, +) -> Result, Option> { + if bump![input, ColonColon] { + call_then![input,] + } else { + error![input] + } +} +fn expr_path__5( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_path__2, expr_path__3, expr_path__4] +} +fn expr_path_(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_path__5] +} +fn expr_path_segment_5( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, ColonColon] { - call_now![input, expr_ident_2] + call_now![input, expr_path_segment_4] + } else { + call_now![input,] + } +} +fn expr_path_segment_3( + input: &mut RustParser, +) -> Result, Option> { + if cond![input, peek2, LessThan] { + call_now![input, expr_path_segment_2] } else { call_now![input,] } } -fn expr_ident_0( +fn expr_path_segment_0( input: &mut RustParser, ) -> Result, Option> { call_now![input, expr_angle_bracketed_generic_arguments] } -fn expr_ident_1( +fn expr_path_segment_1( input: &mut RustParser, ) -> Result, Option> { if bump![input, ColonColon] { @@ -1506,21 +1598,44 @@ fn expr_ident_1( error![input] } } -fn expr_ident_2( +fn expr_path_segment_2( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_path_segment_0, expr_path_segment_1] +} +fn expr_path_segment_4( input: &mut RustParser, ) -> Result, Option> { - call_now![input, expr_ident_0, expr_ident_1] + call_now![input, expr_path_segment_3] } -fn expr_ident_8( +fn expr_path_segment_6( input: &mut RustParser, ) -> Result, Option> { - if cond![input, peek, Ident] || cond![input, peek, FragmentIdent] { - call_now![input, expr_ident_5] + call_now![input, path_segment] +} +fn expr_path_segment_7( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_path_segment_5, expr_path_segment_6] +} +fn expr_path_segment( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_path_segment_7] +} +fn path_segment_4( + input: &mut RustParser, +) -> Result, Option> { + if cond![input, peek, Ident] + || cond![input, peek, FragmentIdent] + || cond![input, peek, SelfUpper] + { + call_now![input, path_segment_1] } else { - call_now![input, expr_ident_7] + call_now![input, path_segment_3] } } -fn expr_ident_4( +fn path_segment_0( input: &mut RustParser, ) -> Result, Option> { if bump![input] { @@ -1529,28 +1644,30 @@ fn expr_ident_4( error![input] } } -fn expr_ident_5( +fn path_segment_1( input: &mut RustParser, ) -> Result, Option> { - call_now![input, expr_ident_4] + call_now![input, path_segment_0] } -fn expr_ident_6( +fn path_segment_2( input: &mut RustParser, ) -> Result, Option> { error![input] } -fn expr_ident_7( +fn path_segment_3( input: &mut RustParser, ) -> Result, Option> { - call_now![input, expr_ident_6] + call_now![input, path_segment_2] } -fn expr_ident_9( +fn path_segment_5( input: &mut RustParser, ) -> Result, Option> { - call_now![input, expr_ident_3, expr_ident_8] + call_now![input, path_segment_4] } -fn expr_ident(input: &mut RustParser) -> Result, Option> { - call_now![input, expr_ident_9] +fn path_segment( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, path_segment_5] } fn expr_if_3(input: &mut RustParser) -> Result, Option> { if cond![input, peek, Else] { diff --git a/tests/ui/fail/invalid_fn_calls.stderr b/tests/ui/fail/invalid_fn_calls.stderr index 1557a91..6615346 100644 --- a/tests/ui/fail/invalid_fn_calls.stderr +++ b/tests/ui/fail/invalid_fn_calls.stderr @@ -1,10 +1,10 @@ -error: Potentially invalid expansion. Expected `break`, `if`, `return`, a `)`, a `[`, a `{`, a literal, an identifier. +error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `)`, a `[`, a `{`, a literal, an identifier. --> tests/ui/fail/invalid_fn_calls.rs:6:18 | 6 | $fn_name(,) | ^ -error: Potentially invalid expansion. Expected `break`, `if`, `return`, a `)`, a `[`, a `{`, a literal, an identifier. +error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `)`, a `[`, a `{`, a literal, an identifier. --> tests/ui/fail/invalid_fn_calls.rs:14:18 | 14 | $fn_name(,,) diff --git a/tests/ui/fail/js_concat.stderr b/tests/ui/fail/js_concat.stderr index b897970..e03951c 100644 --- a/tests/ui/fail/js_concat.stderr +++ b/tests/ui/fail/js_concat.stderr @@ -1,4 +1,4 @@ -error: Potentially invalid expansion. Expected `break`, `if`, `return`, a `[`, a `{`, a literal, an identifier. +error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `[`, a `{`, a literal, an identifier. --> tests/ui/fail/js_concat.rs:5:16 | 5 | $left ++ $right diff --git a/tests/ui/fail/path.rs b/tests/ui/fail/path.rs new file mode 100644 index 0000000..685214a --- /dev/null +++ b/tests/ui/fail/path.rs @@ -0,0 +1,24 @@ +#![allow(unused)] + +#[expandable::expr] +macro_rules! a { + () => { + path::: + }; +} + +#[expandable::expr] +macro_rules! b { + () => { + path::< > + }; +} + +#[expandable::expr] +macro_rules! c { + () => { + path::1 + }; +} + +fn main() {} diff --git a/tests/ui/fail/path.stderr b/tests/ui/fail/path.stderr new file mode 100644 index 0000000..6260dd9 --- /dev/null +++ b/tests/ui/fail/path.stderr @@ -0,0 +1,17 @@ +error: Potentially invalid expansion. Expected `Self`, an identifier. + --> tests/ui/fail/path.rs:6:15 + | +6 | path::: + | ^ + +error: Potentially invalid expansion. Expected `-`, `>`, a `{`, a literal, an identifier, an identifier. + --> tests/ui/fail/path.rs:13:17 + | +13 | path::< > + | ^ + +error: Potentially invalid expansion. Expected `Self`, an identifier. + --> tests/ui/fail/path.rs:20:15 + | +20 | path::1 + | ^ diff --git a/tests/ui/fail/python_power_operator.stderr b/tests/ui/fail/python_power_operator.stderr index cdb53f7..1336898 100644 --- a/tests/ui/fail/python_power_operator.stderr +++ b/tests/ui/fail/python_power_operator.stderr @@ -1,4 +1,4 @@ -error: Potentially invalid expansion. Expected `break`, `if`, `return`, a `[`, a `{`, a literal, an identifier. +error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `[`, a `{`, a literal, an identifier. --> tests/ui/fail/python_power_operator.rs:5:14 | 5 | $e * *$e diff --git a/tests/ui/pass/expr_path.rs b/tests/ui/pass/expr_path.rs new file mode 100644 index 0000000..044866f --- /dev/null +++ b/tests/ui/pass/expr_path.rs @@ -0,0 +1,13 @@ +#[allow(unused)] +#[expandable::expr] +macro_rules! test { + ( $( $ident:ident )* ) => { + $( :: $ident )* + }; + + () => { + ::a::::c::() + } +} + +fn main() {}