diff --git a/src/expr.rs b/src/expr.rs index 1988b544f..2c0b346f2 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -957,6 +957,8 @@ pub(crate) fn requires_terminator(expr: &Expr) -> bool { #[cfg(feature = "parsing")] pub(crate) mod parsing { use super::*; + #[cfg(feature = "full")] + use crate::ext::IdentExt; use crate::parse::discouraged::Speculative; #[cfg(feature = "full")] use crate::parse::ParseBuffer; @@ -1155,6 +1157,25 @@ pub(crate) mod parsing { } } + #[cfg(feature = "full")] + fn can_begin_expr(input: ParseStream) -> bool { + input.peek(Ident::peek_any) // value name or keyword + || input.peek(token::Paren) // tuple + || input.peek(token::Bracket) // array + || input.peek(token::Brace) // block + || input.peek(Lit) // literal + || input.peek(Token![!]) && !input.peek(Token![!=]) // operator not + || input.peek(Token![-]) && !input.peek(Token![-=]) && !input.peek(Token![->]) // unary minus + || input.peek(Token![*]) && !input.peek(Token![*=]) // dereference + || input.peek(Token![|]) && !input.peek(Token![|=]) // closure + || input.peek(Token![&]) && !input.peek(Token![&=]) // reference + || input.peek(Token![..]) // range notation + || input.peek(Token![<]) && !input.peek(Token![<=]) && !input.peek(Token![<<=]) // associated path + || input.peek(Token![::]) // global path + || input.peek(Lifetime) // labeled loop + || input.peek(Token![#]) // expression attributes + } + #[cfg(feature = "full")] fn parse_expr( input: ParseStream, @@ -2247,7 +2268,7 @@ pub(crate) mod parsing { attrs: Vec::new(), yield_token: input.parse()?, expr: { - if !input.is_empty() && !input.peek(Token![,]) && !input.peek(Token![;]) { + if can_begin_expr(input) { Some(input.parse()?) } else { None @@ -2447,15 +2468,11 @@ pub(crate) mod parsing { break_token: input.parse()?, label: input.parse()?, expr: { - if input.is_empty() - || input.peek(Token![,]) - || input.peek(Token![;]) - || !allow_struct.0 && input.peek(token::Brace) - { - None - } else { + if can_begin_expr(input) && (allow_struct.0 || !input.peek(token::Brace)) { let expr = ambiguous_expr(input, allow_struct)?; Some(Box::new(expr)) + } else { + None } }, }) @@ -2467,9 +2484,7 @@ pub(crate) mod parsing { attrs: Vec::new(), return_token: input.parse()?, expr: { - if input.is_empty() || input.peek(Token![,]) || input.peek(Token![;]) { - None - } else { + if can_begin_expr(input) { // NOTE: return is greedy and eats blocks after it even when in a // position where structs are not allowed, such as in if statement // conditions. For example: @@ -2477,6 +2492,8 @@ pub(crate) mod parsing { // if return { println!("A") } {} // Prints "A" let expr = ambiguous_expr(input, allow_struct)?; Some(Box::new(expr)) + } else { + None } }, }) diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs index aa385a1d4..9d5dfa2f7 100644 --- a/tests/repo/mod.rs +++ b/tests/repo/mod.rs @@ -56,10 +56,6 @@ static EXCLUDE_FILES: &[&str] = &[ // https://github.com/dtolnay/syn/issues/1527 "tests/ui/parser/struct-literal-in-match-guard.rs", - // TODO: precedence of return in match guard - // https://github.com/dtolnay/syn/issues/1528 - "tests/ui/unreachable-code.rs", - // Compile-fail expr parameter in const generic position: f::<1 + 2>() "tests/ui/const-generics/early/closing-args-token.rs", "tests/ui/const-generics/early/const-expression-parameter.rs",