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
127 changes: 85 additions & 42 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub(super) enum DestructuredFloat {
/// 1.
TrailingDot(Symbol, Span, Span),
/// 1.2 | 1.2e3
MiddleDot(Symbol, Span, Span, Symbol, Span),
MiddleDot(Symbol, Span, Symbol, Span),
/// Invalid
Error,
}
Expand Down Expand Up @@ -961,6 +961,7 @@ impl<'a> Parser<'a> {
token::Ident(..) => self.parse_dot_suffix(base, lo),
token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {
let ident_span = self.token.span;
let symbol = self.validate_tuple_index(symbol, ident_span);
self.bump();
Ok(self.mk_expr_tuple_field_access(lo, ident_span, base, symbol, suffix))
}
Expand All @@ -986,13 +987,9 @@ impl<'a> Parser<'a> {
self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None)
}
// 1.2 | 1.2e3
DestructuredFloat::MiddleDot(
sym1,
ident1_span,
_dot_span,
sym2,
ident2_span,
) => {
// FIXME(fmease): (preexisting) For some reason for `x.0.0xyz` (i.e., suffixed)
// highlight `0.0xyz` when we should just highlight `0xyz`.
DestructuredFloat::MiddleDot(sym1, ident1_span, sym2, ident2_span) => {
// `foo.1.2` (or `foo.1.2e3`): two complete dot accesses. We end up with
// the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following
// token in `self.token`.
Expand Down Expand Up @@ -1055,30 +1052,68 @@ impl<'a> Parser<'a> {
// support pushing "future tokens" (would be also helpful to `break_and_eat`), or
// we should break everything including floats into more basic proc-macro style
// tokens in the lexer (probably preferable).
// FIXME(fmease): De-jank the impl.
pub(super) fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat {
#[derive(Debug)]
enum FloatComponent {
IdentLike(String),
IdentLike(IdentLike),
Punct(char),
}
use FloatComponent::*;

#[derive(Debug, Default)]
struct IdentLike {
str: String,
len: usize,
poisoned: bool,
}

let float_str = float.as_str();
let mut components = Vec::new();
let mut ident_like = String::new();
let mut ident_like = IdentLike::default();
let mut zero = false;

for c in float_str.chars() {
if c == '_' || c.is_ascii_alphanumeric() {
ident_like.push(c);
} else if matches!(c, '.' | '+' | '-') {
if !ident_like.is_empty() {
components.push(IdentLike(mem::take(&mut ident_like)));
match c {
'0'..='9' => {
ident_like.len += 1;
if zero {
ident_like.poisoned = true;
}
zero = c == '0' && ident_like.str.is_empty();
if !zero {
ident_like.str.push(c);
}
}
components.push(Punct(c));
} else {
panic!("unexpected character in a float token: {c:?}")
'_' | 'b' | 'o' | 'x' => {
ident_like.len += 1;
ident_like.poisoned = true;
}
'e' | 'E' => {
ident_like.len += 1;
ident_like.poisoned = true;
if mem::take(&mut zero) {
ident_like.str.push('0');
}
ident_like.str.push(c);
}
'.' | '+' | '-' => {
if mem::take(&mut zero) {
ident_like.str.push('0');
}
if !ident_like.str.is_empty() {
components.push(IdentLike(mem::take(&mut ident_like)));
}
components.push(Punct(c));
}
_ => panic!("unexpected character in a float token: {c:?}"),
}
}
if !ident_like.is_empty() {

if zero {
ident_like.str.push('0');
}
if !ident_like.str.is_empty() {
components.push(IdentLike(ident_like));
}

Expand All @@ -1090,44 +1125,58 @@ impl<'a> Parser<'a> {

match &*components {
// 1e2
[IdentLike(i)] => {
DestructuredFloat::Single(Symbol::intern(i), span)
}
[IdentLike(ident)] => {
if ident.poisoned {
self.dcx().span_err(span, "invalid tuple index");
}

DestructuredFloat::Single(Symbol::intern(&ident.str), span) },
// 1.
[IdentLike(left), Punct('.')] => {
let (left_span, dot_span) = if can_take_span_apart() {
let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));
let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len));
let dot_span = span.with_lo(left_span.hi());
(left_span, dot_span)
} else {
(span, span)
};
let left = Symbol::intern(left);
if left.poisoned {
self.dcx().span_err(left_span, "invalid tuple index");
}
let left = Symbol::intern(&left.str);
DestructuredFloat::TrailingDot(left, left_span, dot_span)
}
// 1.2 | 1.2e3
[IdentLike(left), Punct('.'), IdentLike(right)] => {
let (left_span, dot_span, right_span) = if can_take_span_apart() {
let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));
let (left_span, right_span) = if can_take_span_apart() {
let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len));
let dot_span = span.with_lo(left_span.hi()).with_hi(left_span.hi() + BytePos(1));
let right_span = span.with_lo(dot_span.hi());
(left_span, dot_span, right_span)
(left_span, right_span)
} else {
(span, span, span)
(span, span)
};
let left = Symbol::intern(left);
let right = Symbol::intern(right);
DestructuredFloat::MiddleDot(left, left_span, dot_span, right, right_span)
if left.poisoned {
self.dcx().span_err(left_span, "invalid tuple index");
}
let left = Symbol::intern(&left.str);
if right.poisoned {
self.dcx().span_err(right_span, "invalid tuple index");
}
let right = Symbol::intern(&right.str);
DestructuredFloat::MiddleDot(left, left_span, right, right_span)
}
// 1e+ | 1e- (recovered)
[IdentLike(_), Punct('+' | '-')] |
[IdentLike(..), Punct('+' | '-')] |
// 1e+2 | 1e-2
[IdentLike(_), Punct('+' | '-'), IdentLike(_)] |
[IdentLike(..), Punct('+' | '-'), IdentLike(..)] |
// 1.2e+ | 1.2e-
[IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-')] |
[IdentLike(..), Punct('.'), IdentLike(..), Punct('+' | '-')] |
// 1.2e+3 | 1.2e-3
[IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => {
[IdentLike(..), Punct('.'), IdentLike(..), Punct('+' | '-'), IdentLike(..)] => {
// See the FIXME about `TokenCursor` above.
// FIXME(fmease): We report 2 errors on `x.0e+1`.
// FIXME(fmease): (preexisting) Too many confusing errs in cases like `x.0x0.0`.
self.error_unexpected_after_dot();
DestructuredFloat::Error
}
Expand Down Expand Up @@ -1187,13 +1236,7 @@ impl<'a> Parser<'a> {
fields.insert(start_idx, Ident::new(sym, sym_span));
}
// 1.2 | 1.2e3
DestructuredFloat::MiddleDot(
symbol1,
span1,
_dot_span,
symbol2,
span2,
) => {
DestructuredFloat::MiddleDot(symbol1, span1, symbol2, span2) => {
trailing_dot = None;
fields.insert(start_idx, Ident::new(symbol2, span2));
fields.insert(start_idx, Ident::new(symbol1, span1));
Expand Down
37 changes: 34 additions & 3 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1307,7 +1307,11 @@ impl<'a> Parser<'a> {
if self.eat_keyword(exp!(Mut)) { Mutability::Mut } else { Mutability::Not }
}

/// Parses reference binding mode (`ref`, `ref mut`, `ref pin const`, `ref pin mut`, or nothing).
/// Parse a reference binding mode.
///
/// ```ebnf
/// ByRef = ("ref" ("mut" | "pin" ("const" | "mut"))?)?
/// ```
fn parse_byref(&mut self) -> ByRef {
if self.eat_keyword(exp!(Ref)) {
let (pinnedness, mutability) = self.parse_pin_and_mut();
Expand All @@ -1328,22 +1332,49 @@ impl<'a> Parser<'a> {
}
}

/// Parse a field name.
///
/// ```enbf
/// FieldName = TupleIndex | Ident
/// TupleIndex = re"0|[1-9][0-9]*"
/// ```
fn parse_field_name(&mut self) -> PResult<'a, Ident> {
// FIXME(fmease): It would be nice if we could emit a custom error when encountering
// float literals. E.g., ideally, we'd emit "invalid tuple index" for `1e1`.
// I'm even thinking about breaking up float lits here, just so we can emit
// unexpected token `.` for `1.2` etc.
if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = self.token.kind
{
let ident_span = self.token.span;
let symbol = self.validate_tuple_index(symbol, ident_span);
if let Some(suffix) = suffix {
self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex {
span: self.token.span,
span: ident_span,
suffix,
});
}
self.bump();
Ok(Ident::new(symbol, self.prev_token.span))
Ok(Ident::new(symbol, ident_span))
} else {
self.parse_ident_common(true)
}
}

// FIXME(fmease): De-jank this impl.
fn validate_tuple_index(&mut self, symbol: Symbol, span: Span) -> Symbol {
let str = symbol.as_str();

if str.contains(|c: char| !c.is_ascii_digit()) || matches!(str.as_bytes(), [b'0', _, ..]) {
self.dcx().span_err(span, "invalid tuple index");
let str = str.replace(|c: char| !c.is_ascii_digit(), "");
let str = str.trim_start_matches('0');
let str = if str.is_empty() { "0" } else { str };
return Symbol::intern(str);
}

symbol
}

fn parse_delim_args(&mut self) -> PResult<'a, Box<DelimArgs>> {
if let Some(args) = self.parse_delim_args_inner() {
Ok(Box::new(args))
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1731,11 +1731,14 @@ impl<'a> Parser<'a> {
self.dcx().emit_err(DotDotDotForRemainingFields { span: self.token.span, token_str });
}

/// Parse a field in a struct pattern.
///
/// ```ebnf
/// PatField = FieldName ":" Pat | "box"? "mut"? ByRef Ident
/// ```
fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField> {
// Check if a colon exists one ahead. This means we're parsing a fieldname.
let hi;
let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) {
// Parsing a pattern of the form `fieldname: pat`.
let fieldname = self.parse_field_name()?;
self.bump();
let pat = self.parse_pat_allow_top_guard(
Expand All @@ -1747,13 +1750,12 @@ impl<'a> Parser<'a> {
hi = pat.span;
(pat, fieldname, false)
} else {
// Parsing a pattern of the form `(box) (ref) (mut) fieldname`.
let is_box = self.eat_keyword(exp!(Box));
let boxed_span = self.token.span;
let mutability = self.parse_mutability();
let by_ref = self.parse_byref();

let fieldname = self.parse_field_name()?;
let fieldname = self.parse_ident_common(false)?;
hi = self.prev_token.span;
let ann = BindingMode(by_ref, mutability);
let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);
Expand Down
10 changes: 0 additions & 10 deletions tests/ui/numeric/numeric-fields.rs

This file was deleted.

28 changes: 0 additions & 28 deletions tests/ui/numeric/numeric-fields.stderr

This file was deleted.

17 changes: 10 additions & 7 deletions tests/ui/offset-of/offset-of-tuple-field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ use std::mem::offset_of;

fn main() {
offset_of!((u8, u8), _0); //~ ERROR no field `_0`
offset_of!((u8, u8), 01); //~ ERROR no field `01`
offset_of!((u8, u8), 1e2); //~ ERROR no field `1e2`
offset_of!((u8, u8), 1_u8); //~ ERROR no field `1_`
offset_of!((u8, u8), 01); //~ ERROR invalid tuple index
offset_of!((u8, u8), 1e2); //~ ERROR invalid tuple index
//~^ ERROR no field `1e2`
offset_of!((u8, u8), 1_u8); //~ ERROR invalid tuple index
//~| ERROR suffixes on a tuple index

builtin # offset_of((u8, u8), 1e2); //~ ERROR no field `1e2`
builtin # offset_of((u8, u8), 1e2); //~ ERROR invalid tuple index
//~^ ERROR no field `1e2`
builtin # offset_of((u8, u8), _0); //~ ERROR no field `_0`
builtin # offset_of((u8, u8), 01); //~ ERROR no field `01`
builtin # offset_of((u8, u8), 1_u8); //~ ERROR no field `1_`
builtin # offset_of((u8, u8), 01); //~ ERROR invalid tuple index
builtin # offset_of((u8, u8), 1_u8); //~ ERROR invalid tuple index
//~| ERROR suffixes on a tuple index

offset_of!(((u8, u16), (u32, u16, u8)), 0.2); //~ ERROR no field `2`
offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2); //~ ERROR no field `1e2`
offset_of!(((u8, u16), (u32, u16, u8)), 0.1e2); //~ ERROR invalid tuple index
//~^ ERROR no field `1e2`
offset_of!(((u8, u16), (u32, u16, u8)), 1.2);
offset_of!(((u8, u16), (u32, u16, u8)), 1.2.0); //~ ERROR no field `0`
}
Loading
Loading