diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f904e0c44ea92..aac8c0b310320 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -772,6 +772,20 @@ parse_unexpected_const_param_declaration = unexpected `const` parameter declarat parse_unexpected_default_value_for_lifetime_in_generic_parameters = unexpected default lifetime parameter .label = lifetime parameters cannot have default values +parse_unexpected_expr_in_pat = + expected {$is_bound -> + [true] a pattern range bound + *[false] a pattern + }, found {$is_method_call -> + [true] a method call + *[false] an expression + } + + .label = {$is_method_call -> + [true] method calls + *[false] arbitrary expressions + } are not allowed in patterns + parse_unexpected_if_with_if = unexpected `if` in the condition expression .suggestion = remove the `if` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 40852048293a9..fa91b64d5310d 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2415,6 +2415,18 @@ pub(crate) struct ExpectedCommaAfterPatternField { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_unexpected_expr_in_pat)] +pub(crate) struct UnexpectedExpressionInPattern { + #[primary_span] + #[label] + pub span: Span, + /// Was a `RangePatternBound` expected? + pub is_bound: bool, + /// Was the unexpected expression a `MethodCallExpression`? + pub is_method_call: bool, +} + #[derive(Diagnostic)] #[diag(parse_unexpected_paren_in_range_pat)] pub(crate) struct UnexpectedParenInRangePat { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b789b65797bad..c395decab129e 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -444,6 +444,19 @@ impl<'a> Parser<'a> { ) if self.restrictions.contains(Restrictions::CONST_EXPR) => { return None; } + // When recovering patterns as expressions, stop parsing when encountering an assignment `=`, an alternative `|`, or a range `..`. + ( + Some( + AssocOp::Assign + | AssocOp::AssignOp(_) + | AssocOp::BitOr + | AssocOp::DotDot + | AssocOp::DotDotEq, + ), + _, + ) if self.restrictions.contains(Restrictions::IS_PAT) => { + return None; + } (Some(op), _) => (op, self.token.span), (None, Some((Ident { name: sym::and, span }, false))) if self.may_recover() => { self.dcx().emit_err(errors::InvalidLogicalOperator { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ff2fb6271a8df..623407eb38003 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -53,6 +53,7 @@ bitflags::bitflags! { const CONST_EXPR = 1 << 2; const ALLOW_LET = 1 << 3; const IN_IF_GUARD = 1 << 4; + const IS_PAT = 1 << 5; } } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7918e03750ce3..d04921dde54b7 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,4 +1,4 @@ -use super::{ForceCollect, Parser, PathStyle, TrailingToken}; +use super::{ForceCollect, Parser, PathStyle, Restrictions, TrailingToken}; use crate::errors::{ self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, @@ -6,14 +6,14 @@ use crate::errors::{ InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, - UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, - UnexpectedVertVertInPattern, + TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, + UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, + UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, }; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter}; +use rustc_ast::token::{self, BinOpToken, Delimiter, Token}; use rustc_ast::{ self as ast, AttrVec, BindingAnnotation, ByRef, Expr, ExprKind, MacCall, Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, @@ -23,7 +23,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::Span; +use rustc_span::{ErrorGuaranteed, Span}; use thin_vec::{thin_vec, ThinVec}; #[derive(PartialEq, Copy, Clone)] @@ -336,6 +336,95 @@ impl<'a> Parser<'a> { } } + /// Ensures that the last parsed pattern (or pattern range bound) is not followed by a method call or an operator. + /// + /// `is_end_bound` indicates whether the last parsed thing was the end bound of a range pattern (see [`parse_pat_range_end`](Self::parse_pat_range_end)) + /// in order to say "expected a pattern range bound" instead of "expected a pattern"; + /// ```text + /// 0..=1 + 2 + /// ^^^^^ + /// ``` + /// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter. + #[must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some"] + fn maybe_recover_trailing_expr( + &mut self, + pat_span: Span, + is_end_bound: bool, + ) -> Option { + if self.prev_token.is_keyword(kw::Underscore) || !self.may_recover() { + // Don't recover anything after an `_` or if recovery is disabled. + return None; + } + + // Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`. + let has_trailing_method = self.check_noexpect(&token::Dot) + && self.look_ahead(1, |tok| { + tok.ident() + .and_then(|(ident, _)| ident.name.as_str().chars().next()) + .is_some_and(char::is_lowercase) + }) + && self.look_ahead(2, |tok| tok.kind == token::OpenDelim(Delimiter::Parenthesis)); + + // Check for operators. + // `|` is excluded as it is used in pattern alternatives and lambdas, + // `?` is included for error propagation, + // `[` is included for indexing operations, + // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`) + let has_trailing_operator = matches!(self.token.kind, token::BinOp(op) if op != BinOpToken::Or) + || self.token.kind == token::Question + || (self.token.kind == token::OpenDelim(Delimiter::Bracket) + && self.look_ahead(1, |tok| tok.kind != token::CloseDelim(Delimiter::Bracket))); + + if !has_trailing_method && !has_trailing_operator { + // Nothing to recover here. + return None; + } + + // Let's try to parse an expression to emit a better diagnostic. + let mut snapshot = self.create_snapshot_for_diagnostic(); + snapshot.restrictions.insert(Restrictions::IS_PAT); + + // Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten. + if let Ok(expr) = snapshot + .parse_expr_dot_or_call_with( + self.mk_expr_err(pat_span), // equivalent to transforming the parsed pattern into an `Expr` + pat_span, + AttrVec::new(), + ) + .map_err(|err| err.cancel()) + { + let non_assoc_span = expr.span; + + // Parse an associative expression such as `+ expr`, `% expr`, ... + // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. + if let Ok(expr) = + snapshot.parse_expr_assoc_with(0, expr.into()).map_err(|err| err.cancel()) + { + // We got a valid expression. + self.restore_snapshot(snapshot); + self.restrictions.remove(Restrictions::IS_PAT); + + let is_bound = is_end_bound + // is_start_bound: either `..` or `)..` + || self.token.is_range_separator() + || self.token.kind == token::CloseDelim(Delimiter::Parenthesis) + && self.look_ahead(1, Token::is_range_separator); + + // Check that `parse_expr_assoc_with` didn't eat a rhs. + let is_method_call = has_trailing_method && non_assoc_span == expr.span; + + return Some(self.dcx().emit_err(UnexpectedExpressionInPattern { + span: expr.span, + is_bound, + is_method_call, + })); + } + } + + // We got a trailing method/operator, but we couldn't parse an expression. + None + } + /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are /// allowed). fn parse_pat_with_range_pat( @@ -441,7 +530,10 @@ impl<'a> Parser<'a> { } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { self.parse_pat_tuple_struct(qself, path)? } else { - PatKind::Path(qself, path) + match self.maybe_recover_trailing_expr(span, false) { + Some(guar) => PatKind::Err(guar), + None => PatKind::Path(qself, path), + } } } else if matches!(self.token.kind, token::Lifetime(_)) // In pattern position, we're totally fine with using "next token isn't colon" @@ -470,10 +562,17 @@ impl<'a> Parser<'a> { } else { // Try to parse everything else as literal with optional minus match self.parse_literal_maybe_minus() { - Ok(begin) => match self.parse_range_end() { - Some(form) => self.parse_pat_range_begin_with(begin, form)?, - None => PatKind::Lit(begin), - }, + Ok(begin) => { + let begin = match self.maybe_recover_trailing_expr(begin.span, false) { + Some(_) => self.mk_expr_err(begin.span), + None => begin, + }; + + match self.parse_range_end() { + Some(form) => self.parse_pat_range_begin_with(begin, form)?, + None => PatKind::Lit(begin), + } + } Err(err) => return self.fatal_unexpected_non_pat(err, expected), } }; @@ -615,6 +714,21 @@ impl<'a> Parser<'a> { self.parse_pat_range_begin_with(begin.clone(), form)? } + // recover ranges with parentheses around the `(start)..` + PatKind::Err(_) + if self.may_recover() + && let Some(form) = self.parse_range_end() => + { + self.dcx().emit_err(UnexpectedParenInRangePat { + span: vec![open_paren, close_paren], + sugg: UnexpectedParenInRangePatSugg { + start_span: open_paren, + end_span: close_paren, + }, + }); + + self.parse_pat_range_begin_with(self.mk_expr(pat.span, ExprKind::Err), form)? + } // (pat) with optional parentheses _ => PatKind::Paren(pat), @@ -853,6 +967,8 @@ impl<'a> Parser<'a> { self.parse_literal_maybe_minus() }?; + let recovered = self.maybe_recover_trailing_expr(bound.span, true); + // recover trailing `)` if let Some(open_paren) = open_paren { self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; @@ -866,7 +982,10 @@ impl<'a> Parser<'a> { }); } - Ok(bound) + Ok(match recovered { + Some(_) => self.mk_expr_err(bound.span), + None => bound, + }) } /// Is this the start of a pattern beginning with a path? @@ -929,7 +1048,17 @@ impl<'a> Parser<'a> { .create_err(EnumPatternInsteadOfIdentifier { span: self.prev_token.span })); } - Ok(PatKind::Ident(binding_annotation, ident, sub)) + // Check for method calls after the `ident`, + // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`. + + let pat = if sub.is_none() + && let Some(guar) = self.maybe_recover_trailing_expr(ident.span, false) + { + PatKind::Err(guar) + } else { + PatKind::Ident(binding_annotation, ident, sub) + }; + Ok(pat) } /// Parse a struct ("record") pattern (e.g. `Foo { ... }` or `Foo::Bar { ... }`). diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.rs b/tests/ui/half-open-range-patterns/range_pat_interactions1.rs index 55353999b6788..0c050c550c4ce 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions1.rs +++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.rs @@ -17,12 +17,18 @@ fn main() { } match x as i32 { 0..5+1 => errors_only.push(x), - //~^ error: expected one of `=>`, `if`, or `|`, found `+` + //~^ error: expected a pattern range bound, found an expression + //~| error: exclusive range pattern syntax is experimental 1 | -3..0 => first_or.push(x), + //~^ error: exclusive range pattern syntax is experimental y @ (0..5 | 6) => or_two.push(y), + //~^ error: exclusive range pattern syntax is experimental y @ 0..const { 5 + 1 } => assert_eq!(y, 5), + //~^ error: exclusive range pattern syntax is experimental + //~| error: inline-const in pattern position is experimental y @ -5.. => range_from.push(y), y @ ..-7 => assert_eq!(y, -8), + //~^ error: exclusive range pattern syntax is experimental y => bottom.push(y), } } diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr index cf3bde9705b6b..cc481f7a79e13 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr @@ -1,8 +1,8 @@ -error: expected one of `=>`, `if`, or `|`, found `+` - --> $DIR/range_pat_interactions1.rs:19:17 +error: expected a pattern range bound, found an expression + --> $DIR/range_pat_interactions1.rs:19:16 | LL | 0..5+1 => errors_only.push(x), - | ^ expected one of `=>`, `if`, or `|` + | ^^^ arbitrary expressions are not allowed in patterns error[E0408]: variable `n` is not bound in all patterns --> $DIR/range_pat_interactions1.rs:10:25 @@ -12,6 +12,16 @@ LL | if let n @ 2..3|4 = x { | | | variable not in all patterns +error[E0658]: inline-const in pattern position is experimental + --> $DIR/range_pat_interactions1.rs:26:20 + | +LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5), + | ^^^^^ + | + = note: see issue #76001 for more information + = help: add `#![feature(inline_const_pat)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: exclusive range pattern syntax is experimental --> $DIR/range_pat_interactions1.rs:10:20 | @@ -34,7 +44,62 @@ LL | } else if let 2..3 | 4 = x { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: use an inclusive range pattern, like N..=M -error: aborting due to 4 previous errors +error[E0658]: exclusive range pattern syntax is experimental + --> $DIR/range_pat_interactions1.rs:19:13 + | +LL | 0..5+1 => errors_only.push(x), + | ^^^^^^ + | + = note: see issue #37854 for more information + = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M + +error[E0658]: exclusive range pattern syntax is experimental + --> $DIR/range_pat_interactions1.rs:22:17 + | +LL | 1 | -3..0 => first_or.push(x), + | ^^^^^ + | + = note: see issue #37854 for more information + = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M + +error[E0658]: exclusive range pattern syntax is experimental + --> $DIR/range_pat_interactions1.rs:24:18 + | +LL | y @ (0..5 | 6) => or_two.push(y), + | ^^^^ + | + = note: see issue #37854 for more information + = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M + +error[E0658]: exclusive range pattern syntax is experimental + --> $DIR/range_pat_interactions1.rs:26:17 + | +LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5), + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #37854 for more information + = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M + +error[E0658]: exclusive range pattern syntax is experimental + --> $DIR/range_pat_interactions1.rs:30:17 + | +LL | y @ ..-7 => assert_eq!(y, -8), + | ^^^^ + | + = note: see issue #37854 for more information + = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M + +error: aborting due to 10 previous errors Some errors have detailed explanations: E0408, E0658. For more information about an error, try `rustc --explain E0408`. diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs b/tests/ui/half-open-range-patterns/range_pat_interactions2.rs index 0e96cfe785857..068104c4b1b8c 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.rs +++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.rs @@ -8,12 +8,18 @@ fn main() { for x in -9 + 1..=(9 - 2) { match x as i32 { 0..=(5+1) => errors_only.push(x), - //~^ error: expected `)`, found `+` + //~^ error: expected a pattern range bound, found an expression + //~| error: range pattern bounds cannot have parentheses 1 | -3..0 => first_or.push(x), + //~^ error: exclusive range pattern syntax is experimental y @ (0..5 | 6) => or_two.push(y), + //~^ error: exclusive range pattern syntax is experimental y @ 0..const { 5 + 1 } => assert_eq!(y, 5), + //~^ error: inline-const in pattern position is experimental + //~| error: exclusive range pattern syntax is experimental y @ -5.. => range_from.push(y), y @ ..-7 => assert_eq!(y, -8), + //~^ error: exclusive range pattern syntax is experimental y => bottom.push(y), } } diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr index a54f29a3b3263..8f21a6149fb89 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr @@ -1,8 +1,75 @@ -error: expected `)`, found `+` - --> $DIR/range_pat_interactions2.rs:10:19 +error: expected a pattern range bound, found an expression + --> $DIR/range_pat_interactions2.rs:10:18 | LL | 0..=(5+1) => errors_only.push(x), - | ^ expected `)` + | ^^^ arbitrary expressions are not allowed in patterns -error: aborting due to 1 previous error +error: range pattern bounds cannot have parentheses + --> $DIR/range_pat_interactions2.rs:10:17 + | +LL | 0..=(5+1) => errors_only.push(x), + | ^ ^ + | +help: remove these parentheses + | +LL - 0..=(5+1) => errors_only.push(x), +LL + 0..=5+1 => errors_only.push(x), + | + +error[E0658]: inline-const in pattern position is experimental + --> $DIR/range_pat_interactions2.rs:17:20 + | +LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5), + | ^^^^^ + | + = note: see issue #76001 for more information + = help: add `#![feature(inline_const_pat)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: exclusive range pattern syntax is experimental + --> $DIR/range_pat_interactions2.rs:13:17 + | +LL | 1 | -3..0 => first_or.push(x), + | ^^^^^ + | + = note: see issue #37854 for more information + = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M + +error[E0658]: exclusive range pattern syntax is experimental + --> $DIR/range_pat_interactions2.rs:15:18 + | +LL | y @ (0..5 | 6) => or_two.push(y), + | ^^^^ + | + = note: see issue #37854 for more information + = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M + +error[E0658]: exclusive range pattern syntax is experimental + --> $DIR/range_pat_interactions2.rs:17:17 + | +LL | y @ 0..const { 5 + 1 } => assert_eq!(y, 5), + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #37854 for more information + = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M + +error[E0658]: exclusive range pattern syntax is experimental + --> $DIR/range_pat_interactions2.rs:21:17 + | +LL | y @ ..-7 => assert_eq!(y, -8), + | ^^^^ + | + = note: see issue #37854 for more information + = help: add `#![feature(exclusive_range_pattern)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use an inclusive range pattern, like N..=M + +error: aborting due to 7 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/issues/issue-24197.rs b/tests/ui/parser/issues/issue-24197.rs index aaf5137461fa6..9bba16e5596ff 100644 --- a/tests/ui/parser/issues/issue-24197.rs +++ b/tests/ui/parser/issues/issue-24197.rs @@ -1,3 +1,3 @@ fn main() { - let buf[0] = 0; //~ ERROR expected one of `:`, `;`, `=`, `@`, or `|`, found `[` + let buf[0] = 0; //~ error: expected a pattern, found an expression } diff --git a/tests/ui/parser/issues/issue-24197.stderr b/tests/ui/parser/issues/issue-24197.stderr index 3ef707f3953cf..7ebbf4ac370d7 100644 --- a/tests/ui/parser/issues/issue-24197.stderr +++ b/tests/ui/parser/issues/issue-24197.stderr @@ -1,8 +1,8 @@ -error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` - --> $DIR/issue-24197.rs:2:12 +error: expected a pattern, found an expression + --> $DIR/issue-24197.rs:2:9 | LL | let buf[0] = 0; - | ^ expected one of `:`, `;`, `=`, `@`, or `|` + | ^^^^^^ arbitrary expressions are not allowed in patterns error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-24375.rs b/tests/ui/parser/issues/issue-24375.rs index 1d128d33e4f49..a5e256b7f1554 100644 --- a/tests/ui/parser/issues/issue-24375.rs +++ b/tests/ui/parser/issues/issue-24375.rs @@ -3,7 +3,7 @@ static tmp : [&'static str; 2] = ["hello", "he"]; fn main() { let z = "hello"; match z { - tmp[0] => {} //~ ERROR expected one of `=>`, `@`, `if`, or `|`, found `[` + tmp[0] => {} //~ error: expected a pattern, found an expression _ => {} } } diff --git a/tests/ui/parser/issues/issue-24375.stderr b/tests/ui/parser/issues/issue-24375.stderr index bb1e19e9e6d8d..e6ef07d13fd8d 100644 --- a/tests/ui/parser/issues/issue-24375.stderr +++ b/tests/ui/parser/issues/issue-24375.stderr @@ -1,8 +1,8 @@ -error: expected one of `=>`, `@`, `if`, or `|`, found `[` - --> $DIR/issue-24375.rs:6:12 +error: expected a pattern, found an expression + --> $DIR/issue-24375.rs:6:9 | LL | tmp[0] => {} - | ^ expected one of `=>`, `@`, `if`, or `|` + | ^^^^^^ arbitrary expressions are not allowed in patterns error: aborting due to 1 previous error diff --git a/tests/ui/parser/pat-lt-bracket-5.rs b/tests/ui/parser/pat-lt-bracket-5.rs index aaece1f6bd9cb..6d784494d56a3 100644 --- a/tests/ui/parser/pat-lt-bracket-5.rs +++ b/tests/ui/parser/pat-lt-bracket-5.rs @@ -1,3 +1,5 @@ fn main() { - let v[0] = v[1]; //~ ERROR expected one of `:`, `;`, `=`, `@`, or `|`, found `[` + let v[0] = v[1]; + //~^ error: expected a pattern, found an expression + //~| error: cannot find value `v` in this scope } diff --git a/tests/ui/parser/pat-lt-bracket-5.stderr b/tests/ui/parser/pat-lt-bracket-5.stderr index e556e6c0206fb..18cf2df028216 100644 --- a/tests/ui/parser/pat-lt-bracket-5.stderr +++ b/tests/ui/parser/pat-lt-bracket-5.stderr @@ -1,8 +1,15 @@ -error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` - --> $DIR/pat-lt-bracket-5.rs:2:10 +error: expected a pattern, found an expression + --> $DIR/pat-lt-bracket-5.rs:2:9 | LL | let v[0] = v[1]; - | ^ expected one of `:`, `;`, `=`, `@`, or `|` + | ^^^^ arbitrary expressions are not allowed in patterns -error: aborting due to 1 previous error +error[E0425]: cannot find value `v` in this scope + --> $DIR/pat-lt-bracket-5.rs:2:16 + | +LL | let v[0] = v[1]; + | ^ not found in this scope + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/parser/pat-lt-bracket-6.rs b/tests/ui/parser/pat-lt-bracket-6.rs index 7becffa9fe2f7..496525ed5370c 100644 --- a/tests/ui/parser/pat-lt-bracket-6.rs +++ b/tests/ui/parser/pat-lt-bracket-6.rs @@ -3,7 +3,8 @@ fn main() { let x = Test(&0, []); let Test(&desc[..]) = x; - //~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[` + //~^ error: expected a pattern, found an expression + //~| error: this pattern has 1 field, but the corresponding tuple struct has 2 fields } const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types diff --git a/tests/ui/parser/pat-lt-bracket-6.stderr b/tests/ui/parser/pat-lt-bracket-6.stderr index 035d0dbfe06d6..10c638a63e44c 100644 --- a/tests/ui/parser/pat-lt-bracket-6.stderr +++ b/tests/ui/parser/pat-lt-bracket-6.stderr @@ -1,18 +1,30 @@ -error: expected one of `)`, `,`, `@`, or `|`, found `[` - --> $DIR/pat-lt-bracket-6.rs:5:19 +error: expected a pattern, found an expression + --> $DIR/pat-lt-bracket-6.rs:5:15 | LL | let Test(&desc[..]) = x; - | ^ - | | - | expected one of `)`, `,`, `@`, or `|` - | help: missing `,` + | ^^^^^^^^ arbitrary expressions are not allowed in patterns error[E0308]: mismatched types - --> $DIR/pat-lt-bracket-6.rs:9:30 + --> $DIR/pat-lt-bracket-6.rs:10:30 | LL | const RECOVERY_WITNESS: () = 0; | ^ expected `()`, found integer -error: aborting due to 2 previous errors +error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields + --> $DIR/pat-lt-bracket-6.rs:5:14 + | +LL | struct Test(&'static u8, [u8; 0]); + | ----------- ------- tuple struct has 2 fields +... +LL | let Test(&desc[..]) = x; + | ^^^^^^^^^ expected 2 fields, found 1 + | +help: use `_` to explicitly ignore each field + | +LL | let Test(&desc[..], _) = x; + | +++ + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0023, E0308. +For more information about an error, try `rustc --explain E0023`. diff --git a/tests/ui/parser/pat-ranges-3.rs b/tests/ui/parser/pat-ranges-3.rs index 8976dcf0d90f9..419768a2a2022 100644 --- a/tests/ui/parser/pat-ranges-3.rs +++ b/tests/ui/parser/pat-ranges-3.rs @@ -1,5 +1,9 @@ // Parsing of range patterns fn main() { - let 10 ..= 10 + 3 = 12; //~ expected one of `:`, `;`, `=`, or `|`, found `+` + let 10 ..= 10 + 3 = 12; + //~^ error: expected a pattern range bound, found an expression + + let 10 - 3 ..= 10 = 8; + //~^ error: expected a pattern range bound, found an expression } diff --git a/tests/ui/parser/pat-ranges-3.stderr b/tests/ui/parser/pat-ranges-3.stderr index 611b35a650280..5e1f35d1b6f9a 100644 --- a/tests/ui/parser/pat-ranges-3.stderr +++ b/tests/ui/parser/pat-ranges-3.stderr @@ -1,8 +1,14 @@ -error: expected one of `:`, `;`, `=`, or `|`, found `+` - --> $DIR/pat-ranges-3.rs:4:19 +error: expected a pattern range bound, found an expression + --> $DIR/pat-ranges-3.rs:4:16 | LL | let 10 ..= 10 + 3 = 12; - | ^ expected one of `:`, `;`, `=`, or `|` + | ^^^^^^ arbitrary expressions are not allowed in patterns -error: aborting due to 1 previous error +error: expected a pattern range bound, found an expression + --> $DIR/pat-ranges-3.rs:7:9 + | +LL | let 10 - 3 ..= 10 = 8; + | ^^^^^^ arbitrary expressions are not allowed in patterns + +error: aborting due to 2 previous errors diff --git a/tests/ui/parser/pat-ranges-4.rs b/tests/ui/parser/pat-ranges-4.rs deleted file mode 100644 index 61188976b028c..0000000000000 --- a/tests/ui/parser/pat-ranges-4.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Parsing of range patterns - -fn main() { - let 10 - 3 ..= 10 = 8; - //~^ error: expected one of `...`, `..=`, `..`, `:`, `;`, `=`, or `|`, found `-` -} diff --git a/tests/ui/parser/pat-ranges-4.stderr b/tests/ui/parser/pat-ranges-4.stderr deleted file mode 100644 index c30160291d608..0000000000000 --- a/tests/ui/parser/pat-ranges-4.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `...`, `..=`, `..`, `:`, `;`, `=`, or `|`, found `-` - --> $DIR/pat-ranges-4.rs:4:12 - | -LL | let 10 - 3 ..= 10 = 8; - | ^ expected one of 7 possible tokens - -error: aborting due to 1 previous error - diff --git a/tests/ui/parser/pat-recover-exprs.rs b/tests/ui/parser/pat-recover-exprs.rs new file mode 100644 index 0000000000000..ecd471467e380 --- /dev/null +++ b/tests/ui/parser/pat-recover-exprs.rs @@ -0,0 +1,28 @@ +fn main() { + match u8::MAX { + u8::MAX.abs() => (), + //~^ error: expected a pattern, found a method call + x.sqrt() @ .. => (), + //~^ error: expected a pattern, found a method call + //~| error: left-hand side of `@` must be a binding + z @ w @ v.u() => (), + //~^ error: expected a pattern, found a method call + y.ilog(3) => (), + //~^ error: expected a pattern, found a method call + n + 1 => (), + //~^ error: expected a pattern, found an expression + ("".f() + 14 * 8) => (), + //~^ error: expected a pattern, found an expression + 0 | ((1) | 2) | 3 => (), + f?() => (), + //~^ error: expected a pattern, found an expression + (_ + 1) => (), + //~^ error: expected one of `)`, `,`, or `|`, found `+` + } + + let 1 + 1 = 2; + //~^ error: expected a pattern, found an expression + + let b = matches!(x, (x * x | x.f()) | x[0]); + //~^ error: expected one of `)`, `,`, `@`, or `|`, found `*` +} diff --git a/tests/ui/parser/pat-recover-exprs.stderr b/tests/ui/parser/pat-recover-exprs.stderr new file mode 100644 index 0000000000000..787fd03b0c30d --- /dev/null +++ b/tests/ui/parser/pat-recover-exprs.stderr @@ -0,0 +1,76 @@ +error: expected a pattern, found a method call + --> $DIR/pat-recover-exprs.rs:3:9 + | +LL | u8::MAX.abs() => (), + | ^^^^^^^^^^^^^ method calls are not allowed in patterns + +error: expected a pattern, found a method call + --> $DIR/pat-recover-exprs.rs:5:9 + | +LL | x.sqrt() @ .. => (), + | ^^^^^^^^ method calls are not allowed in patterns + +error: left-hand side of `@` must be a binding + --> $DIR/pat-recover-exprs.rs:5:9 + | +LL | x.sqrt() @ .. => (), + | --------^^^-- + | | | + | | also a pattern + | interpreted as a pattern, not a binding + | + = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x` + +error: expected a pattern, found a method call + --> $DIR/pat-recover-exprs.rs:8:17 + | +LL | z @ w @ v.u() => (), + | ^^^^^ method calls are not allowed in patterns + +error: expected a pattern, found a method call + --> $DIR/pat-recover-exprs.rs:10:9 + | +LL | y.ilog(3) => (), + | ^^^^^^^^^ method calls are not allowed in patterns + +error: expected a pattern, found an expression + --> $DIR/pat-recover-exprs.rs:12:9 + | +LL | n + 1 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + +error: expected a pattern, found an expression + --> $DIR/pat-recover-exprs.rs:14:10 + | +LL | ("".f() + 14 * 8) => (), + | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + +error: expected a pattern, found an expression + --> $DIR/pat-recover-exprs.rs:17:9 + | +LL | f?() => (), + | ^^^^ arbitrary expressions are not allowed in patterns + +error: expected one of `)`, `,`, or `|`, found `+` + --> $DIR/pat-recover-exprs.rs:19:12 + | +LL | (_ + 1) => (), + | ^ expected one of `)`, `,`, or `|` + +error: expected a pattern, found an expression + --> $DIR/pat-recover-exprs.rs:23:9 + | +LL | let 1 + 1 = 2; + | ^^^^^ arbitrary expressions are not allowed in patterns + +error: expected one of `)`, `,`, `@`, or `|`, found `*` + --> $DIR/pat-recover-exprs.rs:26:28 + | +LL | let b = matches!(x, (x * x | x.f()) | x[0]); + | ^ expected one of `)`, `,`, `@`, or `|` + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL + | + = note: while parsing argument for this `pat` macro fragment + +error: aborting due to 11 previous errors + diff --git a/tests/ui/parser/pat-recover-methodcalls.rs b/tests/ui/parser/pat-recover-methodcalls.rs new file mode 100644 index 0000000000000..54104e9a53559 --- /dev/null +++ b/tests/ui/parser/pat-recover-methodcalls.rs @@ -0,0 +1,37 @@ +struct Foo(String); +struct Bar { baz: String } + +fn foo(foo: Foo) -> bool { + match foo { + Foo("hi".to_owned()) => true, + //~^ error: expected a pattern, found a method call + _ => false + } +} + +fn bar(bar: Bar) -> bool { + match bar { + Bar { baz: "hi".to_owned() } => true, + //~^ error: expected a pattern, found a method call + _ => false + } +} + +fn baz() { // issue #90121 + let foo = vec!["foo".to_string()]; + + match foo.as_slice() { + &["foo".to_string()] => {} + //~^ error: expected a pattern, found a method call + _ => {} + }; +} + +fn main() { + if let (-1.some(4)) = (0, Some(4)) {} + //~^ error: expected a pattern, found a method call + + if let (-1.Some(4)) = (0, Some(4)) {} + //~^ error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found `.` + //~| help: missing `,` +} diff --git a/tests/ui/parser/pat-recover-methodcalls.stderr b/tests/ui/parser/pat-recover-methodcalls.stderr new file mode 100644 index 0000000000000..1f9ae81dc0c55 --- /dev/null +++ b/tests/ui/parser/pat-recover-methodcalls.stderr @@ -0,0 +1,35 @@ +error: expected a pattern, found a method call + --> $DIR/pat-recover-methodcalls.rs:6:13 + | +LL | Foo("hi".to_owned()) => true, + | ^^^^^^^^^^^^^^^ method calls are not allowed in patterns + +error: expected a pattern, found a method call + --> $DIR/pat-recover-methodcalls.rs:14:20 + | +LL | Bar { baz: "hi".to_owned() } => true, + | ^^^^^^^^^^^^^^^ method calls are not allowed in patterns + +error: expected a pattern, found a method call + --> $DIR/pat-recover-methodcalls.rs:24:11 + | +LL | &["foo".to_string()] => {} + | ^^^^^^^^^^^^^^^^^ method calls are not allowed in patterns + +error: expected a pattern, found a method call + --> $DIR/pat-recover-methodcalls.rs:31:13 + | +LL | if let (-1.some(4)) = (0, Some(4)) {} + | ^^^^^^^^^^ method calls are not allowed in patterns + +error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found `.` + --> $DIR/pat-recover-methodcalls.rs:34:15 + | +LL | if let (-1.Some(4)) = (0, Some(4)) {} + | ^ + | | + | expected one of `)`, `,`, `...`, `..=`, `..`, or `|` + | help: missing `,` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/parser/pat-recover-ranges.rs b/tests/ui/parser/pat-recover-ranges.rs index 65a6fc6fe21f3..7d77e950d905a 100644 --- a/tests/ui/parser/pat-recover-ranges.rs +++ b/tests/ui/parser/pat-recover-ranges.rs @@ -8,6 +8,22 @@ fn main() { (0)..=(-4) => (), //~^ error: range pattern bounds cannot have parentheses //~| error: range pattern bounds cannot have parentheses + ..=1 + 2 => (), + //~^ error: expected a pattern range bound, found an expression + (4).. => (), + //~^ error: range pattern bounds cannot have parentheses + (-4 + 0).. => (), + //~^ error: expected a pattern range bound, found an expression + //~| error: range pattern bounds cannot have parentheses + (1 + 4)...1 * 2 => (), + //~^ error: expected a pattern range bound, found an expression + //~| error: expected a pattern range bound, found an expression + //~| error: range pattern bounds cannot have parentheses + //~| warning: `...` range patterns are deprecated + //~| warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + 0.x()..="y".z() => (), + //~^ error: expected a pattern range bound, found a method call + //~| error: expected a pattern range bound, found a method call }; } diff --git a/tests/ui/parser/pat-recover-ranges.stderr b/tests/ui/parser/pat-recover-ranges.stderr index 0d722b5aa95c8..a7d62bd7f8ad2 100644 --- a/tests/ui/parser/pat-recover-ranges.stderr +++ b/tests/ui/parser/pat-recover-ranges.stderr @@ -46,5 +46,87 @@ LL - (0)..=(-4) => (), LL + (0)..=-4 => (), | -error: aborting due to 4 previous errors +error: expected a pattern range bound, found an expression + --> $DIR/pat-recover-ranges.rs:11:12 + | +LL | ..=1 + 2 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:13:9 + | +LL | (4).. => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (4).. => (), +LL + 4.. => (), + | + +error: expected a pattern range bound, found an expression + --> $DIR/pat-recover-ranges.rs:15:10 + | +LL | (-4 + 0).. => (), + | ^^^^^^ arbitrary expressions are not allowed in patterns + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:15:9 + | +LL | (-4 + 0).. => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (-4 + 0).. => (), +LL + -4 + 0.. => (), + | + +error: expected a pattern range bound, found an expression + --> $DIR/pat-recover-ranges.rs:18:10 + | +LL | (1 + 4)...1 * 2 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-ranges.rs:18:9 + | +LL | (1 + 4)...1 * 2 => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (1 + 4)...1 * 2 => (), +LL + 1 + 4...1 * 2 => (), + | + +error: expected a pattern range bound, found an expression + --> $DIR/pat-recover-ranges.rs:18:19 + | +LL | (1 + 4)...1 * 2 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + +error: expected a pattern range bound, found a method call + --> $DIR/pat-recover-ranges.rs:24:9 + | +LL | 0.x()..="y".z() => (), + | ^^^^^ method calls are not allowed in patterns + +error: expected a pattern range bound, found a method call + --> $DIR/pat-recover-ranges.rs:24:17 + | +LL | 0.x()..="y".z() => (), + | ^^^^^^^ method calls are not allowed in patterns + +warning: `...` range patterns are deprecated + --> $DIR/pat-recover-ranges.rs:18:16 + | +LL | (1 + 4)...1 * 2 => (), + | ^^^ help: use `..=` for an inclusive range + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see + = note: `#[warn(ellipsis_inclusive_range_patterns)]` on by default + +error: aborting due to 13 previous errors; 1 warning emitted diff --git a/tests/ui/parser/pat-recover-wildcards.rs b/tests/ui/parser/pat-recover-wildcards.rs new file mode 100644 index 0000000000000..f506e2223d608 --- /dev/null +++ b/tests/ui/parser/pat-recover-wildcards.rs @@ -0,0 +1,61 @@ +// check that we can't do funny things with wildcards. + +fn a() { + match 1 { + _ + 1 => () //~ error: expected one of `=>`, `if`, or `|`, found `+` + } +} + +fn b() { + match 2 { + (_ % 4) => () //~ error: expected one of `)`, `,`, or `|`, found `%` + } +} + +fn c() { + match 3 { + _.x() => () //~ error: expected one of `=>`, `if`, or `|`, found `.` + } +} + +fn d() { + match 4 { + _..=4 => () //~ error: expected one of `=>`, `if`, or `|`, found `..=` + } +} + +fn e() { + match 5 { + .._ => () //~ error: expected one of `=>`, `if`, or `|`, found reserved identifier `_` + } +} + +fn f() { + match 6 { + 0..._ => () + //~^ error: inclusive range with no end + //~| error: expected one of `=>`, `if`, or `|`, found reserved identifier `_` + } +} + +fn g() { + match 7 { + (_ * 0)..5 => () //~ error: expected one of `)`, `,`, or `|`, found `*` + } +} + +fn h() { + match 8 { + ..(_) => () //~ error: expected one of `=>`, `if`, or `|`, found `(` + } +} + +fn i() { + match 9 { + 4..=(2 + _) => () + //~^ error: expected a pattern range bound, found an expression + //~| error: range pattern bounds cannot have parentheses + } +} + +fn main() {} diff --git a/tests/ui/parser/pat-recover-wildcards.stderr b/tests/ui/parser/pat-recover-wildcards.stderr new file mode 100644 index 0000000000000..2b0c9bbc5be86 --- /dev/null +++ b/tests/ui/parser/pat-recover-wildcards.stderr @@ -0,0 +1,77 @@ +error: expected one of `=>`, `if`, or `|`, found `+` + --> $DIR/pat-recover-wildcards.rs:5:11 + | +LL | _ + 1 => () + | ^ expected one of `=>`, `if`, or `|` + +error: expected one of `)`, `,`, or `|`, found `%` + --> $DIR/pat-recover-wildcards.rs:11:12 + | +LL | (_ % 4) => () + | ^ expected one of `)`, `,`, or `|` + +error: expected one of `=>`, `if`, or `|`, found `.` + --> $DIR/pat-recover-wildcards.rs:17:10 + | +LL | _.x() => () + | ^ expected one of `=>`, `if`, or `|` + +error: expected one of `=>`, `if`, or `|`, found `..=` + --> $DIR/pat-recover-wildcards.rs:23:10 + | +LL | _..=4 => () + | ^^^ expected one of `=>`, `if`, or `|` + +error: expected one of `=>`, `if`, or `|`, found reserved identifier `_` + --> $DIR/pat-recover-wildcards.rs:29:11 + | +LL | .._ => () + | ^ expected one of `=>`, `if`, or `|` + +error[E0586]: inclusive range with no end + --> $DIR/pat-recover-wildcards.rs:35:10 + | +LL | 0..._ => () + | ^^^ help: use `..` instead + | + = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) + +error: expected one of `=>`, `if`, or `|`, found reserved identifier `_` + --> $DIR/pat-recover-wildcards.rs:35:13 + | +LL | 0..._ => () + | ^ expected one of `=>`, `if`, or `|` + +error: expected one of `)`, `,`, or `|`, found `*` + --> $DIR/pat-recover-wildcards.rs:43:12 + | +LL | (_ * 0)..5 => () + | ^ expected one of `)`, `,`, or `|` + +error: expected one of `=>`, `if`, or `|`, found `(` + --> $DIR/pat-recover-wildcards.rs:49:11 + | +LL | ..(_) => () + | ^ expected one of `=>`, `if`, or `|` + +error: expected a pattern range bound, found an expression + --> $DIR/pat-recover-wildcards.rs:55:14 + | +LL | 4..=(2 + _) => () + | ^^^^^ arbitrary expressions are not allowed in patterns + +error: range pattern bounds cannot have parentheses + --> $DIR/pat-recover-wildcards.rs:55:13 + | +LL | 4..=(2 + _) => () + | ^ ^ + | +help: remove these parentheses + | +LL - 4..=(2 + _) => () +LL + 4..=2 + _ => () + | + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0586`.