Skip to content

Commit 81bcee6

Browse files
authored
Rollup merge of rust-lang#71322 - petrochenkov:tuple00, r=nikomatsakis
Accept tuple.0.0 as tuple indexing (take 2) If we expect something identifier-like when parsing a field name after `.`, but encounter a float token, we break that float token into parts, similarly to how we break `&&` into `&` `&`, or `<<` into `<` `<`, etc. An alternative to rust-lang#70420.
2 parents daecab3 + 52bdaaa commit 81bcee6

13 files changed

+614
-59
lines changed

src/librustc_parse/parser/expr.rs

+81-38
Original file line numberDiff line numberDiff line change
@@ -770,10 +770,10 @@ impl<'a> Parser<'a> {
770770
match self.token.uninterpolate().kind {
771771
token::Ident(..) => self.parse_dot_suffix(base, lo),
772772
token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {
773-
Ok(self.parse_tuple_field_access_expr(lo, base, symbol, suffix))
773+
Ok(self.parse_tuple_field_access_expr(lo, base, symbol, suffix, None))
774774
}
775-
token::Literal(token::Lit { kind: token::Float, symbol, .. }) => {
776-
self.recover_field_access_by_float_lit(lo, base, symbol)
775+
token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => {
776+
Ok(self.parse_tuple_field_access_expr_float(lo, base, symbol, suffix))
777777
}
778778
_ => {
779779
self.error_unexpected_after_dot();
@@ -788,45 +788,84 @@ impl<'a> Parser<'a> {
788788
self.struct_span_err(self.token.span, &format!("unexpected token: `{}`", actual)).emit();
789789
}
790790

791-
fn recover_field_access_by_float_lit(
791+
// We need and identifier or integer, but the next token is a float.
792+
// Break the float into components to extract the identifier or integer.
793+
// FIXME: With current `TokenCursor` it's hard to break tokens into more than 2
794+
// parts unless those parts are processed immediately. `TokenCursor` should either
795+
// support pushing "future tokens" (would be also helpful to `break_and_eat`), or
796+
// we should break everything including floats into more basic proc-macro style
797+
// tokens in the lexer (probably preferable).
798+
fn parse_tuple_field_access_expr_float(
792799
&mut self,
793800
lo: Span,
794801
base: P<Expr>,
795-
sym: Symbol,
796-
) -> PResult<'a, P<Expr>> {
797-
self.bump();
798-
799-
let fstr = sym.as_str();
800-
let msg = format!("unexpected token: `{}`", sym);
801-
802-
let mut err = self.struct_span_err(self.prev_token.span, &msg);
803-
err.span_label(self.prev_token.span, "unexpected token");
804-
805-
if fstr.chars().all(|x| "0123456789.".contains(x)) {
806-
let float = match fstr.parse::<f64>() {
807-
Ok(f) => f,
808-
Err(_) => {
809-
err.emit();
810-
return Ok(base);
802+
float: Symbol,
803+
suffix: Option<Symbol>,
804+
) -> P<Expr> {
805+
#[derive(Debug)]
806+
enum FloatComponent {
807+
IdentLike(String),
808+
Punct(char),
809+
}
810+
use FloatComponent::*;
811+
812+
let mut components = Vec::new();
813+
let mut ident_like = String::new();
814+
for c in float.as_str().chars() {
815+
if c == '_' || c.is_ascii_alphanumeric() {
816+
ident_like.push(c);
817+
} else if matches!(c, '.' | '+' | '-') {
818+
if !ident_like.is_empty() {
819+
components.push(IdentLike(mem::take(&mut ident_like)));
811820
}
812-
};
813-
let sugg = pprust::to_string(|s| {
814-
s.popen();
815-
s.print_expr(&base);
816-
s.s.word(".");
817-
s.print_usize(float.trunc() as usize);
818-
s.pclose();
819-
s.s.word(".");
820-
s.s.word(fstr.splitn(2, '.').last().unwrap().to_string())
821-
});
822-
err.span_suggestion(
823-
lo.to(self.prev_token.span),
824-
"try parenthesizing the first index",
825-
sugg,
826-
Applicability::MachineApplicable,
827-
);
821+
components.push(Punct(c));
822+
} else {
823+
panic!("unexpected character in a float token: {:?}", c)
824+
}
825+
}
826+
if !ident_like.is_empty() {
827+
components.push(IdentLike(ident_like));
828+
}
829+
830+
// FIXME: Make the span more precise.
831+
let span = self.token.span;
832+
match &*components {
833+
// 1e2
834+
[IdentLike(i)] => {
835+
self.parse_tuple_field_access_expr(lo, base, Symbol::intern(&i), suffix, None)
836+
}
837+
// 1.
838+
[IdentLike(i), Punct('.')] => {
839+
assert!(suffix.is_none());
840+
let symbol = Symbol::intern(&i);
841+
self.token = Token::new(token::Ident(symbol, false), span);
842+
let next_token = Token::new(token::Dot, span);
843+
self.parse_tuple_field_access_expr(lo, base, symbol, None, Some(next_token))
844+
}
845+
// 1.2 | 1.2e3
846+
[IdentLike(i1), Punct('.'), IdentLike(i2)] => {
847+
let symbol1 = Symbol::intern(&i1);
848+
self.token = Token::new(token::Ident(symbol1, false), span);
849+
let next_token1 = Token::new(token::Dot, span);
850+
let base1 =
851+
self.parse_tuple_field_access_expr(lo, base, symbol1, None, Some(next_token1));
852+
let symbol2 = Symbol::intern(&i2);
853+
let next_token2 = Token::new(token::Ident(symbol2, false), span);
854+
self.bump_with(next_token2); // `.`
855+
self.parse_tuple_field_access_expr(lo, base1, symbol2, suffix, None)
856+
}
857+
// 1e+ | 1e- (recovered)
858+
[IdentLike(_), Punct('+' | '-')] |
859+
// 1e+2 | 1e-2
860+
[IdentLike(_), Punct('+' | '-'), IdentLike(_)] |
861+
// 1.2e+3 | 1.2e-3
862+
[IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => {
863+
// See the FIXME about `TokenCursor` above.
864+
self.error_unexpected_after_dot();
865+
base
866+
}
867+
_ => panic!("unexpected components in a float token: {:?}", components),
828868
}
829-
Err(err)
830869
}
831870

832871
fn parse_tuple_field_access_expr(
@@ -835,8 +874,12 @@ impl<'a> Parser<'a> {
835874
base: P<Expr>,
836875
field: Symbol,
837876
suffix: Option<Symbol>,
877+
next_token: Option<Token>,
838878
) -> P<Expr> {
839-
self.bump();
879+
match next_token {
880+
Some(next_token) => self.bump_with(next_token),
881+
None => self.bump(),
882+
}
840883
let span = self.prev_token.span;
841884
let field = ExprKind::Field(base, Ident::new(field, span));
842885
self.expect_no_suffix(span, "a tuple index", suffix);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
struct S(u8, (u8, u8));
2+
3+
macro_rules! generate_field_accesses {
4+
($a:tt, $b:literal, $c:expr) => {
5+
let s = S(0, (0, 0));
6+
7+
s.$a; // OK
8+
{ s.$b; } //~ ERROR unexpected token: `1.1`
9+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1`
10+
{ s.$c; } //~ ERROR unexpected token: `1.1`
11+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1`
12+
};
13+
}
14+
15+
fn main() {
16+
generate_field_accesses!(1.1, 1.1, 1.1);
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
error: unexpected token: `1.1`
2+
--> $DIR/float-field-interpolated.rs:8:13
3+
|
4+
LL | { s.$b; }
5+
| ^^
6+
...
7+
LL | generate_field_accesses!(1.1, 1.1, 1.1);
8+
| ---------------------------------------- in this macro invocation
9+
|
10+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
11+
12+
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1`
13+
--> $DIR/float-field-interpolated.rs:8:13
14+
|
15+
LL | { s.$b; }
16+
| ^^ expected one of `.`, `;`, `?`, `}`, or an operator
17+
...
18+
LL | generate_field_accesses!(1.1, 1.1, 1.1);
19+
| ---------------------------------------- in this macro invocation
20+
|
21+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
22+
23+
error: unexpected token: `1.1`
24+
--> $DIR/float-field-interpolated.rs:10:13
25+
|
26+
LL | { s.$c; }
27+
| ^^
28+
...
29+
LL | generate_field_accesses!(1.1, 1.1, 1.1);
30+
| ---------------------------------------- in this macro invocation
31+
|
32+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
33+
34+
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1`
35+
--> $DIR/float-field-interpolated.rs:10:13
36+
|
37+
LL | { s.$c; }
38+
| ^^ expected one of `.`, `;`, `?`, `}`, or an operator
39+
...
40+
LL | generate_field_accesses!(1.1, 1.1, 1.1);
41+
| ---------------------------------------- in this macro invocation
42+
|
43+
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
44+
45+
error: aborting due to 4 previous errors
46+

src/test/ui/parser/float-field.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
struct S(u8, (u8, u8));
2+
3+
fn main() {
4+
let s = S(0, (0, 0));
5+
6+
s.1e1; //~ ERROR no field `1e1` on type `S`
7+
s.1.; //~ ERROR unexpected token: `;`
8+
s.1.1;
9+
s.1.1e1; //~ ERROR no field `1e1` on type `(u8, u8)`
10+
{ s.1e+; } //~ ERROR unexpected token: `1e+`
11+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+`
12+
//~| ERROR expected at least one digit in exponent
13+
{ s.1e-; } //~ ERROR unexpected token: `1e-`
14+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-`
15+
//~| ERROR expected at least one digit in exponent
16+
{ s.1e+1; } //~ ERROR unexpected token: `1e+1`
17+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+1`
18+
{ s.1e-1; } //~ ERROR unexpected token: `1e-1`
19+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-1`
20+
{ s.1.1e+1; } //~ ERROR unexpected token: `1.1e+1`
21+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e+1`
22+
{ s.1.1e-1; } //~ ERROR unexpected token: `1.1e-1`
23+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e-1`
24+
s.0x1e1; //~ ERROR no field `0x1e1` on type `S`
25+
s.0x1.; //~ ERROR no field `0x1` on type `S`
26+
//~| ERROR hexadecimal float literal is not supported
27+
//~| ERROR unexpected token: `;`
28+
s.0x1.1; //~ ERROR no field `0x1` on type `S`
29+
//~| ERROR hexadecimal float literal is not supported
30+
s.0x1.1e1; //~ ERROR no field `0x1` on type `S`
31+
//~| ERROR hexadecimal float literal is not supported
32+
{ s.0x1e+; } //~ ERROR expected expression, found `;`
33+
{ s.0x1e-; } //~ ERROR expected expression, found `;`
34+
s.0x1e+1; //~ ERROR no field `0x1e` on type `S`
35+
s.0x1e-1; //~ ERROR no field `0x1e` on type `S`
36+
{ s.0x1.1e+1; } //~ ERROR unexpected token: `0x1.1e+1`
37+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e+1`
38+
//~| ERROR hexadecimal float literal is not supported
39+
{ s.0x1.1e-1; } //~ ERROR unexpected token: `0x1.1e-1`
40+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `0x1.1e-1`
41+
//~| ERROR hexadecimal float literal is not supported
42+
s.1e1f32; //~ ERROR no field `1e1` on type `S`
43+
//~| ERROR suffixes on a tuple index are invalid
44+
s.1.f32; //~ ERROR no field `f32` on type `(u8, u8)`
45+
s.1.1f32; //~ ERROR suffixes on a tuple index are invalid
46+
s.1.1e1f32; //~ ERROR no field `1e1` on type `(u8, u8)`
47+
//~| ERROR suffixes on a tuple index are invalid
48+
{ s.1e+f32; } //~ ERROR unexpected token: `1e+f32`
49+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+f32`
50+
//~| ERROR expected at least one digit in exponent
51+
{ s.1e-f32; } //~ ERROR unexpected token: `1e-f32`
52+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-f32`
53+
//~| ERROR expected at least one digit in exponent
54+
{ s.1e+1f32; } //~ ERROR unexpected token: `1e+1f32`
55+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e+1f32`
56+
{ s.1e-1f32; } //~ ERROR unexpected token: `1e-1f32`
57+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1e-1f32`
58+
{ s.1.1e+1f32; } //~ ERROR unexpected token: `1.1e+1f32`
59+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e+1f32`
60+
{ s.1.1e-1f32; } //~ ERROR unexpected token: `1.1e-1f32`
61+
//~| ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `1.1e-1f32`
62+
}

0 commit comments

Comments
 (0)