Skip to content
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
25 changes: 20 additions & 5 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,13 @@ impl LitKind {
}

pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool {
// WARNING: Take care when modifying this function! It will change the stable(!) set of
// tokens that are allowed to match an `expr` nonterminal which is user observable.

let ident_token = Token::new(Ident(name, is_raw), span);

// FIXME: Remove `box` from this list given we officially no longer support box expressions
// (#108471) (needs lang FCP as it affects stable macro matching behavior).
!ident_token.is_reserved_ident()
|| ident_token.is_path_segment_keyword()
|| [
Expand Down Expand Up @@ -340,6 +345,9 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: IdentIsRaw) -> boo
}

fn ident_can_begin_type(name: Symbol, span: Span, is_raw: IdentIsRaw) -> bool {
// WARNING: Take care when modifying this function! It will change the stable(!) set of
// tokens that are allowed to match an `ty` nonterminal which is user observable.

let ident_token = Token::new(Ident(name, is_raw), span);

!ident_token.is_reserved_ident()
Expand Down Expand Up @@ -661,10 +669,10 @@ impl Token {
}

/// Returns `true` if the token can appear at the start of an expression.
///
/// **NB**: Take care when modifying this function, since it will change
/// the stable set of tokens that are allowed to match an expr nonterminal.
pub fn can_begin_expr(&self) -> bool {
// WARNING: Take care when modifying this function! It will change the stable(!) set of
// tokens that are allowed to match an `expr` nonterminal which is user observable.

match self.uninterpolate().kind {
Ident(name, is_raw) =>
ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
Expand Down Expand Up @@ -695,9 +703,10 @@ impl Token {
}

/// Returns `true` if the token can appear at the start of a pattern.
///
/// Shamelessly borrowed from `can_begin_expr`.
pub fn can_begin_pattern(&self, pat_kind: NtPatKind) -> bool {
// WARNING: Take care when modifying this function! It will change the stable(!) set of
// tokens that are allowed to match an `pat` nonterminal which is user observable.

match &self.uninterpolate().kind {
// box, ref, mut, and other identifiers (can stricten)
Ident(..) | NtIdent(..) |
Expand Down Expand Up @@ -727,6 +736,12 @@ impl Token {

/// Returns `true` if the token can appear at the start of a type.
pub fn can_begin_type(&self) -> bool {
// WARNING: Take care when modifying this function! It will change the stable(!) set of
// tokens that are allowed to match an `ty` nonterminal which is user observable.

// FIXME: Arguably, `use` should be included in this list since it can begin bare trait
// object types (consider `use<>+` and `use<T> + Trait` for example).

match self.uninterpolate().kind {
Ident(name, is_raw) =>
ident_can_begin_type(name, self.span, is_raw), // type name or keyword
Expand Down
50 changes: 31 additions & 19 deletions compiler/rustc_parse/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3728,32 +3728,44 @@ impl HelpUseLatestEdition {
}

#[derive(Diagnostic)]
#[diag("`box_syntax` has been removed")]
pub(crate) struct BoxSyntaxRemoved {
#[diag("`box` patterns have been removed (feature `box_patterns`)")]
#[help("enable feature `deref_patterns` instead and...")]
pub(crate) struct BoxPatsRemoved {
#[primary_span]
pub span: Span,
#[suggestion(
"...if possible just remove keyword `box`...",
code = "",
applicability = "maybe-incorrect",
style = "verbose"
)]
pub sugg_removal: Span,
#[subdiagnostic]
pub sugg: AddBoxNew,
pub sugg_deref_macro_call: UseDerefMacro,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(
"use `Box::new()` instead",
applicability = "machine-applicable",
style = "verbose"
)]
pub(crate) struct AddBoxNew {
#[suggestion_part(code = "Box::new(")]
pub box_kw_and_lo: Span,
#[suggestion_part(code = ")")]
pub hi: Span,
pub(crate) struct UseDerefMacro {
pub field: Option<(Span, Ident)>,
pub before: Span,
pub after: Span,
}

#[derive(Diagnostic)]
#[diag("`box_patterns` has been removed")]
pub(crate) struct BoxPatternsRemoved {
#[primary_span]
pub span: Span,
impl Subdiagnostic for UseDerefMacro {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
let Self { field, before, after } = self;

let mut parts = Vec::new();
if let Some((span, field)) = field {
parts.push((span, format!("{field}: ")));
}
parts.push((before, "deref!(".into()));
parts.push((after, ")".into()));
diag.multipart_suggestion(
"...otherwise replace it with an invocation of macro `deref`",
parts,
Applicability::MaybeIncorrect,
);
}
}

#[derive(Diagnostic)]
Expand Down
16 changes: 0 additions & 16 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,9 +542,6 @@ impl<'a> Parser<'a> {
let operand_expr = this.parse_expr_dot_or_call(attrs)?;
this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt)
}
token::Ident(..) if this.token.is_keyword(kw::Box) => {
make_it!(this, attrs, |this, _| this.parse_expr_box(lo))
}
token::Ident(..)
if this.token.is_keyword(kw::Move)
&& this.look_ahead(1, |t| *t == token::OpenParen) =>
Expand Down Expand Up @@ -582,19 +579,6 @@ impl<'a> Parser<'a> {
self.parse_expr_unary(lo, UnOp::Not)
}

/// Parse `box expr` - this syntax has been removed, but we still parse this
/// for now to provide a more useful error
fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> {
self.bump(); // `box`
let (span, expr) = self.parse_expr_prefix_common(box_kw)?;
// Make a multipart suggestion instead of `span_to_snippet` in case source isn't available
let box_kw_and_lo = box_kw.until(self.interpolated_or_expr_span(&expr));
let hi = span.shrink_to_hi();
let sugg = diagnostics::AddBoxNew { box_kw_and_lo, hi };
let guar = self.dcx().emit_err(diagnostics::BoxSyntaxRemoved { span, sugg });
Ok((span, ExprKind::Err(guar)))
}

fn parse_expr_move(&mut self, move_kw: Span) -> PResult<'a, (Span, ExprKind)> {
self.bump();
self.psess.gated_spans.gate(sym::move_expr, move_kw);
Expand Down
40 changes: 31 additions & 9 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1650,8 +1650,15 @@ impl<'a> Parser<'a> {
Ok(PatKind::Ident(BindingMode::NONE, Ident::new(kw::Box, box_span), sub))
} else {
let pat = Box::new(self.parse_pat_with_range_pat(false, None, None)?);
self.dcx().emit_err(diagnostics::BoxPatternsRemoved {
span: box_span.to(self.prev_token.span),
let before_span = box_span.until(pat.span);
self.dcx().emit_err(diagnostics::BoxPatsRemoved {
span: box_span,
sugg_deref_macro_call: diagnostics::UseDerefMacro {
field: None,
before: before_span,
after: pat.span.shrink_to_hi(),
},
sugg_removal: before_span,
});
// Treat the box pattern like a deref pattern to avoid lots of "value not found" errors.
Ok(PatKind::Deref(pat))
Expand Down Expand Up @@ -1905,12 +1912,13 @@ impl<'a> Parser<'a> {
(pat, fieldname, false)
} else {
// FIXME: remove the recovery for parsing box patterrns entirely
let is_box = self.eat_keyword(exp!(Box));
if is_box {
self.dcx()
.create_err(diagnostics::BoxPatternsRemoved { span: self.prev_token.span })
.emit();
}
let is_box = if self.eat_keyword(exp!(Box)) {
let span = self.prev_token.span;
self.dcx().span_delayed_bug(span, "box patterns have been removed");
Some(span)
} else {
None
};
let boxed_span = self.token.span;
let mutability = self.parse_mutability();
let by_ref = self.parse_byref();
Expand All @@ -1925,7 +1933,21 @@ impl<'a> Parser<'a> {
) {
self.psess.gated_spans.gate(sym::mut_ref, fieldpat.span);
}
let subpat = if is_box {
let subpat = if let Some(box_span) = is_box {
let prefix_span = box_span.until(boxed_span);

self.dcx()
.create_err(diagnostics::BoxPatsRemoved {
span: box_span,
sugg_deref_macro_call: diagnostics::UseDerefMacro {
field: Some((prefix_span, fieldname)),
before: boxed_span.shrink_to_lo(),
after: hi.shrink_to_hi(),
},
sugg_removal: prefix_span,
})
.emit();

self.mk_pat(lo.to(hi), PatKind::Deref(Box::new(fieldpat)))
} else {
fieldpat
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,10 @@ impl<'a> Parser<'a> {

/// Can the current token begin a bound?
fn can_begin_bound(&mut self) -> bool {
// NOTE: Tokens `!`, `~`, `const` & `async` which represent the start of currently unstable
// trait bound modifiers are intentionally not included in `Token::can_begin_type` to
// avoid affecting stable macro matching behavior.

self.check_path()
|| self.check_lifetime()
|| self.check(exp!(Bang))
Expand Down
18 changes: 18 additions & 0 deletions tests/ui/parser/box-can-begin-expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Demonstrate that we still consider keyword `box` to begin expressions (`can_begin_expr`) even
// though we officially no longer support box expressions (#108471).
// It means that we take the first rule and fail immediately afterward.

// FIXME: Remove `box` from the list of tokens that can begin expressions which would make us take
// the second rule instead and consequently accept this program (needs lang FCP).
//
// Alternatively we could unreserve keyword `box` (needs lang FCP) which would make us
// continue to take the first rule but also start accepting this program.

macro_rules! mk {
($e:expr) => {};
(box $e:expr) => {};
}

mk!(box 0); //~ ERROR expected expression, found reserved keyword `box`

fn main() {}
11 changes: 11 additions & 0 deletions tests/ui/parser/box-can-begin-expr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: expected expression, found reserved keyword `box`
--> $DIR/box-can-begin-expr.rs:16:5
|
LL | ($e:expr) => {};
| ------- while parsing argument for this `expr` macro fragment
...
LL | mk!(box 0);
| ^^^ expected expression

error: aborting due to 1 previous error

6 changes: 4 additions & 2 deletions tests/ui/parser/removed-syntax/removed-syntax-box-patterns.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
fn main() {
let box x = Box::new('c'); //~ ERROR `box_patterns` has been removed
let box x = Box::new('c'); //~ ERROR `box` patterns have been removed
let _: char = x;

struct Packet { x: Box<i32> }

let Packet { box x } = Packet { x: Box::new(0) }; //~ ERROR `box_patterns` has been removed
let Packet { box x } = Packet { x: Box::new(0) }; //~ ERROR `box` patterns have been removed
let _: i32 = x;

let Packet { box ref mut x }; //~ ERROR `box` patterns have been removed
}
50 changes: 46 additions & 4 deletions tests/ui/parser/removed-syntax/removed-syntax-box-patterns.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,56 @@
error: `box_patterns` has been removed
error: `box` patterns have been removed (feature `box_patterns`)
--> $DIR/removed-syntax-box-patterns.rs:2:9
|
LL | let box x = Box::new('c');
| ^^^^^
| ^^^
|
= help: enable feature `deref_patterns` instead and...
help: ...if possible just remove keyword `box`...
|
LL - let box x = Box::new('c');
LL + let x = Box::new('c');
|
help: ...otherwise replace it with an invocation of macro `deref`
|
LL - let box x = Box::new('c');
LL + let deref!(x) = Box::new('c');
|

error: `box_patterns` has been removed
error: `box` patterns have been removed (feature `box_patterns`)
--> $DIR/removed-syntax-box-patterns.rs:7:18
|
LL | let Packet { box x } = Packet { x: Box::new(0) };
| ^^^
|
= help: enable feature `deref_patterns` instead and...
help: ...if possible just remove keyword `box`...
|
LL - let Packet { box x } = Packet { x: Box::new(0) };
LL + let Packet { x } = Packet { x: Box::new(0) };
|
help: ...otherwise replace it with an invocation of macro `deref`
|
LL - let Packet { box x } = Packet { x: Box::new(0) };
LL + let Packet { x: deref!(x) } = Packet { x: Box::new(0) };
|

error: `box` patterns have been removed (feature `box_patterns`)
--> $DIR/removed-syntax-box-patterns.rs:10:18
|
LL | let Packet { box ref mut x };
| ^^^
|
= help: enable feature `deref_patterns` instead and...
help: ...if possible just remove keyword `box`...
|
LL - let Packet { box ref mut x };
LL + let Packet { ref mut x };
|
help: ...otherwise replace it with an invocation of macro `deref`
|
LL - let Packet { box ref mut x };
LL + let Packet { x: deref!(ref mut x) };
|

error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

14 changes: 0 additions & 14 deletions tests/ui/parser/removed-syntax/removed-syntax-box.fixed

This file was deleted.

14 changes: 0 additions & 14 deletions tests/ui/parser/removed-syntax/removed-syntax-box.rs

This file was deleted.

Loading
Loading