From 7c102c3c8b3dc076c03cbe842266a2b140be6323 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 14:31:18 -0700 Subject: [PATCH 1/5] Extract non-full expr scanner to module --- src/data.rs | 83 ++---------------------------------------------- src/lib.rs | 3 ++ src/scan_expr.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 80 deletions(-) create mode 100644 src/scan_expr.rs diff --git a/src/data.rs b/src/data.rs index 6c7688751..96db2a0b7 100644 --- a/src/data.rs +++ b/src/data.rs @@ -248,6 +248,8 @@ pub(crate) mod parsing { use crate::parse::discouraged::Speculative as _; use crate::parse::{Parse, ParseStream}; use crate::restriction::{FieldMutability, Visibility}; + #[cfg(not(feature = "full"))] + use crate::scan_expr::scan_expr; use crate::token; use crate::ty::Type; use crate::verbatim; @@ -276,7 +278,7 @@ pub(crate) mod parsing { let mut discriminant: Result = ahead.parse(); if discriminant.is_ok() { input.advance_to(&ahead); - } else if scan_lenient_discriminant(input).is_ok() { + } else if scan_expr(input).is_ok() { discriminant = Ok(Expr::Verbatim(verbatim::between(&begin, input))); } discriminant? @@ -294,85 +296,6 @@ pub(crate) mod parsing { } } - #[cfg(not(feature = "full"))] - pub(crate) fn scan_lenient_discriminant(input: ParseStream) -> Result<()> { - use crate::expr::Member; - use crate::lifetime::Lifetime; - use crate::lit::Lit; - use crate::lit::LitFloat; - use crate::op::{BinOp, UnOp}; - use crate::path::{self, AngleBracketedGenericArguments}; - use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis}; - - let consume = |delimiter: Delimiter| { - Result::unwrap(input.step(|cursor| match cursor.group(delimiter) { - Some((_inside, _span, rest)) => Ok((true, rest)), - None => Ok((false, *cursor)), - })) - }; - - macro_rules! consume { - [$token:tt] => { - input.parse::>().unwrap().is_some() - }; - } - - let mut initial = true; - let mut depth = 0usize; - loop { - if initial { - if consume![&] { - initial = consume![mut] || !consume![raw] || consume![const] || consume![mut]; - } else if consume![if] || consume![match] || consume![while] { - depth += 1; - } else if input.parse::>()?.is_some() - || (consume(Brace) || consume(Bracket) || consume(Parenthesis)) - || (consume![async] || consume![const] || consume![loop] || consume![unsafe]) - && (consume(Brace) || break) - { - initial = false; - } else if consume![let] { - while !consume![=] { - if !((consume![|] || consume![ref] || consume![mut] || consume![@]) - || (consume![!] || input.parse::>()?.is_some()) - || (consume![..=] || consume![..] || consume![&] || consume![_]) - || (consume(Brace) || consume(Bracket) || consume(Parenthesis))) - { - path::parsing::qpath(input, true)?; - } - } - } else if input.parse::>()?.is_some() && !consume![:] { - break; - } else if input.parse::().is_err() { - path::parsing::qpath(input, true)?; - initial = consume![!] || depth == 0 && input.peek(token::Brace); - } - } else if input.is_empty() || input.peek(Token![,]) { - return Ok(()); - } else if depth > 0 && consume(Brace) { - if consume![else] && !consume(Brace) { - initial = consume![if] || break; - } else { - depth -= 1; - } - } else if input.parse::().is_ok() || (consume![..] | consume![=]) { - initial = true; - } else if consume![.] { - if input.parse::>()?.is_none() - && (input.parse::()?.is_named() && consume![::]) - { - AngleBracketedGenericArguments::do_parse(None, input)?; - } - } else if consume![as] { - input.parse::()?; - } else if !(consume(Brace) || consume(Bracket) || consume(Parenthesis)) { - break; - } - } - - Err(input.error("unsupported expression")) - } - #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for FieldsNamed { fn parse(input: ParseStream) -> Result { diff --git a/src/lib.rs b/src/lib.rs index 2eb370ad9..ee6e716d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -509,6 +509,9 @@ pub use crate::restriction::{FieldMutability, VisRestricted, Visibility}; mod sealed; +#[cfg(all(feature = "parsing", feature = "derive", not(feature = "full")))] +mod scan_expr; + mod span; #[cfg(all(feature = "parsing", feature = "printing"))] diff --git a/src/scan_expr.rs b/src/scan_expr.rs new file mode 100644 index 000000000..04db8dc30 --- /dev/null +++ b/src/scan_expr.rs @@ -0,0 +1,80 @@ +use crate::expr::Member; +use crate::lifetime::Lifetime; +use crate::lit::Lit; +use crate::lit::LitFloat; +use crate::op::{BinOp, UnOp}; +use crate::parse::{ParseStream, Result}; +use crate::path::{self, AngleBracketedGenericArguments}; +use crate::token; +use crate::ty::Type; +use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis}; + +pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { + let consume = |delimiter: Delimiter| { + Result::unwrap(input.step(|cursor| match cursor.group(delimiter) { + Some((_inside, _span, rest)) => Ok((true, rest)), + None => Ok((false, *cursor)), + })) + }; + + macro_rules! consume { + [$token:tt] => { + input.parse::>().unwrap().is_some() + }; + } + + let mut initial = true; + let mut depth = 0usize; + loop { + if initial { + if consume![&] { + initial = consume![mut] || !consume![raw] || consume![const] || consume![mut]; + } else if consume![if] || consume![match] || consume![while] { + depth += 1; + } else if input.parse::>()?.is_some() + || (consume(Brace) || consume(Bracket) || consume(Parenthesis)) + || (consume![async] || consume![const] || consume![loop] || consume![unsafe]) + && (consume(Brace) || break) + { + initial = false; + } else if consume![let] { + while !consume![=] { + if !((consume![|] || consume![ref] || consume![mut] || consume![@]) + || (consume![!] || input.parse::>()?.is_some()) + || (consume![..=] || consume![..] || consume![&] || consume![_]) + || (consume(Brace) || consume(Bracket) || consume(Parenthesis))) + { + path::parsing::qpath(input, true)?; + } + } + } else if input.parse::>()?.is_some() && !consume![:] { + break; + } else if input.parse::().is_err() { + path::parsing::qpath(input, true)?; + initial = consume![!] || depth == 0 && input.peek(token::Brace); + } + } else if input.is_empty() || input.peek(Token![,]) { + return Ok(()); + } else if depth > 0 && consume(Brace) { + if consume![else] && !consume(Brace) { + initial = consume![if] || break; + } else { + depth -= 1; + } + } else if input.parse::().is_ok() || (consume![..] | consume![=]) { + initial = true; + } else if consume![.] { + if input.parse::>()?.is_none() + && (input.parse::()?.is_named() && consume![::]) + { + AngleBracketedGenericArguments::do_parse(None, input)?; + } + } else if consume![as] { + input.parse::()?; + } else if !(consume(Brace) || consume(Bracket) || consume(Parenthesis)) { + break; + } + } + + Err(input.error("unsupported expression")) +} From 0132c447fe045431906945178bea219816d5e55f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 14:37:42 -0700 Subject: [PATCH 2/5] Make scan_expr compilable from integration test --- src/lib.rs | 2 ++ src/scan_expr.rs | 22 +++++++++------------- tests/test_precedence.rs | 4 ++++ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ee6e716d7..4c5c70f06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -307,6 +307,8 @@ clippy::wildcard_imports, )] +extern crate self as syn; + #[cfg(feature = "proc-macro")] extern crate proc_macro; diff --git a/src/scan_expr.rs b/src/scan_expr.rs index 04db8dc30..a3ec34708 100644 --- a/src/scan_expr.rs +++ b/src/scan_expr.rs @@ -1,13 +1,9 @@ -use crate::expr::Member; -use crate::lifetime::Lifetime; -use crate::lit::Lit; -use crate::lit::LitFloat; -use crate::op::{BinOp, UnOp}; -use crate::parse::{ParseStream, Result}; -use crate::path::{self, AngleBracketedGenericArguments}; -use crate::token; -use crate::ty::Type; use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis}; +use syn::parse::{ParseStream, Result}; +use syn::{ + token, AngleBracketedGenericArguments, BinOp, ExprPath, Lifetime, Lit, LitFloat, Member, Token, + Type, UnOp, +}; pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { let consume = |delimiter: Delimiter| { @@ -44,13 +40,13 @@ pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { || (consume![..=] || consume![..] || consume![&] || consume![_]) || (consume(Brace) || consume(Bracket) || consume(Parenthesis))) { - path::parsing::qpath(input, true)?; + input.parse::()?; } } } else if input.parse::>()?.is_some() && !consume![:] { break; } else if input.parse::().is_err() { - path::parsing::qpath(input, true)?; + input.parse::()?; initial = consume![!] || depth == 0 && input.peek(token::Brace); } } else if input.is_empty() || input.peek(Token![,]) { @@ -65,9 +61,9 @@ pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { initial = true; } else if consume![.] { if input.parse::>()?.is_none() - && (input.parse::()?.is_named() && consume![::]) + && (matches!(input.parse()?, Member::Named(_)) && input.peek(Token![::])) { - AngleBracketedGenericArguments::do_parse(None, input)?; + input.parse::()?; } } else if consume![as] { input.parse::()?; diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs index 7231bd640..077e8d486 100644 --- a/tests/test_precedence.rs +++ b/tests/test_precedence.rs @@ -56,6 +56,10 @@ mod macros; mod common; mod repo; +#[path = "../src/scan_expr.rs"] +#[allow(dead_code)] +mod scan_expr; + #[test] fn test_rustc_precedence() { repo::rayon_init(); From 8039cb37a02cbf080f48416651141d4c77c05075 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 14:47:22 -0700 Subject: [PATCH 3/5] Test that every expr can be scanned --- tests/test_precedence.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs index 077e8d486..1fa64b320 100644 --- a/tests/test_precedence.rs +++ b/tests/test_precedence.rs @@ -49,6 +49,7 @@ use std::fs; use std::path::Path; use std::process; use std::sync::atomic::{AtomicUsize, Ordering}; +use syn::parse::Parser as _; #[macro_use] mod macros; @@ -57,7 +58,6 @@ mod common; mod repo; #[path = "../src/scan_expr.rs"] -#[allow(dead_code)] mod scan_expr; #[test] @@ -119,7 +119,8 @@ fn test_expressions(path: &Path, edition: Edition, exprs: Vec) -> (us rustc_span::create_session_if_not_set_then(edition, |_| { for expr in exprs { - let source_code = expr.to_token_stream().to_string(); + let expr_tokens = expr.to_token_stream(); + let source_code = expr_tokens.to_string(); let librustc_ast = if let Some(e) = librustc_parse_and_rewrite(&source_code) { e } else { @@ -177,6 +178,16 @@ fn test_expressions(path: &Path, edition: Edition, exprs: Vec) -> (us continue; } + if scan_expr::scan_expr.parse2(expr_tokens).is_err() { + failed += 1; + errorf!( + "\nFAIL {} - failed to scan expr\n{}\n", + path.display(), + source_code, + ); + continue; + } + passed += 1; } }); From ca97c7d82d9837c1b49c085a546a481cf879e619 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 14:55:30 -0700 Subject: [PATCH 4/5] Translate expr scanner to table driven --- src/scan_expr.rs | 320 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 254 insertions(+), 66 deletions(-) diff --git a/src/scan_expr.rs b/src/scan_expr.rs index a3ec34708..155b5b63b 100644 --- a/src/scan_expr.rs +++ b/src/scan_expr.rs @@ -1,76 +1,264 @@ -use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis}; +use self::{Action::*, Input::*}; +use proc_macro2::{Delimiter, Ident, Spacing, TokenTree}; use syn::parse::{ParseStream, Result}; -use syn::{ - token, AngleBracketedGenericArguments, BinOp, ExprPath, Lifetime, Lit, LitFloat, Member, Token, - Type, UnOp, -}; +use syn::{AngleBracketedGenericArguments, BinOp, Expr, ExprPath, Lifetime, Lit, Token, Type}; -pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { - let consume = |delimiter: Delimiter| { - Result::unwrap(input.step(|cursor| match cursor.group(delimiter) { - Some((_inside, _span, rest)) => Ok((true, rest)), - None => Ok((false, *cursor)), - })) - }; - - macro_rules! consume { - [$token:tt] => { - input.parse::>().unwrap().is_some() - }; - } +enum Input { + Keyword(&'static str), + Punct(&'static str), + ConsumeAny, + ConsumeBinOp, + ConsumeBrace, + ConsumeDelimiter, + ConsumeIdent, + ConsumeLifetime, + ConsumeLiteral, + ConsumeNestedBrace, + ExpectPath, + ExpectTurbofish, + ExpectType, + CanBeginExpr, + Otherwise, + Empty, +} + +enum Action { + SetState(&'static [(Input, Action)]), + IncDepth, + DecDepth, + Finish, +} + +static INIT: [(Input, Action); 28] = [ + (ConsumeDelimiter, SetState(&POSTFIX)), + (Keyword("async"), SetState(&ASYNC)), + (Keyword("break"), SetState(&BREAK_LABEL)), + (Keyword("const"), SetState(&CONST)), + (Keyword("continue"), SetState(&CONTINUE)), + (Keyword("for"), SetState(&FOR)), + (Keyword("if"), IncDepth), + (Keyword("let"), SetState(&PATTERN)), + (Keyword("loop"), SetState(&BLOCK)), + (Keyword("match"), IncDepth), + (Keyword("move"), SetState(&CLOSURE)), + (Keyword("return"), SetState(&RETURN)), + (Keyword("static"), SetState(&CLOSURE)), + (Keyword("unsafe"), SetState(&BLOCK)), + (Keyword("while"), IncDepth), + (Keyword("yield"), SetState(&RETURN)), + (Keyword("_"), SetState(&POSTFIX)), + (Punct("!"), SetState(&INIT)), + (Punct("#"), SetState(&[(ConsumeDelimiter, SetState(&INIT))])), + (Punct("&"), SetState(&REFERENCE)), + (Punct("*"), SetState(&INIT)), + (Punct("-"), SetState(&INIT)), + (Punct("..="), SetState(&INIT)), + (Punct(".."), SetState(&RANGE)), + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeLifetime, SetState(&[(Punct(":"), SetState(&INIT))])), + (ConsumeLiteral, SetState(&POSTFIX)), + (ExpectPath, SetState(&PATH)), +]; + +static POSTFIX: [(Input, Action); 10] = [ + (Keyword("as"), SetState(&[(ExpectType, SetState(&POSTFIX))])), + (Punct("..="), SetState(&INIT)), + (Punct(".."), SetState(&RANGE)), + (Punct("."), SetState(&DOT)), + (Punct("?"), SetState(&POSTFIX)), + (ConsumeBinOp, SetState(&INIT)), + (Punct("="), SetState(&INIT)), + (ConsumeNestedBrace, SetState(&IF_THEN)), + (ConsumeDelimiter, SetState(&POSTFIX)), + (Empty, Finish), +]; + +static ASYNC: [(Input, Action); 3] = [ + (Keyword("move"), SetState(&ASYNC)), + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeBrace, SetState(&POSTFIX)), +]; + +static BLOCK: [(Input, Action); 1] = [(ConsumeBrace, SetState(&POSTFIX))]; + +static BREAK_LABEL: [(Input, Action); 2] = [ + (ConsumeLifetime, SetState(&BREAK_VALUE)), + (Otherwise, SetState(&BREAK_VALUE)), +]; + +static BREAK_VALUE: [(Input, Action); 3] = [ + (ConsumeNestedBrace, SetState(&IF_THEN)), + (CanBeginExpr, SetState(&INIT)), + (Otherwise, SetState(&POSTFIX)), +]; + +static CLOSURE: [(Input, Action); 6] = [ + (Keyword("async"), SetState(&CLOSURE)), + (Keyword("move"), SetState(&CLOSURE)), + (Punct(","), SetState(&CLOSURE)), + (Punct(">"), SetState(&CLOSURE)), + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeLifetime, SetState(&CLOSURE)), +]; + +static CLOSURE_ARGS: [(Input, Action); 2] = [ + (Punct("|"), SetState(&CLOSURE_RET)), + (ConsumeAny, SetState(&CLOSURE_ARGS)), +]; + +static CLOSURE_RET: [(Input, Action); 2] = [ + (Punct("->"), SetState(&[(ExpectType, SetState(&BLOCK))])), + (Otherwise, SetState(&INIT)), +]; + +static CONST: [(Input, Action); 2] = [ + (Punct("|"), SetState(&CLOSURE_ARGS)), + (ConsumeBrace, SetState(&POSTFIX)), +]; + +static CONTINUE: [(Input, Action); 2] = [ + (ConsumeLifetime, SetState(&POSTFIX)), + (Otherwise, SetState(&POSTFIX)), +]; + +static DOT: [(Input, Action); 3] = [ + (Keyword("await"), SetState(&POSTFIX)), + (ConsumeIdent, SetState(&METHOD)), + (ConsumeLiteral, SetState(&POSTFIX)), +]; - let mut initial = true; +static FOR: [(Input, Action); 2] = [ + (Punct("<"), SetState(&CLOSURE)), + (Otherwise, SetState(&PATTERN)), +]; + +static IF_ELSE: [(Input, Action); 2] = [(Keyword("if"), SetState(&INIT)), (ConsumeBrace, DecDepth)]; +static IF_THEN: [(Input, Action); 2] = + [(Keyword("else"), SetState(&IF_ELSE)), (Otherwise, DecDepth)]; + +static METHOD: [(Input, Action); 1] = [(ExpectTurbofish, SetState(&POSTFIX))]; + +static PATH: [(Input, Action); 4] = [ + (Punct("!="), SetState(&INIT)), + (Punct("!"), SetState(&INIT)), + (ConsumeNestedBrace, SetState(&IF_THEN)), + (Otherwise, SetState(&POSTFIX)), +]; + +static PATTERN: [(Input, Action); 15] = [ + (ConsumeDelimiter, SetState(&PATTERN)), + (Keyword("box"), SetState(&PATTERN)), + (Keyword("in"), IncDepth), + (Keyword("mut"), SetState(&PATTERN)), + (Keyword("ref"), SetState(&PATTERN)), + (Keyword("_"), SetState(&PATTERN)), + (Punct("!"), SetState(&PATTERN)), + (Punct("&"), SetState(&PATTERN)), + (Punct("..="), SetState(&PATTERN)), + (Punct(".."), SetState(&PATTERN)), + (Punct("="), SetState(&INIT)), + (Punct("@"), SetState(&PATTERN)), + (Punct("|"), SetState(&PATTERN)), + (ConsumeLiteral, SetState(&PATTERN)), + (ExpectPath, SetState(&PATTERN)), +]; + +static RANGE: [(Input, Action); 6] = [ + (Punct("..="), SetState(&INIT)), + (Punct(".."), SetState(&RANGE)), + (Punct("."), SetState(&DOT)), + (ConsumeNestedBrace, SetState(&IF_THEN)), + (Empty, Finish), + (Otherwise, SetState(&INIT)), +]; + +static RAW: [(Input, Action); 3] = [ + (Keyword("const"), SetState(&INIT)), + (Keyword("mut"), SetState(&INIT)), + (Otherwise, SetState(&POSTFIX)), +]; + +static REFERENCE: [(Input, Action); 3] = [ + (Keyword("mut"), SetState(&INIT)), + (Keyword("raw"), SetState(&RAW)), + (Otherwise, SetState(&INIT)), +]; + +static RETURN: [(Input, Action); 2] = [ + (CanBeginExpr, SetState(&INIT)), + (Otherwise, SetState(&POSTFIX)), +]; + +pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { + let mut state = INIT.as_slice(); let mut depth = 0usize; - loop { - if initial { - if consume![&] { - initial = consume![mut] || !consume![raw] || consume![const] || consume![mut]; - } else if consume![if] || consume![match] || consume![while] { - depth += 1; - } else if input.parse::>()?.is_some() - || (consume(Brace) || consume(Bracket) || consume(Parenthesis)) - || (consume![async] || consume![const] || consume![loop] || consume![unsafe]) - && (consume(Brace) || break) - { - initial = false; - } else if consume![let] { - while !consume![=] { - if !((consume![|] || consume![ref] || consume![mut] || consume![@]) - || (consume![!] || input.parse::>()?.is_some()) - || (consume![..=] || consume![..] || consume![&] || consume![_]) - || (consume(Brace) || consume(Bracket) || consume(Parenthesis))) - { - input.parse::()?; + 'table: loop { + for rule in state { + if match rule.0 { + Input::Keyword(expected) => input.step(|cursor| match cursor.ident() { + Some((ident, rest)) if ident == expected => Ok((true, rest)), + _ => Ok((false, *cursor)), + })?, + Input::Punct(expected) => input.step(|cursor| { + let begin = *cursor; + let mut cursor = begin; + for (i, ch) in expected.chars().enumerate() { + match cursor.punct() { + Some((punct, _)) if punct.as_char() != ch => break, + Some((_, rest)) if i == expected.len() - 1 => { + return Ok((true, rest)); + } + Some((punct, rest)) if punct.spacing() == Spacing::Joint => { + cursor = rest; + } + _ => break, + } } + Ok((false, begin)) + })?, + Input::ConsumeAny => input.parse::>()?.is_some(), + Input::ConsumeBinOp => input.parse::().is_ok(), + Input::ConsumeBrace | Input::ConsumeNestedBrace => { + (matches!(rule.0, Input::ConsumeBrace) || depth > 0) + && input.step(|cursor| match cursor.group(Delimiter::Brace) { + Some((_inside, _span, rest)) => Ok((true, rest)), + None => Ok((false, *cursor)), + })? } - } else if input.parse::>()?.is_some() && !consume![:] { - break; - } else if input.parse::().is_err() { - input.parse::()?; - initial = consume![!] || depth == 0 && input.peek(token::Brace); - } - } else if input.is_empty() || input.peek(Token![,]) { - return Ok(()); - } else if depth > 0 && consume(Brace) { - if consume![else] && !consume(Brace) { - initial = consume![if] || break; - } else { - depth -= 1; - } - } else if input.parse::().is_ok() || (consume![..] | consume![=]) { - initial = true; - } else if consume![.] { - if input.parse::>()?.is_none() - && (matches!(input.parse()?, Member::Named(_)) && input.peek(Token![::])) - { - input.parse::()?; + Input::ConsumeDelimiter => input.step(|cursor| match cursor.any_group() { + Some((_inside, _delimiter, _span, rest)) => Ok((true, rest)), + None => Ok((false, *cursor)), + })?, + Input::ConsumeIdent => input.parse::>()?.is_some(), + Input::ConsumeLifetime => input.parse::>()?.is_some(), + Input::ConsumeLiteral => input.parse::>()?.is_some(), + Input::ExpectPath => { + input.parse::()?; + true + } + Input::ExpectTurbofish => { + if input.peek(Token![::]) { + input.parse::()?; + } + true + } + Input::ExpectType => { + Type::without_plus(input)?; + true + } + Input::CanBeginExpr => Expr::peek(input), + Input::Otherwise => true, + Input::Empty => input.is_empty() || input.peek(Token![,]), + } { + state = match rule.1 { + Action::SetState(next) => next, + Action::IncDepth => (depth += 1, &INIT).1, + Action::DecDepth => (depth -= 1, &POSTFIX).1, + Action::Finish => return if depth == 0 { Ok(()) } else { break }, + }; + continue 'table; } - } else if consume![as] { - input.parse::()?; - } else if !(consume(Brace) || consume(Bracket) || consume(Parenthesis)) { - break; } + return Err(input.error("unsupported expression")); } - - Err(input.error("unsupported expression")) } From 0986a66e1764ed37a4931dde8c509412474636fe Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Nov 2024 21:36:43 -0700 Subject: [PATCH 5/5] Ignore enum_glob_use pedantic clippy lint warning: usage of wildcard import for enum variants --> src/scan_expr.rs:1:12 | 1 | use self::{Action::*, Input::*}; | ^^^^^^^^^ help: try: `Action::{DecDepth, Finish, IncDepth, SetState}` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use = note: `-W clippy::enum-glob-use` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::enum_glob_use)]` warning: usage of wildcard import for enum variants --> src/scan_expr.rs:1:23 | 1 | use self::{Action::*, Input::*}; | ^^^^^^^^ help: try: `Input::{CanBeginExpr, ConsumeAny, ConsumeBinOp, ConsumeBrace, ConsumeDelimiter, ConsumeIdent, ConsumeLifetime, ConsumeLiteral, ConsumeNestedBrace, Empty, ExpectPath, ExpectTurbofish, ExpectType, Keyword, Otherwise, Punct}` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 4c5c70f06..d97f3b047 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,6 +264,7 @@ clippy::derivable_impls, clippy::diverging_sub_expression, clippy::doc_markdown, + clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, clippy::explicit_auto_deref, clippy::if_not_else,