Skip to content
Draft
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: 1 addition & 1 deletion compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ impl Token {
}

pub fn is_non_reserved_ident(&self) -> bool {
self.ident().is_some_and(|(id, raw)| raw == IdentIsRaw::Yes || !Ident::is_reserved(id))
self.ident().is_some_and(|(id, raw)| raw == IdentIsRaw::Yes || !id.is_reserved())
}

/// Returns `true` if the token is the identifier `true` or `false`.
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use rustc_session::lint::builtin::{
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
PATTERNS_IN_FNS_WITHOUT_BODY, UNUSED_VISIBILITIES,
};
use rustc_span::{Ident, Span, kw, sym};
use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
use rustc_target::spec::{AbiMap, AbiMapping};
use thin_vec::thin_vec;

Expand Down Expand Up @@ -256,7 +256,10 @@ impl<'a> AstValidator<'a> {
fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option<Ident>, bool)) {
for Param { pat, .. } in &decl.inputs {
match pat.kind {
PatKind::Missing | PatKind::Ident(BindingMode::NONE, _, None) | PatKind::Wild => {}
PatKind::Missing
| PatKind::Ident(BindingMode::NONE, _, None)
| PatKind::Wild
| PatKind::Err(ErrorGuaranteed { .. }) => {}
PatKind::Ident(BindingMode::MUT, ident, None) => {
report_err(pat.span, Some(ident), true)
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0642.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
Trait methods currently cannot take patterns as arguments.
<!-- FIXME(fmease): Update this -->

Trait methods currently cannot take patterns as parameters.

Erroneous code example:

Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1823,16 +1823,17 @@ pub(crate) struct AttributeOnEmptyType {
}

#[derive(Diagnostic)]
#[diag("patterns aren't allowed in methods without bodies", code = E0642)]
pub(crate) struct PatternMethodParamWithoutBody {
#[diag("parameters can't have complex patterns in {$context}", code = E0642)]
pub(crate) struct ComplexParamPat {
#[primary_span]
#[suggestion(
"give this argument a name or use an underscore to ignore it",
"give this parameter a name or use an underscore to ignore it",
code = "_",
applicability = "machine-applicable",
style = "verbose"
)]
pub span: Span,
pub context: &'static str,
}

#[derive(Diagnostic)]
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_parse/src/parser/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,9 @@ pub fn parse_asm_args<'a>(
}

// Parse operand names.
let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
let (ident, _) = p.token.ident().unwrap();
let name = if let Some((ident, _)) = p.token.ident()
&& p.look_ahead(1, |t| *t == token::Eq)
{
p.bump();
p.expect(exp!(Eq))?;
allow_templates = false;
Expand Down
28 changes: 18 additions & 10 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ use super::{
use crate::errors::{
AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AsyncUseBlockIn2015, AttributeOnParamType,
AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,
ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg, ComplexParamPat,
DocCommentDoesNotDocumentAnything, DocCommentOnParamType, DoubleColonInBound,
ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets,
GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, HelpUseLatestEdition,
InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse,
MisspelledKw, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg,
SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg,
SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator,
TernaryOperatorSuggestion, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
MisspelledKw, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt,
SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, TernaryOperatorSuggestion,
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
};
use crate::exp;
Expand Down Expand Up @@ -2267,6 +2267,7 @@ impl<'a> Parser<'a> {
if matches!(fn_parse_mode.context, crate::parser::item::FnContext::Trait)
&& (fn_parse_mode.req_name)(ed, IsDotDotDot::No)
{
// FIXME: and beyond
err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)");
}
};
Expand Down Expand Up @@ -2385,16 +2386,23 @@ impl<'a> Parser<'a> {
}

#[cold]
pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (Box<ast::Pat>, Box<ast::Ty>)> {
let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?;
pub(super) fn recover_complex_param_pat(
&mut self,
context: FnContext,
) -> PResult<'a, (Box<ast::Pat>, Box<ast::Ty>)> {
let pat = self.parse_pat_no_top_alt(Some(Expected::TyOrParamName), None)?;
self.expect(exp!(Colon))?;
let ty = self.parse_ty()?;

self.dcx().emit_err(PatternMethodParamWithoutBody { span: pat.span });
let context = match context {
FnContext::FnPtrTy => "function pointer types",
FnContext::Trait => "associated functions in traits in Rust 2015",
_ => unreachable!(),
};
let guar = self.dcx().emit_err(ComplexParamPat { span: pat.span, context });

// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
let pat = Box::new(Pat {
kind: PatKind::Wild,
kind: PatKind::Err(guar),
span: pat.span,
id: ast::DUMMY_NODE_ID,
tokens: None,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2608,7 +2608,7 @@ impl<'a> Parser<'a> {
let lo = self.token.span;
let attrs = self.parse_outer_attributes()?;
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
let pat = Box::new(this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?);
let pat = Box::new(this.parse_pat_no_top_alt(Some(Expected::ParamName), None)?);
let ty = if this.eat(exp!(Colon)) {
this.parse_ty()?
} else {
Expand Down
37 changes: 19 additions & 18 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::mem;
use ast::token::IdentIsRaw;
use rustc_ast as ast;
use rustc_ast::ast::*;
use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind};
use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, Token, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::util::case::Case;
use rustc_ast_pretty::pprust;
Expand Down Expand Up @@ -2687,12 +2687,10 @@ pub(crate) struct FnParseMode {
/// FIXME(estebank, xizheyin): Use more variants.
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum FnContext {
/// Free context.
Free,
/// A Trait context.
Trait,
/// An Impl block.
Impl,
FnPtrTy,
}

/// Parsing of functions and methods.
Expand Down Expand Up @@ -3336,6 +3334,7 @@ impl<'a> Parser<'a> {
&mut self,
fn_parse_mode: &FnParseMode,
first_param: bool,
// FIXME(fmease): rename arg -> param
recover_arg_parse: bool,
) -> PResult<'a, Param> {
let lo = self.token.span;
Expand Down Expand Up @@ -3368,6 +3367,7 @@ impl<'a> Parser<'a> {
} else {
is_name_required
};

let (pat, ty) = if is_name_required || this.is_named_param() {
debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);
let (pat, colon) = this.parse_fn_param_pat_colon()?;
Expand All @@ -3391,6 +3391,7 @@ impl<'a> Parser<'a> {
(pat, this.parse_ty_for_param()?)
} else {
debug!("parse_param_general ident_to_pat");
// FIXME: This creates a snapshot in the happy path!
let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic();
this.eat_incorrect_doc_comment_for_param_type();
let mut ty = this.parse_ty_for_param();
Expand Down Expand Up @@ -3427,7 +3428,7 @@ impl<'a> Parser<'a> {
// Recover from attempting to parse the argument as a type without pattern.
err.cancel();
this.restore_snapshot(parser_snapshot_before_ty);
this.recover_arg_parse()?
this.recover_complex_param_pat(fn_parse_mode.context)?
}
Err(err) => return Err(err),
}
Expand Down Expand Up @@ -3590,21 +3591,21 @@ impl<'a> Parser<'a> {
Ok(Some(Param::from_self(AttrVec::default(), eself, eself_ident)))
}

// FIXME(fmease): Better name.
fn is_named_param(&self) -> bool {
let offset = match &self.token.kind {
token::OpenInvisible(origin) => match origin {
InvisibleOrigin::MetaVar(MetaVarKind::Pat(_)) => {
return self.check_noexpect_past_close_delim(&token::Colon);
}
_ => 0,
},
token::And | token::AndAnd => 1,
_ if self.token.is_keyword(kw::Mut) => 1,
_ => 0,
};
if let token::OpenInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Pat(_))) = self.token.kind
{
return self.check_noexpect_past_close_delim(&token::Colon);
}

fn is_param_name(t: &Token) -> bool {
!t.is_non_raw_ident_where(|ident| ident.name != kw::Underscore && ident.is_reserved())
}

let offset = if self.token.is_keyword(kw::Mut) { 1 } else { 0 };

self.look_ahead(offset, |t| t.is_ident())
&& self.look_ahead(offset + 1, |t| t == &token::Colon)
self.look_ahead(offset, is_param_name)
&& self.look_ahead(offset + 1, |t| t.kind == token::Colon)
}

fn recover_self_param(&mut self) -> bool {
Expand Down
26 changes: 13 additions & 13 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ use crate::{exp, maybe_recover_from_interpolated_ty_qpath};

#[derive(PartialEq, Copy, Clone)]
pub enum Expected {
ParameterName,
ArgumentName,
Identifier,
BindingPattern,
ParamName,
TyOrParamName,
Ident,
BindingPat,
}

impl Expected {
// FIXME(#100717): migrate users of this to proper localization
fn to_string_or_fallback(expected: Option<Expected>) -> &'static str {
match expected {
Some(Expected::ParameterName) => "parameter name",
Some(Expected::ArgumentName) => "argument name",
Some(Expected::Identifier) => "identifier",
Some(Expected::BindingPattern) => "binding pattern",
Some(Expected::ParamName) => "parameter name",
Some(Expected::TyOrParamName) => "type or parameter name",
Some(Expected::Ident) => "identifier",
Some(Expected::BindingPat) => "binding pattern",
None => "pattern",
}
}
Expand Down Expand Up @@ -319,7 +319,7 @@ impl<'a> Parser<'a> {
}

self.parse_pat_before_ty(
Some(Expected::ParameterName),
Some(Expected::ParamName),
RecoverComma::No,
PatternLocation::FunctionParameter,
)
Expand Down Expand Up @@ -908,7 +908,7 @@ impl<'a> Parser<'a> {
// A typoed rest pattern `...`.
self.bump(); // `...`

if let Some(Expected::ParameterName) = expected {
if let Some(Expected::ParamName) = expected {
// We have `...` in a closure argument, likely meant to be var-arg, which aren't
// supported in closures (#146489).
PatKind::Err(self.dcx().emit_err(DotDotDotRestPattern {
Expand Down Expand Up @@ -1092,7 +1092,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), None)?;
let mut pat = self.parse_pat_no_top_alt(Some(Expected::Ident), None)?;

// If we don't have `mut $ident (@ pat)?`, error.
if let PatKind::Ident(BindingMode(br @ ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind
Expand Down Expand Up @@ -1370,7 +1370,7 @@ impl<'a> Parser<'a> {
}

let sub = if self.eat(exp!(At)) {
Some(Box::new(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?))
Some(Box::new(self.parse_pat_no_top_alt(Some(Expected::BindingPat), None)?))
} else {
None
};
Expand Down Expand Up @@ -1488,7 +1488,7 @@ impl<'a> Parser<'a> {
// We cannot use `parse_pat_ident()` since it will complain `box`
// is not an identifier.
let sub = if self.eat(exp!(At)) {
Some(Box::new(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?))
Some(Box::new(self.parse_pat_no_top_alt(Some(Expected::BindingPat), None)?))
} else {
None
};
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ impl<'a> Parser<'a> {
}
let mode = crate::parser::item::FnParseMode {
req_name: |_, _| false,
context: FnContext::Free,
context: FnContext::FnPtrTy,
req_body: false,
};
let decl = self.parse_fn_decl(&mode, AllowPlus::No, recover_return_sign)?;
Expand Down
19 changes: 11 additions & 8 deletions tests/ui/error-codes/E0642.fixed
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
//@ edition:2015
//@ run-rustfix
// FIXME(fmease): Add historical context.

#![allow(unused)] // for rustfix

#[derive(Clone, Copy)]
struct S;

trait T {
fn foo(_: (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies

fn bar(_: (i32, i32)) {} //~ ERROR patterns aren't allowed in methods without bodies

fn method(_: S) {} //~ ERROR patterns aren't allowed in methods without bodies

fn f(&ident: &S) {} // ok
fn g(&&ident: &&S) {} // ok
fn foo(_: (i32, i32));
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn bar(_: (i32, i32)) {}
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn method(_: S) {}
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn f(_: &S) {}
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn g(_: &&S) {}
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn h(mut ident: S) {} // ok
}

Expand Down
19 changes: 11 additions & 8 deletions tests/ui/error-codes/E0642.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
//@ edition:2015
//@ run-rustfix
// FIXME(fmease): Add historical context.

#![allow(unused)] // for rustfix

#[derive(Clone, Copy)]
struct S;

trait T {
fn foo((x, y): (i32, i32)); //~ ERROR patterns aren't allowed in methods without bodies

fn bar((x, y): (i32, i32)) {} //~ ERROR patterns aren't allowed in methods without bodies

fn method(S { .. }: S) {} //~ ERROR patterns aren't allowed in methods without bodies

fn f(&ident: &S) {} // ok
fn g(&&ident: &&S) {} // ok
fn foo((x, y): (i32, i32));
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn bar((x, y): (i32, i32)) {}
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn method(S { .. }: S) {}
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn f(&ident: &S) {}
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn g(&&ident: &&S) {}
//~^ ERROR parameters can't have complex patterns in associated functions in traits in Rust 2015
fn h(mut ident: S) {} // ok
}

Expand Down
Loading
Loading