Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggests turbofish in patterns #114300

Merged
merged 6 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}`
parse_function_body_equals_expr = function body cannot be `= expression;`
.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
.suggestion = surround the type parameters with angle brackets

Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2731,3 +2731,17 @@ pub(crate) struct WhereClauseBeforeConstBodySugg {
#[suggestion_part(code = "")]
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,
}
6 changes: 3 additions & 3 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2100,7 +2100,7 @@ impl<'a> Parser<'a> {
}

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)?;
let ty = self.parse_ty()?;

Expand Down Expand Up @@ -2508,7 +2508,7 @@ impl<'a> Parser<'a> {
// Skip the `:`.
snapshot_pat.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) => {
inner_err.cancel();
}
Expand Down Expand Up @@ -2772,7 +2772,7 @@ impl<'a> Parser<'a> {
/// sequence of patterns until `)` is reached.
fn skip_pat_list(&mut self) -> PResult<'a, ()> {
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) {
return Ok(());
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2338,7 +2338,7 @@ impl<'a> Parser<'a> {
let lo = self.token.span;
let attrs = self.parse_outer_attributes()?;
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) {
this.parse_ty()?
} else {
Expand Down Expand Up @@ -2781,7 +2781,7 @@ impl<'a> Parser<'a> {
return None;
}
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) => {
if self.token.kind == token::FatArrow {
// Reached arm end.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/nonterminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ impl<'a> Parser<'a> {
},
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => {
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(
None,
RecoverComma::No,
Expand Down
68 changes: 46 additions & 22 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken};
use crate::errors::{
self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow,
InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect,
RemoveLet, RepeatedMutInPattern, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax,
InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern,
TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed,
UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
UnexpectedVertVertInPattern,
};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
Expand Down Expand Up @@ -80,7 +81,8 @@ enum EatOrResult {
}

/// The syntax location of a given pattern. Used for diagnostics.
pub(super) enum PatternLocation {
#[derive(Clone, Copy)]
pub enum PatternLocation {
LetBinding,
FunctionParameter,
}
Expand All @@ -91,8 +93,12 @@ impl<'a> Parser<'a> {
/// 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,
/// functions, function pointers, and `pat` macro fragments.
pub fn parse_pat_no_top_alt(&mut self, expected: Option<Expected>) -> PResult<'a, P<Pat>> {
self.parse_pat_with_range_pat(true, expected)
pub fn parse_pat_no_top_alt(
&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.
Expand All @@ -110,7 +116,7 @@ impl<'a> Parser<'a> {
ra: RecoverColon,
rt: CommaRecoveryMode,
) -> 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 =
Expand All @@ -121,6 +127,7 @@ impl<'a> Parser<'a> {
rc: RecoverComma,
ra: RecoverColon,
rt: CommaRecoveryMode,
syntax_loc: Option<PatternLocation>,
) -> PResult<'a, (P<Pat>, bool)> {
// Keep track of whether we recovered from a trailing vert so that we can avoid duplicated
// suggestions (which bothers rustfix).
Expand All @@ -133,7 +140,7 @@ impl<'a> Parser<'a> {
};

// 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 {
self.maybe_recover_unexpected_comma(first_pat.span, rt)?;
}
Expand Down Expand Up @@ -172,7 +179,7 @@ impl<'a> Parser<'a> {
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
})?;
Expand Down Expand Up @@ -208,6 +215,7 @@ impl<'a> Parser<'a> {
rc,
RecoverColon::No,
CommaRecoveryMode::LikelyTuple,
Some(syntax_loc),
)?;
let colon = self.eat(&token::Colon);

Expand Down Expand Up @@ -319,6 +327,7 @@ impl<'a> Parser<'a> {
&mut self,
allow_range_pat: bool,
expected: Option<Expected>,
syntax_loc: Option<PatternLocation>,
) -> PResult<'a, P<Pat>> {
maybe_recover_from_interpolated_ty_qpath!(self, true);
maybe_whole!(self, NtPat, |x| x);
Expand Down Expand Up @@ -358,11 +367,11 @@ impl<'a> Parser<'a> {
// Parse _
PatKind::Wild
} 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) {
// Parse ref ident @ pat / ref mut ident @ pat
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) {
self.parse_pat_box()?
} else if self.check_inline_const(0) {
Expand All @@ -384,7 +393,7 @@ impl<'a> Parser<'a> {
// Parse `ident @ pat`
// This can give false positives and parse nullary enums,
// 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() {
// Parse pattern starting with a path
let (qself, path) = if self.eat_lt() {
Expand Down Expand Up @@ -485,7 +494,7 @@ impl<'a> Parser<'a> {

// At this point we attempt to parse `@ $pat_rhs` and emit an error.
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);

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

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))
}

Expand All @@ -566,12 +575,12 @@ impl<'a> Parser<'a> {
}

/// 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;

if self.eat_keyword(kw::Ref) {
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();
Expand All @@ -584,7 +593,7 @@ impl<'a> Parser<'a> {
}

// 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 let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind
Expand Down Expand Up @@ -810,10 +819,25 @@ impl<'a> Parser<'a> {
/// Parses `ident` or `ident @ pat`.
/// Used by the copy foo and ref foo patterns to give a good
/// 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()?;

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) {
Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?)
Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?)
} else {
None
};
Expand Down Expand Up @@ -902,14 +926,14 @@ impl<'a> Parser<'a> {
// We cannot use `parse_pat_ident()` since it will complain `box`
// is not an identifier.
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 {
None
};

Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub))
} 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));
Ok(PatKind::Box(pat))
}
Expand Down
2 changes: 1 addition & 1 deletion src/tools/rustfmt/src/parse/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
);
parse_macro_arg!(
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)
);
// `parse_item` returns `Option<ptr::P<ast::Item>>`.
Expand Down
11 changes: 11 additions & 0 deletions tests/ui/did_you_mean/issue-114112.rs
Original file line number Diff line number Diff line change
@@ -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:?}");
},
}
}
13 changes: 13 additions & 0 deletions tests/ui/did_you_mean/issue-114112.stderr
Original file line number Diff line number Diff line change
@@ -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

2 changes: 1 addition & 1 deletion tests/ui/parser/issues/issue-22647.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this error, the suggestion we should be giving is to add a type, but don't know how hard it would be to change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like function parameters, maybe we can also suggest name: caller<F> or _: caller<F>? I think it may be more general, because caller<F> is more like a type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think the later is the better suggestion (particularly if the rest can be parsed as a type until ; or = is reached).

where F: Fn() -> i32
{
let x = f();
Expand Down
9 changes: 7 additions & 2 deletions tests/ui/parser/issues/issue-22647.stderr
Original file line number Diff line number Diff line change
@@ -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
|
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

2 changes: 1 addition & 1 deletion tests/ui/parser/issues/issue-22712.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ struct Foo<B> {
}

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() {}
9 changes: 7 additions & 2 deletions tests/ui/parser/issues/issue-22712.stderr
Original file line number Diff line number Diff line change
@@ -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
|
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

3 changes: 1 addition & 2 deletions tests/ui/parser/pat-lt-bracket-3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ struct Foo<T>(T, T);
impl<T> Foo<T> {
fn foo(&self) {
match *self {
Foo<T>(x, y) => {
//~^ error: expected one of `=>`, `@`, `if`, or `|`, found `<`
Foo<T>(x, y) => { //~ ERROR generic args in patterns require the turbofish syntax
println!("Goodbye, World!")
}
}
Expand Down
9 changes: 7 additions & 2 deletions tests/ui/parser/pat-lt-bracket-3.stderr
Original file line number Diff line number Diff line change
@@ -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
|
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

Loading
Loading