Skip to content

Commit

Permalink
feat: add support for unqualified path expressions (#40)
Browse files Browse the repository at this point in the history
Part of #29.

---------

Signed-off-by: Sasha Pourcelot <[email protected]>
  • Loading branch information
scrabsha authored Apr 27, 2024
1 parent 9cc30af commit ad451ec
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 38 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions expandable-impl/src/expansion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,4 +546,13 @@ mod tests {
}
}
}

assert_valid_arm! {
expr_path_full_fragment {
#[expr]
( #ident:ident ) => {
#( :: #ident )*
}
}
}
}
46 changes: 35 additions & 11 deletions rust-grammar-dpdfa/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
}
}

Expand Down
161 changes: 139 additions & 22 deletions rust-grammar-dpdfa/src/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,14 +1267,17 @@ fn expr_atom_1<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Sp
fn expr_atom_18<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
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<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_ident]
call_now![input, expr_path]
}
fn expr_atom_3<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_atom_2]
Expand Down Expand Up @@ -1483,21 +1486,110 @@ fn expr_return_or_break<Span: Copy>(
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_return_or_break_11]
}
fn expr_ident_3<Span: Copy>(
fn expr_path_2<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
if cond![input, peek, ColonColon] {
call_now![input, expr_path_1]
} else {
call_now![input,]
}
}
fn expr_path_0<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_]
}
fn expr_path_1<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_0]
}
fn expr_path_3<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_segment]
}
fn expr_path_6<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
if cond![input, peek, ColonColon] {
call_now![input, expr_path_5]
} else {
call_now![input,]
}
}
fn expr_path_4<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
if bump![input, ColonColon] {
call_then![input,]
} else {
error![input]
}
}
fn expr_path_5<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_4]
}
fn expr_path_7<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_2, expr_path_3, expr_path_6]
}
fn expr_path<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_7]
}
fn expr_path__2<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
if cond![input, peek, ColonColon] {
call_now![input, expr_path__1]
} else {
call_now![input,]
}
}
fn expr_path__0<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_]
}
fn expr_path__1<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path__0]
}
fn expr_path__3<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_segment]
}
fn expr_path__4<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
if bump![input, ColonColon] {
call_then![input,]
} else {
error![input]
}
}
fn expr_path__5<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path__2, expr_path__3, expr_path__4]
}
fn expr_path_<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path__5]
}
fn expr_path_segment_5<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
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<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
if cond![input, peek2, LessThan] {
call_now![input, expr_path_segment_2]
} else {
call_now![input,]
}
}
fn expr_ident_0<Span: Copy>(
fn expr_path_segment_0<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_angle_bracketed_generic_arguments]
}
fn expr_ident_1<Span: Copy>(
fn expr_path_segment_1<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
if bump![input, ColonColon] {
Expand All @@ -1506,21 +1598,44 @@ fn expr_ident_1<Span: Copy>(
error![input]
}
}
fn expr_ident_2<Span: Copy>(
fn expr_path_segment_2<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_segment_0, expr_path_segment_1]
}
fn expr_path_segment_4<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_ident_0, expr_ident_1]
call_now![input, expr_path_segment_3]
}
fn expr_ident_8<Span: Copy>(
fn expr_path_segment_6<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
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<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_segment_5, expr_path_segment_6]
}
fn expr_path_segment<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_path_segment_7]
}
fn path_segment_4<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
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<Span: Copy>(
fn path_segment_0<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
if bump![input] {
Expand All @@ -1529,28 +1644,30 @@ fn expr_ident_4<Span: Copy>(
error![input]
}
}
fn expr_ident_5<Span: Copy>(
fn path_segment_1<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_ident_4]
call_now![input, path_segment_0]
}
fn expr_ident_6<Span: Copy>(
fn path_segment_2<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
error![input]
}
fn expr_ident_7<Span: Copy>(
fn path_segment_3<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_ident_6]
call_now![input, path_segment_2]
}
fn expr_ident_9<Span: Copy>(
fn path_segment_5<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_ident_3, expr_ident_8]
call_now![input, path_segment_4]
}
fn expr_ident<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
call_now![input, expr_ident_9]
fn path_segment<Span: Copy>(
input: &mut RustParser<Span>,
) -> Result<Transition<Span>, Option<Span>> {
call_now![input, path_segment_5]
}
fn expr_if_3<Span: Copy>(input: &mut RustParser<Span>) -> Result<Transition<Span>, Option<Span>> {
if cond![input, peek, Else] {
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/fail/invalid_fn_calls.stderr
Original file line number Diff line number Diff line change
@@ -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(,,)
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/fail/js_concat.stderr
Original file line number Diff line number Diff line change
@@ -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
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/fail/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![allow(unused)]

#[expandable::expr]
macro_rules! a {
() => {
path:::<foo>
};
}

#[expandable::expr]
macro_rules! b {
() => {
path::< <foo> >
};
}

#[expandable::expr]
macro_rules! c {
() => {
path::1
};
}

fn main() {}
17 changes: 17 additions & 0 deletions tests/ui/fail/path.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error: Potentially invalid expansion. Expected `Self`, an identifier.
--> tests/ui/fail/path.rs:6:15
|
6 | path:::<foo>
| ^

error: Potentially invalid expansion. Expected `-`, `>`, a `{`, a literal, an identifier, an identifier.
--> tests/ui/fail/path.rs:13:17
|
13 | path::< <foo> >
| ^

error: Potentially invalid expansion. Expected `Self`, an identifier.
--> tests/ui/fail/path.rs:20:15
|
20 | path::1
| ^
2 changes: 1 addition & 1 deletion tests/ui/fail/python_power_operator.stderr
Original file line number Diff line number Diff line change
@@ -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
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/pass/expr_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#[allow(unused)]
#[expandable::expr]
macro_rules! test {
( $( $ident:ident )* ) => {
$( :: $ident )*
};

() => {
::a::<b>::c::<d>()
}
}

fn main() {}

0 comments on commit ad451ec

Please sign in to comment.