1
Fork 0

Rollup merge of #114300 - MU001999:fix/turbofish-pat, r=estebank

Suggests turbofish in patterns

Fixes #114112

r? ```@estebank```
This commit is contained in:
Matthias Krüger 2023-08-03 17:29:07 +02:00 committed by GitHub
commit 51d1dacdc2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 125 additions and 42 deletions

View file

@ -272,6 +272,8 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}`
parse_function_body_equals_expr = function body cannot be `= expression;` parse_function_body_equals_expr = function body cannot be `= expression;`
.suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;`
parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax
parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets
.suggestion = surround the type parameters with angle brackets .suggestion = surround the type parameters with angle brackets

View file

@ -2738,3 +2738,17 @@ pub(crate) struct WhereClauseBeforeConstBodySugg {
#[suggestion_part(code = "")] #[suggestion_part(code = "")]
pub right: Span, pub right: Span,
} }
#[derive(Diagnostic)]
#[diag(parse_generic_args_in_pat_require_turbofish_syntax)]
pub(crate) struct GenericArgsInPatRequireTurbofishSyntax {
#[primary_span]
pub span: Span,
#[suggestion(
parse_sugg_turbofish_syntax,
style = "verbose",
code = "::",
applicability = "maybe-incorrect"
)]
pub suggest_turbofish: Span,
}

View file

@ -2107,7 +2107,7 @@ impl<'a> Parser<'a> {
} }
pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> { pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName))?; let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?;
self.expect(&token::Colon)?; self.expect(&token::Colon)?;
let ty = self.parse_ty()?; let ty = self.parse_ty()?;
@ -2515,7 +2515,7 @@ impl<'a> Parser<'a> {
// Skip the `:`. // Skip the `:`.
snapshot_pat.bump(); snapshot_pat.bump();
snapshot_type.bump(); snapshot_type.bump();
match snapshot_pat.parse_pat_no_top_alt(expected) { match snapshot_pat.parse_pat_no_top_alt(expected, None) {
Err(inner_err) => { Err(inner_err) => {
inner_err.cancel(); inner_err.cancel();
} }
@ -2779,7 +2779,7 @@ impl<'a> Parser<'a> {
/// sequence of patterns until `)` is reached. /// sequence of patterns until `)` is reached.
fn skip_pat_list(&mut self) -> PResult<'a, ()> { fn skip_pat_list(&mut self) -> PResult<'a, ()> {
while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) { while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) {
self.parse_pat_no_top_alt(None)?; self.parse_pat_no_top_alt(None, None)?;
if !self.eat(&token::Comma) { if !self.eat(&token::Comma) {
return Ok(()); return Ok(());
} }

View file

@ -2338,7 +2338,7 @@ impl<'a> Parser<'a> {
let lo = self.token.span; let lo = self.token.span;
let attrs = self.parse_outer_attributes()?; let attrs = self.parse_outer_attributes()?;
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName))?; let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?;
let ty = if this.eat(&token::Colon) { let ty = if this.eat(&token::Colon) {
this.parse_ty()? this.parse_ty()?
} else { } else {
@ -2781,7 +2781,7 @@ impl<'a> Parser<'a> {
return None; return None;
} }
let pre_pat_snapshot = self.create_snapshot_for_diagnostic(); let pre_pat_snapshot = self.create_snapshot_for_diagnostic();
match self.parse_pat_no_top_alt(None) { match self.parse_pat_no_top_alt(None, None) {
Ok(_pat) => { Ok(_pat) => {
if self.token.kind == token::FatArrow { if self.token.kind == token::FatArrow {
// Reached arm end. // Reached arm end.

View file

@ -131,7 +131,7 @@ impl<'a> Parser<'a> {
}, },
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => { NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => {
token::NtPat(self.collect_tokens_no_attrs(|this| match kind { token::NtPat(self.collect_tokens_no_attrs(|this| match kind {
NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None), NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None),
NonterminalKind::PatWithOr { .. } => this.parse_pat_allow_top_alt( NonterminalKind::PatWithOr { .. } => this.parse_pat_allow_top_alt(
None, None,
RecoverComma::No, RecoverComma::No,

View file

@ -2,10 +2,11 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken};
use crate::errors::{ use crate::errors::{
self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax,
InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
RemoveLet, RepeatedMutInPattern, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern,
TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed,
UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
UnexpectedVertVertInPattern, UnexpectedVertVertInPattern,
}; };
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
@ -80,7 +81,8 @@ enum EatOrResult {
} }
/// The syntax location of a given pattern. Used for diagnostics. /// The syntax location of a given pattern. Used for diagnostics.
pub(super) enum PatternLocation { #[derive(Clone, Copy)]
pub enum PatternLocation {
LetBinding, LetBinding,
FunctionParameter, FunctionParameter,
} }
@ -91,8 +93,12 @@ impl<'a> Parser<'a> {
/// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns
/// at the top level. Used when parsing the parameters of lambda expressions, /// at the top level. Used when parsing the parameters of lambda expressions,
/// functions, function pointers, and `pat` macro fragments. /// functions, function pointers, and `pat` macro fragments.
pub fn parse_pat_no_top_alt(&mut self, expected: Option<Expected>) -> PResult<'a, P<Pat>> { pub fn parse_pat_no_top_alt(
self.parse_pat_with_range_pat(true, expected) &mut self,
expected: Option<Expected>,
syntax_loc: Option<PatternLocation>,
) -> PResult<'a, P<Pat>> {
self.parse_pat_with_range_pat(true, expected, syntax_loc)
} }
/// Parses a pattern. /// Parses a pattern.
@ -110,7 +116,7 @@ impl<'a> Parser<'a> {
ra: RecoverColon, ra: RecoverColon,
rt: CommaRecoveryMode, rt: CommaRecoveryMode,
) -> PResult<'a, P<Pat>> { ) -> PResult<'a, P<Pat>> {
self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt).map(|(pat, _)| pat) self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
} }
/// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
@ -121,6 +127,7 @@ impl<'a> Parser<'a> {
rc: RecoverComma, rc: RecoverComma,
ra: RecoverColon, ra: RecoverColon,
rt: CommaRecoveryMode, rt: CommaRecoveryMode,
syntax_loc: Option<PatternLocation>,
) -> PResult<'a, (P<Pat>, bool)> { ) -> PResult<'a, (P<Pat>, bool)> {
// Keep track of whether we recovered from a trailing vert so that we can avoid duplicated // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated
// suggestions (which bothers rustfix). // suggestions (which bothers rustfix).
@ -133,7 +140,7 @@ impl<'a> Parser<'a> {
}; };
// Parse the first pattern (`p_0`). // Parse the first pattern (`p_0`).
let mut first_pat = self.parse_pat_no_top_alt(expected)?; let mut first_pat = self.parse_pat_no_top_alt(expected, syntax_loc)?;
if rc == RecoverComma::Yes { if rc == RecoverComma::Yes {
self.maybe_recover_unexpected_comma(first_pat.span, rt)?; self.maybe_recover_unexpected_comma(first_pat.span, rt)?;
} }
@ -172,7 +179,7 @@ impl<'a> Parser<'a> {
break; break;
} }
} }
let pat = self.parse_pat_no_top_alt(expected).map_err(|mut err| { let pat = self.parse_pat_no_top_alt(expected, syntax_loc).map_err(|mut err| {
err.span_label(lo, WHILE_PARSING_OR_MSG); err.span_label(lo, WHILE_PARSING_OR_MSG);
err err
})?; })?;
@ -208,6 +215,7 @@ impl<'a> Parser<'a> {
rc, rc,
RecoverColon::No, RecoverColon::No,
CommaRecoveryMode::LikelyTuple, CommaRecoveryMode::LikelyTuple,
Some(syntax_loc),
)?; )?;
let colon = self.eat(&token::Colon); let colon = self.eat(&token::Colon);
@ -319,6 +327,7 @@ impl<'a> Parser<'a> {
&mut self, &mut self,
allow_range_pat: bool, allow_range_pat: bool,
expected: Option<Expected>, expected: Option<Expected>,
syntax_loc: Option<PatternLocation>,
) -> PResult<'a, P<Pat>> { ) -> PResult<'a, P<Pat>> {
maybe_recover_from_interpolated_ty_qpath!(self, true); maybe_recover_from_interpolated_ty_qpath!(self, true);
maybe_whole!(self, NtPat, |x| x); maybe_whole!(self, NtPat, |x| x);
@ -358,11 +367,11 @@ impl<'a> Parser<'a> {
// Parse _ // Parse _
PatKind::Wild PatKind::Wild
} else if self.eat_keyword(kw::Mut) { } else if self.eat_keyword(kw::Mut) {
self.parse_pat_ident_mut()? self.parse_pat_ident_mut(syntax_loc)?
} else if self.eat_keyword(kw::Ref) { } else if self.eat_keyword(kw::Ref) {
// Parse ref ident @ pat / ref mut ident @ pat // Parse ref ident @ pat / ref mut ident @ pat
let mutbl = self.parse_mutability(); let mutbl = self.parse_mutability();
self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl))? self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl), syntax_loc)?
} else if self.eat_keyword(kw::Box) { } else if self.eat_keyword(kw::Box) {
self.parse_pat_box()? self.parse_pat_box()?
} else if self.check_inline_const(0) { } else if self.check_inline_const(0) {
@ -384,7 +393,7 @@ impl<'a> Parser<'a> {
// Parse `ident @ pat` // Parse `ident @ pat`
// This can give false positives and parse nullary enums, // This can give false positives and parse nullary enums,
// they are dealt with later in resolve. // they are dealt with later in resolve.
self.parse_pat_ident(BindingAnnotation::NONE)? self.parse_pat_ident(BindingAnnotation::NONE, syntax_loc)?
} else if self.is_start_of_pat_with_path() { } else if self.is_start_of_pat_with_path() {
// Parse pattern starting with a path // Parse pattern starting with a path
let (qself, path) = if self.eat_lt() { let (qself, path) = if self.eat_lt() {
@ -485,7 +494,7 @@ impl<'a> Parser<'a> {
// At this point we attempt to parse `@ $pat_rhs` and emit an error. // At this point we attempt to parse `@ $pat_rhs` and emit an error.
self.bump(); // `@` self.bump(); // `@`
let mut rhs = self.parse_pat_no_top_alt(None)?; let mut rhs = self.parse_pat_no_top_alt(None, None)?;
let whole_span = lhs.span.to(rhs.span); let whole_span = lhs.span.to(rhs.span);
if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind { if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind {
@ -541,7 +550,7 @@ impl<'a> Parser<'a> {
} }
let mutbl = self.parse_mutability(); let mutbl = self.parse_mutability();
let subpat = self.parse_pat_with_range_pat(false, expected)?; let subpat = self.parse_pat_with_range_pat(false, expected, None)?;
Ok(PatKind::Ref(subpat, mutbl)) Ok(PatKind::Ref(subpat, mutbl))
} }
@ -566,12 +575,12 @@ impl<'a> Parser<'a> {
} }
/// Parse a mutable binding with the `mut` token already eaten. /// Parse a mutable binding with the `mut` token already eaten.
fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> { fn parse_pat_ident_mut(&mut self, syntax_loc: Option<PatternLocation>) -> PResult<'a, PatKind> {
let mut_span = self.prev_token.span; let mut_span = self.prev_token.span;
if self.eat_keyword(kw::Ref) { if self.eat_keyword(kw::Ref) {
self.sess.emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) }); self.sess.emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) });
return self.parse_pat_ident(BindingAnnotation::REF_MUT); return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc);
} }
self.recover_additional_muts(); self.recover_additional_muts();
@ -584,7 +593,7 @@ impl<'a> Parser<'a> {
} }
// Parse the pattern we hope to be an identifier. // Parse the pattern we hope to be an identifier.
let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier))?; let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?;
// If we don't have `mut $ident (@ pat)?`, error. // If we don't have `mut $ident (@ pat)?`, error.
if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind
@ -810,10 +819,25 @@ impl<'a> Parser<'a> {
/// Parses `ident` or `ident @ pat`. /// Parses `ident` or `ident @ pat`.
/// Used by the copy foo and ref foo patterns to give a good /// Used by the copy foo and ref foo patterns to give a good
/// error message when parsing mistakes like `ref foo(a, b)`. /// error message when parsing mistakes like `ref foo(a, b)`.
fn parse_pat_ident(&mut self, binding_annotation: BindingAnnotation) -> PResult<'a, PatKind> { fn parse_pat_ident(
&mut self,
binding_annotation: BindingAnnotation,
syntax_loc: Option<PatternLocation>,
) -> PResult<'a, PatKind> {
let ident = self.parse_ident()?; let ident = self.parse_ident()?;
if !matches!(syntax_loc, Some(PatternLocation::FunctionParameter))
&& self.check_noexpect(&token::Lt)
&& self.look_ahead(1, |t| t.can_begin_type())
{
return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax {
span: self.token.span,
suggest_turbofish: self.token.span.shrink_to_lo(),
}));
}
let sub = if self.eat(&token::At) { let sub = if self.eat(&token::At) {
Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)
} else { } else {
None None
}; };
@ -902,14 +926,14 @@ impl<'a> Parser<'a> {
// We cannot use `parse_pat_ident()` since it will complain `box` // We cannot use `parse_pat_ident()` since it will complain `box`
// is not an identifier. // is not an identifier.
let sub = if self.eat(&token::At) { let sub = if self.eat(&token::At) {
Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)
} else { } else {
None None
}; };
Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub)) Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub))
} else { } else {
let pat = self.parse_pat_with_range_pat(false, None)?; let pat = self.parse_pat_with_range_pat(false, None, None)?;
self.sess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span)); self.sess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span));
Ok(PatKind::Box(pat)) Ok(PatKind::Box(pat))
} }

View file

@ -56,7 +56,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
); );
parse_macro_arg!( parse_macro_arg!(
Pat, Pat,
|parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None), |parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None, None),
|x: ptr::P<ast::Pat>| Some(x) |x: ptr::P<ast::Pat>| Some(x)
); );
// `parse_item` returns `Option<ptr::P<ast::Item>>`. // `parse_item` returns `Option<ptr::P<ast::Item>>`.

View file

@ -0,0 +1,11 @@
enum E<T> {
A(T)
}
fn main() {
match E::<i32>::A(1) {
E<i32>::A(v) => { //~ ERROR generic args in patterns require the turbofish syntax
println!("{v:?}");
},
}
}

View file

@ -0,0 +1,13 @@
error: generic args in patterns require the turbofish syntax
--> $DIR/issue-114112.rs:7:10
|
LL | E<i32>::A(v) => {
| ^
|
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
|
LL | E::<i32>::A(v) => {
| ++
error: aborting due to previous error

View file

@ -1,5 +1,5 @@
fn main() { fn main() {
let caller<F> = |f: F| //~ ERROR expected one of `:`, `;`, `=`, `@`, or `|`, found `<` let caller<F> = |f: F| //~ ERROR generic args in patterns require the turbofish syntax
where F: Fn() -> i32 where F: Fn() -> i32
{ {
let x = f(); let x = f();

View file

@ -1,8 +1,13 @@
error: expected one of `:`, `;`, `=`, `@`, or `|`, found `<` error: generic args in patterns require the turbofish syntax
--> $DIR/issue-22647.rs:2:15 --> $DIR/issue-22647.rs:2:15
| |
LL | let caller<F> = |f: F| LL | let caller<F> = |f: F|
| ^ expected one of `:`, `;`, `=`, `@`, or `|` | ^
|
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
|
LL | let caller::<F> = |f: F|
| ++
error: aborting due to previous error error: aborting due to previous error

View file

@ -3,7 +3,7 @@ struct Foo<B> {
} }
fn bar() { fn bar() {
let Foo<Vec<u8>> //~ ERROR expected one of `:`, `;`, `=`, `@`, or `|`, found `<` let Foo<Vec<u8>> //~ ERROR generic args in patterns require the turbofish syntax
} }
fn main() {} fn main() {}

View file

@ -1,8 +1,13 @@
error: expected one of `:`, `;`, `=`, `@`, or `|`, found `<` error: generic args in patterns require the turbofish syntax
--> $DIR/issue-22712.rs:6:12 --> $DIR/issue-22712.rs:6:12
| |
LL | let Foo<Vec<u8>> LL | let Foo<Vec<u8>>
| ^ expected one of `:`, `;`, `=`, `@`, or `|` | ^
|
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
|
LL | let Foo::<Vec<u8>>
| ++
error: aborting due to previous error error: aborting due to previous error

View file

@ -3,8 +3,7 @@ struct Foo<T>(T, T);
impl<T> Foo<T> { impl<T> Foo<T> {
fn foo(&self) { fn foo(&self) {
match *self { match *self {
Foo<T>(x, y) => { Foo<T>(x, y) => { //~ ERROR generic args in patterns require the turbofish syntax
//~^ error: expected one of `=>`, `@`, `if`, or `|`, found `<`
println!("Goodbye, World!") println!("Goodbye, World!")
} }
} }

View file

@ -1,8 +1,13 @@
error: expected one of `=>`, `@`, `if`, or `|`, found `<` error: generic args in patterns require the turbofish syntax
--> $DIR/pat-lt-bracket-3.rs:6:16 --> $DIR/pat-lt-bracket-3.rs:6:16
| |
LL | Foo<T>(x, y) => { LL | Foo<T>(x, y) => {
| ^ expected one of `=>`, `@`, `if`, or `|` | ^
|
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
|
LL | Foo::<T>(x, y) => {
| ++
error: aborting due to previous error error: aborting due to previous error

View file

@ -5,7 +5,7 @@ enum BtNode {
fn main() { fn main() {
let y = match 10 { let y = match 10 {
Foo<T>::A(value) => value, //~ error: expected one of `=>`, `@`, `if`, or `|`, found `<` Foo<T>::A(value) => value, //~ ERROR generic args in patterns require the turbofish syntax
Foo<T>::B => 7, Foo<T>::B => 7,
}; };
} }

View file

@ -1,8 +1,13 @@
error: expected one of `=>`, `@`, `if`, or `|`, found `<` error: generic args in patterns require the turbofish syntax
--> $DIR/pat-lt-bracket-4.rs:8:12 --> $DIR/pat-lt-bracket-4.rs:8:12
| |
LL | Foo<T>::A(value) => value, LL | Foo<T>::A(value) => value,
| ^ expected one of `=>`, `@`, `if`, or `|` | ^
|
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
|
LL | Foo::<T>::A(value) => value,
| ++
error: aborting due to previous error error: aborting due to previous error