Skip to content

Accept tuple.0.0 as tuple indexing #70420

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

Closed
wants to merge 9 commits into from
19 changes: 17 additions & 2 deletions src/librustc_parse/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl<'a> StringReader<'a> {
/// Turns simple `rustc_lexer::TokenKind` enum into a rich
/// `librustc_ast::TokenKind`. This turns strings into interned
/// symbols and runs additional validation.
fn cook_lexer_token(&self, token: rustc_lexer::TokenKind, start: BytePos) -> TokenKind {
fn cook_lexer_token(&mut self, token: rustc_lexer::TokenKind, start: BytePos) -> TokenKind {
match token {
rustc_lexer::TokenKind::LineComment => {
let string = self.str_from(start);
Expand Down Expand Up @@ -323,7 +323,7 @@ impl<'a> StringReader<'a> {
}

fn cook_lexer_literal(
&self,
&mut self,
start: BytePos,
suffix_start: BytePos,
kind: rustc_lexer::LiteralKind,
Expand Down Expand Up @@ -440,6 +440,16 @@ impl<'a> StringReader<'a> {
_ => (),
}

// tuple.0.0
// ^^^ we truncate this "float" literal at the dot.
if self.ends_with(start, ".") && !self.ends_with(start, "..") {
if let Some(dot) = self.str_from(start).find('.') {
self.pos = start + BytePos(dot as u32);
let id = self.symbol_from(start);
return (token::Integer, id);
}
}

let id = self.symbol_from_to(start, suffix_start);
(token::Float, id)
}
Expand Down Expand Up @@ -474,6 +484,11 @@ impl<'a> StringReader<'a> {
&self.src[self.src_index(start)..self.src_index(end)]
}

/// Whether the source text leading up to position `pos` ends with `suffix`.
fn ends_with(&self, pos: BytePos, suffix: &str) -> bool {
self.src[..self.src_index(pos)].ends_with(suffix)
}

fn forbid_bare_cr(&self, start: BytePos, s: &str, errmsg: &str) {
let mut idx = 0;
loop {
Expand Down
47 changes: 4 additions & 43 deletions src/librustc_parse/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,61 +767,22 @@ impl<'a> Parser<'a> {
token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {
Ok(self.parse_tuple_field_access_expr(lo, base, symbol, suffix))
}
token::Literal(token::Lit { kind: token::Float, symbol, .. }) => {
self.recover_field_access_by_float_lit(lo, base, symbol)
}
_ => {
self.error_unexpected_after_dot();
Ok(base)
}
}
}

fn error_unexpected_after_dot(&self) {
fn error_unexpected_after_dot(&mut self) {
// FIXME Could factor this out into non_fatal_unexpected or something.
let actual = pprust::token_to_string(&self.token);
self.struct_span_err(self.token.span, &format!("unexpected token: `{}`", actual)).emit();
}

fn recover_field_access_by_float_lit(
Copy link
Contributor

Choose a reason for hiding this comment

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

Happy to see this method go; it was ungreat to refactor in the past :) 🎉

&mut self,
lo: Span,
base: P<Expr>,
sym: Symbol,
) -> PResult<'a, P<Expr>> {
self.bump();

let fstr = sym.as_str();
let msg = format!("unexpected token: `{}`", sym);

let mut err = self.struct_span_err(self.prev_token.span, &msg);
err.span_label(self.prev_token.span, "unexpected token");

if fstr.chars().all(|x| "0123456789.".contains(x)) {
let float = match fstr.parse::<f64>() {
Ok(f) => f,
Err(_) => {
err.emit();
return Ok(base);
}
};
let sugg = pprust::to_string(|s| {
s.popen();
s.print_expr(&base);
s.s.word(".");
s.print_usize(float.trunc() as usize);
s.pclose();
s.s.word(".");
s.s.word(fstr.splitn(2, '.').last().unwrap().to_string())
});
err.span_suggestion(
lo.to(self.prev_token.span),
"try parenthesizing the first index",
sugg,
Applicability::MachineApplicable,
);
// Recover `tuple. 0.0`.
if let token::Literal(token::Lit { kind: token::Float, .. }) = self.token.kind {
self.bump();
}
Err(err)
}

fn parse_tuple_field_access_expr(
Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/tuple/index-float.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn main() {
let tuple = (((),),);

let _ = tuple. 0.0; //~ ERROR unexpected token: `0.0`

let _ = tuple./*special cases*/0.0; //~ ERROR unexpected token: `0.0`
}
14 changes: 14 additions & 0 deletions src/test/ui/tuple/index-float.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: unexpected token: `0.0`
--> $DIR/index-float.rs:4:20
|
LL | let _ = tuple. 0.0;
| ^^^

error: unexpected token: `0.0`
--> $DIR/index-float.rs:6:36
|
LL | let _ = tuple./*special cases*/0.0;
| ^^^

error: aborting due to 2 previous errors

7 changes: 7 additions & 0 deletions src/test/ui/tuple/index-invalid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn main() {
let _ = (((),),).1.0; //~ ERROR no field `1` on type `(((),),)`

let _ = (((),),).0.1; //~ ERROR no field `1` on type `((),)`

let _ = (((),),).000.000; //~ ERROR no field `000` on type `(((),),)`
}
21 changes: 21 additions & 0 deletions src/test/ui/tuple/index-invalid.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0609]: no field `1` on type `(((),),)`
--> $DIR/index-invalid.rs:2:22
|
LL | let _ = (((),),).1.0;
| ^

error[E0609]: no field `1` on type `((),)`
--> $DIR/index-invalid.rs:4:24
|
LL | let _ = (((),),).0.1;
| ^

error[E0609]: no field `000` on type `(((),),)`
--> $DIR/index-invalid.rs:6:22
|
LL | let _ = (((),),).000.000;
| ^^^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0609`.
9 changes: 9 additions & 0 deletions src/test/ui/tuple/indexing-in-macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Broken by https://github.com/rust-lang/rust/pull/70420.

macro_rules! m {
(.$l:literal) => {};
}

m!(.0.0); //~ ERROR no rules expected the token `.`

fn main() {}
11 changes: 11 additions & 0 deletions src/test/ui/tuple/indexing-in-macro.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: no rules expected the token `.`
--> $DIR/indexing-in-macro.rs:7:6
|
LL | macro_rules! m {
| -------------- when calling this macro
...
LL | m!(.0.0);
| ^ no rules expected this token in macro call

error: aborting due to previous error

12 changes: 12 additions & 0 deletions src/test/ui/tuple/nested-index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// run-pass

fn main () {
let n = (1, (2, 3)).1.1;
assert_eq!(n, 3);

let n = (1, (2, (3, 4))).1.1.1;
assert_eq!(n, 4);

// This is a range expression, not nested indexing.
let _ = 0.0..1.1;
}
5 changes: 0 additions & 5 deletions src/test/ui/tuple/tuple-float-index.fixed

This file was deleted.

5 changes: 0 additions & 5 deletions src/test/ui/tuple/tuple-float-index.rs

This file was deleted.

11 changes: 0 additions & 11 deletions src/test/ui/tuple/tuple-float-index.stderr

This file was deleted.