Skip to content

Commit 51d1dac

Browse files
authored
Rollup merge of #114300 - MU001999:fix/turbofish-pat, r=estebank
Suggests turbofish in patterns Fixes #114112 r? ```@estebank```
2 parents 649d0a9 + dce7e87 commit 51d1dac

17 files changed

+125
-42
lines changed

compiler/rustc_parse/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}`
272272
parse_function_body_equals_expr = function body cannot be `= expression;`
273273
.suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;`
274274
275+
parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax
276+
275277
parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets
276278
.suggestion = surround the type parameters with angle brackets
277279

compiler/rustc_parse/src/errors.rs

+14
Original file line numberDiff line numberDiff line change
@@ -2738,3 +2738,17 @@ pub(crate) struct WhereClauseBeforeConstBodySugg {
27382738
#[suggestion_part(code = "")]
27392739
pub right: Span,
27402740
}
2741+
2742+
#[derive(Diagnostic)]
2743+
#[diag(parse_generic_args_in_pat_require_turbofish_syntax)]
2744+
pub(crate) struct GenericArgsInPatRequireTurbofishSyntax {
2745+
#[primary_span]
2746+
pub span: Span,
2747+
#[suggestion(
2748+
parse_sugg_turbofish_syntax,
2749+
style = "verbose",
2750+
code = "::",
2751+
applicability = "maybe-incorrect"
2752+
)]
2753+
pub suggest_turbofish: Span,
2754+
}

compiler/rustc_parse/src/parser/diagnostics.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2107,7 +2107,7 @@ impl<'a> Parser<'a> {
21072107
}
21082108

21092109
pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
2110-
let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName))?;
2110+
let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?;
21112111
self.expect(&token::Colon)?;
21122112
let ty = self.parse_ty()?;
21132113

@@ -2515,7 +2515,7 @@ impl<'a> Parser<'a> {
25152515
// Skip the `:`.
25162516
snapshot_pat.bump();
25172517
snapshot_type.bump();
2518-
match snapshot_pat.parse_pat_no_top_alt(expected) {
2518+
match snapshot_pat.parse_pat_no_top_alt(expected, None) {
25192519
Err(inner_err) => {
25202520
inner_err.cancel();
25212521
}
@@ -2779,7 +2779,7 @@ impl<'a> Parser<'a> {
27792779
/// sequence of patterns until `)` is reached.
27802780
fn skip_pat_list(&mut self) -> PResult<'a, ()> {
27812781
while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) {
2782-
self.parse_pat_no_top_alt(None)?;
2782+
self.parse_pat_no_top_alt(None, None)?;
27832783
if !self.eat(&token::Comma) {
27842784
return Ok(());
27852785
}

compiler/rustc_parse/src/parser/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2338,7 +2338,7 @@ impl<'a> Parser<'a> {
23382338
let lo = self.token.span;
23392339
let attrs = self.parse_outer_attributes()?;
23402340
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
2341-
let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName))?;
2341+
let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?;
23422342
let ty = if this.eat(&token::Colon) {
23432343
this.parse_ty()?
23442344
} else {
@@ -2781,7 +2781,7 @@ impl<'a> Parser<'a> {
27812781
return None;
27822782
}
27832783
let pre_pat_snapshot = self.create_snapshot_for_diagnostic();
2784-
match self.parse_pat_no_top_alt(None) {
2784+
match self.parse_pat_no_top_alt(None, None) {
27852785
Ok(_pat) => {
27862786
if self.token.kind == token::FatArrow {
27872787
// Reached arm end.

compiler/rustc_parse/src/parser/nonterminal.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ impl<'a> Parser<'a> {
131131
},
132132
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => {
133133
token::NtPat(self.collect_tokens_no_attrs(|this| match kind {
134-
NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None),
134+
NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None),
135135
NonterminalKind::PatWithOr { .. } => this.parse_pat_allow_top_alt(
136136
None,
137137
RecoverComma::No,

compiler/rustc_parse/src/parser/pat.rs

+46-22
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken};
22
use crate::errors::{
33
self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
44
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
5-
ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow,
6-
InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect,
7-
RemoveLet, RepeatedMutInPattern, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
8-
TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
5+
ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax,
6+
InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
7+
PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern,
8+
TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed,
9+
UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
910
UnexpectedVertVertInPattern,
1011
};
1112
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
@@ -80,7 +81,8 @@ enum EatOrResult {
8081
}
8182

8283
/// The syntax location of a given pattern. Used for diagnostics.
83-
pub(super) enum PatternLocation {
84+
#[derive(Clone, Copy)]
85+
pub enum PatternLocation {
8486
LetBinding,
8587
FunctionParameter,
8688
}
@@ -91,8 +93,12 @@ impl<'a> Parser<'a> {
9193
/// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns
9294
/// at the top level. Used when parsing the parameters of lambda expressions,
9395
/// functions, function pointers, and `pat` macro fragments.
94-
pub fn parse_pat_no_top_alt(&mut self, expected: Option<Expected>) -> PResult<'a, P<Pat>> {
95-
self.parse_pat_with_range_pat(true, expected)
96+
pub fn parse_pat_no_top_alt(
97+
&mut self,
98+
expected: Option<Expected>,
99+
syntax_loc: Option<PatternLocation>,
100+
) -> PResult<'a, P<Pat>> {
101+
self.parse_pat_with_range_pat(true, expected, syntax_loc)
96102
}
97103

98104
/// Parses a pattern.
@@ -110,7 +116,7 @@ impl<'a> Parser<'a> {
110116
ra: RecoverColon,
111117
rt: CommaRecoveryMode,
112118
) -> PResult<'a, P<Pat>> {
113-
self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt).map(|(pat, _)| pat)
119+
self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
114120
}
115121

116122
/// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
@@ -121,6 +127,7 @@ impl<'a> Parser<'a> {
121127
rc: RecoverComma,
122128
ra: RecoverColon,
123129
rt: CommaRecoveryMode,
130+
syntax_loc: Option<PatternLocation>,
124131
) -> PResult<'a, (P<Pat>, bool)> {
125132
// Keep track of whether we recovered from a trailing vert so that we can avoid duplicated
126133
// suggestions (which bothers rustfix).
@@ -133,7 +140,7 @@ impl<'a> Parser<'a> {
133140
};
134141

135142
// Parse the first pattern (`p_0`).
136-
let mut first_pat = self.parse_pat_no_top_alt(expected)?;
143+
let mut first_pat = self.parse_pat_no_top_alt(expected, syntax_loc)?;
137144
if rc == RecoverComma::Yes {
138145
self.maybe_recover_unexpected_comma(first_pat.span, rt)?;
139146
}
@@ -172,7 +179,7 @@ impl<'a> Parser<'a> {
172179
break;
173180
}
174181
}
175-
let pat = self.parse_pat_no_top_alt(expected).map_err(|mut err| {
182+
let pat = self.parse_pat_no_top_alt(expected, syntax_loc).map_err(|mut err| {
176183
err.span_label(lo, WHILE_PARSING_OR_MSG);
177184
err
178185
})?;
@@ -208,6 +215,7 @@ impl<'a> Parser<'a> {
208215
rc,
209216
RecoverColon::No,
210217
CommaRecoveryMode::LikelyTuple,
218+
Some(syntax_loc),
211219
)?;
212220
let colon = self.eat(&token::Colon);
213221

@@ -319,6 +327,7 @@ impl<'a> Parser<'a> {
319327
&mut self,
320328
allow_range_pat: bool,
321329
expected: Option<Expected>,
330+
syntax_loc: Option<PatternLocation>,
322331
) -> PResult<'a, P<Pat>> {
323332
maybe_recover_from_interpolated_ty_qpath!(self, true);
324333
maybe_whole!(self, NtPat, |x| x);
@@ -358,11 +367,11 @@ impl<'a> Parser<'a> {
358367
// Parse _
359368
PatKind::Wild
360369
} else if self.eat_keyword(kw::Mut) {
361-
self.parse_pat_ident_mut()?
370+
self.parse_pat_ident_mut(syntax_loc)?
362371
} else if self.eat_keyword(kw::Ref) {
363372
// Parse ref ident @ pat / ref mut ident @ pat
364373
let mutbl = self.parse_mutability();
365-
self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl))?
374+
self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl), syntax_loc)?
366375
} else if self.eat_keyword(kw::Box) {
367376
self.parse_pat_box()?
368377
} else if self.check_inline_const(0) {
@@ -384,7 +393,7 @@ impl<'a> Parser<'a> {
384393
// Parse `ident @ pat`
385394
// This can give false positives and parse nullary enums,
386395
// they are dealt with later in resolve.
387-
self.parse_pat_ident(BindingAnnotation::NONE)?
396+
self.parse_pat_ident(BindingAnnotation::NONE, syntax_loc)?
388397
} else if self.is_start_of_pat_with_path() {
389398
// Parse pattern starting with a path
390399
let (qself, path) = if self.eat_lt() {
@@ -485,7 +494,7 @@ impl<'a> Parser<'a> {
485494

486495
// At this point we attempt to parse `@ $pat_rhs` and emit an error.
487496
self.bump(); // `@`
488-
let mut rhs = self.parse_pat_no_top_alt(None)?;
497+
let mut rhs = self.parse_pat_no_top_alt(None, None)?;
489498
let whole_span = lhs.span.to(rhs.span);
490499

491500
if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind {
@@ -541,7 +550,7 @@ impl<'a> Parser<'a> {
541550
}
542551

543552
let mutbl = self.parse_mutability();
544-
let subpat = self.parse_pat_with_range_pat(false, expected)?;
553+
let subpat = self.parse_pat_with_range_pat(false, expected, None)?;
545554
Ok(PatKind::Ref(subpat, mutbl))
546555
}
547556

@@ -566,12 +575,12 @@ impl<'a> Parser<'a> {
566575
}
567576

568577
/// Parse a mutable binding with the `mut` token already eaten.
569-
fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> {
578+
fn parse_pat_ident_mut(&mut self, syntax_loc: Option<PatternLocation>) -> PResult<'a, PatKind> {
570579
let mut_span = self.prev_token.span;
571580

572581
if self.eat_keyword(kw::Ref) {
573582
self.sess.emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) });
574-
return self.parse_pat_ident(BindingAnnotation::REF_MUT);
583+
return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc);
575584
}
576585

577586
self.recover_additional_muts();
@@ -584,7 +593,7 @@ impl<'a> Parser<'a> {
584593
}
585594

586595
// Parse the pattern we hope to be an identifier.
587-
let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier))?;
596+
let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?;
588597

589598
// If we don't have `mut $ident (@ pat)?`, error.
590599
if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind
@@ -810,10 +819,25 @@ impl<'a> Parser<'a> {
810819
/// Parses `ident` or `ident @ pat`.
811820
/// Used by the copy foo and ref foo patterns to give a good
812821
/// error message when parsing mistakes like `ref foo(a, b)`.
813-
fn parse_pat_ident(&mut self, binding_annotation: BindingAnnotation) -> PResult<'a, PatKind> {
822+
fn parse_pat_ident(
823+
&mut self,
824+
binding_annotation: BindingAnnotation,
825+
syntax_loc: Option<PatternLocation>,
826+
) -> PResult<'a, PatKind> {
814827
let ident = self.parse_ident()?;
828+
829+
if !matches!(syntax_loc, Some(PatternLocation::FunctionParameter))
830+
&& self.check_noexpect(&token::Lt)
831+
&& self.look_ahead(1, |t| t.can_begin_type())
832+
{
833+
return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax {
834+
span: self.token.span,
835+
suggest_turbofish: self.token.span.shrink_to_lo(),
836+
}));
837+
}
838+
815839
let sub = if self.eat(&token::At) {
816-
Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?)
840+
Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)
817841
} else {
818842
None
819843
};
@@ -902,14 +926,14 @@ impl<'a> Parser<'a> {
902926
// We cannot use `parse_pat_ident()` since it will complain `box`
903927
// is not an identifier.
904928
let sub = if self.eat(&token::At) {
905-
Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?)
929+
Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)
906930
} else {
907931
None
908932
};
909933

910934
Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub))
911935
} else {
912-
let pat = self.parse_pat_with_range_pat(false, None)?;
936+
let pat = self.parse_pat_with_range_pat(false, None, None)?;
913937
self.sess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span));
914938
Ok(PatKind::Box(pat))
915939
}

src/tools/rustfmt/src/parse/macros/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
5656
);
5757
parse_macro_arg!(
5858
Pat,
59-
|parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None),
59+
|parser: &mut rustc_parse::parser::Parser<'b>| parser.parse_pat_no_top_alt(None, None),
6060
|x: ptr::P<ast::Pat>| Some(x)
6161
);
6262
// `parse_item` returns `Option<ptr::P<ast::Item>>`.

tests/ui/did_you_mean/issue-114112.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
enum E<T> {
2+
A(T)
3+
}
4+
5+
fn main() {
6+
match E::<i32>::A(1) {
7+
E<i32>::A(v) => { //~ ERROR generic args in patterns require the turbofish syntax
8+
println!("{v:?}");
9+
},
10+
}
11+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: generic args in patterns require the turbofish syntax
2+
--> $DIR/issue-114112.rs:7:10
3+
|
4+
LL | E<i32>::A(v) => {
5+
| ^
6+
|
7+
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
8+
|
9+
LL | E::<i32>::A(v) => {
10+
| ++
11+
12+
error: aborting due to previous error
13+

tests/ui/parser/issues/issue-22647.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
fn main() {
2-
let caller<F> = |f: F| //~ ERROR expected one of `:`, `;`, `=`, `@`, or `|`, found `<`
2+
let caller<F> = |f: F| //~ ERROR generic args in patterns require the turbofish syntax
33
where F: Fn() -> i32
44
{
55
let x = f();
+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
error: expected one of `:`, `;`, `=`, `@`, or `|`, found `<`
1+
error: generic args in patterns require the turbofish syntax
22
--> $DIR/issue-22647.rs:2:15
33
|
44
LL | let caller<F> = |f: F|
5-
| ^ expected one of `:`, `;`, `=`, `@`, or `|`
5+
| ^
6+
|
7+
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
8+
|
9+
LL | let caller::<F> = |f: F|
10+
| ++
611

712
error: aborting due to previous error
813

tests/ui/parser/issues/issue-22712.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ struct Foo<B> {
33
}
44

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

99
fn main() {}
+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
error: expected one of `:`, `;`, `=`, `@`, or `|`, found `<`
1+
error: generic args in patterns require the turbofish syntax
22
--> $DIR/issue-22712.rs:6:12
33
|
44
LL | let Foo<Vec<u8>>
5-
| ^ expected one of `:`, `;`, `=`, `@`, or `|`
5+
| ^
6+
|
7+
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
8+
|
9+
LL | let Foo::<Vec<u8>>
10+
| ++
611

712
error: aborting due to previous error
813

tests/ui/parser/pat-lt-bracket-3.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ struct Foo<T>(T, T);
33
impl<T> Foo<T> {
44
fn foo(&self) {
55
match *self {
6-
Foo<T>(x, y) => {
7-
//~^ error: expected one of `=>`, `@`, `if`, or `|`, found `<`
6+
Foo<T>(x, y) => { //~ ERROR generic args in patterns require the turbofish syntax
87
println!("Goodbye, World!")
98
}
109
}
+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
error: expected one of `=>`, `@`, `if`, or `|`, found `<`
1+
error: generic args in patterns require the turbofish syntax
22
--> $DIR/pat-lt-bracket-3.rs:6:16
33
|
44
LL | Foo<T>(x, y) => {
5-
| ^ expected one of `=>`, `@`, `if`, or `|`
5+
| ^
6+
|
7+
help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
8+
|
9+
LL | Foo::<T>(x, y) => {
10+
| ++
611

712
error: aborting due to previous error
813

0 commit comments

Comments
 (0)