Fix some grammar inconsistencies for the '..' range notation.
Grammar changes:
* allow 'for _ in 1..i {}' (fixes #20241)
* allow 'for _ in 1.. {}' as infinite loop
* prevent use of range notation in contexts where only operators of high
precedence are expected (fixes #20811)
Parser code cleanup:
* remove RESTRICTION_NO_DOTS
* make AS_PREC const and follow naming convention
* make min_prec inclusive
This commit is contained in:
@@ -3135,18 +3135,17 @@ The precedence of Rust binary operators is ordered as follows, going from
|
|||||||
strong to weak:
|
strong to weak:
|
||||||
|
|
||||||
```{.text .precedence}
|
```{.text .precedence}
|
||||||
* / %
|
|
||||||
as
|
as
|
||||||
|
* / %
|
||||||
+ -
|
+ -
|
||||||
<< >>
|
<< >>
|
||||||
&
|
&
|
||||||
^
|
^
|
||||||
|
|
|
|
||||||
< > <= >=
|
== != < > <= >=
|
||||||
== !=
|
|
||||||
&&
|
&&
|
||||||
||
|
||
|
||||||
=
|
= ..
|
||||||
```
|
```
|
||||||
|
|
||||||
Operators at the same precedence level are evaluated left-to-right. [Unary
|
Operators at the same precedence level are evaluated left-to-right. [Unary
|
||||||
|
|||||||
@@ -336,8 +336,7 @@ pub fn operator_prec(op: ast::BinOp) -> usize {
|
|||||||
|
|
||||||
/// Precedence of the `as` operator, which is a binary operator
|
/// Precedence of the `as` operator, which is a binary operator
|
||||||
/// not appearing in the prior table.
|
/// not appearing in the prior table.
|
||||||
#[allow(non_upper_case_globals)]
|
pub const AS_PREC: usize = 12us;
|
||||||
pub static as_prec: usize = 12us;
|
|
||||||
|
|
||||||
pub fn empty_generics() -> Generics {
|
pub fn empty_generics() -> Generics {
|
||||||
Generics {
|
Generics {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ use ast::{UnnamedField, UnsafeBlock};
|
|||||||
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
|
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
|
||||||
use ast::{Visibility, WhereClause};
|
use ast::{Visibility, WhereClause};
|
||||||
use ast;
|
use ast;
|
||||||
use ast_util::{self, as_prec, ident_to_path, operator_prec};
|
use ast_util::{self, AS_PREC, ident_to_path, operator_prec};
|
||||||
use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp};
|
use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp};
|
||||||
use diagnostic;
|
use diagnostic;
|
||||||
use ext::tt::macro_parser;
|
use ext::tt::macro_parser;
|
||||||
@@ -93,7 +93,6 @@ bitflags! {
|
|||||||
const RESTRICTION_STMT_EXPR = 0b0001,
|
const RESTRICTION_STMT_EXPR = 0b0001,
|
||||||
const RESTRICTION_NO_BAR_OP = 0b0010,
|
const RESTRICTION_NO_BAR_OP = 0b0010,
|
||||||
const RESTRICTION_NO_STRUCT_LITERAL = 0b0100,
|
const RESTRICTION_NO_STRUCT_LITERAL = 0b0100,
|
||||||
const RESTRICTION_NO_DOTS = 0b1000,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2775,13 +2774,6 @@ impl<'a> Parser<'a> {
|
|||||||
hi = e.span.hi;
|
hi = e.span.hi;
|
||||||
ex = ExprAddrOf(m, e);
|
ex = ExprAddrOf(m, e);
|
||||||
}
|
}
|
||||||
token::DotDot if !self.restrictions.contains(RESTRICTION_NO_DOTS) => {
|
|
||||||
// A range, closed above: `..expr`.
|
|
||||||
self.bump();
|
|
||||||
let e = self.parse_expr();
|
|
||||||
hi = e.span.hi;
|
|
||||||
ex = self.mk_range(None, Some(e));
|
|
||||||
}
|
|
||||||
token::Ident(_, _) => {
|
token::Ident(_, _) => {
|
||||||
if !self.token.is_keyword(keywords::Box) {
|
if !self.token.is_keyword(keywords::Box) {
|
||||||
return self.parse_dot_or_call_expr();
|
return self.parse_dot_or_call_expr();
|
||||||
@@ -2855,10 +2847,10 @@ impl<'a> Parser<'a> {
|
|||||||
self.check_no_chained_comparison(&*lhs, cur_op)
|
self.check_no_chained_comparison(&*lhs, cur_op)
|
||||||
}
|
}
|
||||||
let cur_prec = operator_prec(cur_op);
|
let cur_prec = operator_prec(cur_op);
|
||||||
if cur_prec > min_prec {
|
if cur_prec >= min_prec {
|
||||||
self.bump();
|
self.bump();
|
||||||
let expr = self.parse_prefix_expr();
|
let expr = self.parse_prefix_expr();
|
||||||
let rhs = self.parse_more_binops(expr, cur_prec);
|
let rhs = self.parse_more_binops(expr, cur_prec + 1);
|
||||||
let lhs_span = lhs.span;
|
let lhs_span = lhs.span;
|
||||||
let rhs_span = rhs.span;
|
let rhs_span = rhs.span;
|
||||||
let binary = self.mk_binary(cur_op, lhs, rhs);
|
let binary = self.mk_binary(cur_op, lhs, rhs);
|
||||||
@@ -2869,7 +2861,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if as_prec > min_prec && self.eat_keyword(keywords::As) {
|
if AS_PREC >= min_prec && self.eat_keyword(keywords::As) {
|
||||||
let rhs = self.parse_ty();
|
let rhs = self.parse_ty();
|
||||||
let _as = self.mk_expr(lhs.span.lo,
|
let _as = self.mk_expr(lhs.span.lo,
|
||||||
rhs.span.hi,
|
rhs.span.hi,
|
||||||
@@ -2905,9 +2897,25 @@ impl<'a> Parser<'a> {
|
|||||||
/// actually, this seems to be the main entry point for
|
/// actually, this seems to be the main entry point for
|
||||||
/// parsing an arbitrary expression.
|
/// parsing an arbitrary expression.
|
||||||
pub fn parse_assign_expr(&mut self) -> P<Expr> {
|
pub fn parse_assign_expr(&mut self) -> P<Expr> {
|
||||||
|
match self.token {
|
||||||
|
token::DotDot => {
|
||||||
|
// prefix-form of range notation '..expr'
|
||||||
|
// This has the same precedence as assignment expressions
|
||||||
|
// (much lower than other prefix expressions) to be consistent
|
||||||
|
// with the postfix-form 'expr..'
|
||||||
|
let lo = self.span.lo;
|
||||||
|
self.bump();
|
||||||
|
let rhs = self.parse_binops();
|
||||||
|
let hi = rhs.span.hi;
|
||||||
|
let ex = self.mk_range(None, Some(rhs));
|
||||||
|
self.mk_expr(lo, hi, ex)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
let lhs = self.parse_binops();
|
let lhs = self.parse_binops();
|
||||||
self.parse_assign_expr_with(lhs)
|
self.parse_assign_expr_with(lhs)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_assign_expr_with(&mut self, lhs: P<Expr>) -> P<Expr> {
|
pub fn parse_assign_expr_with(&mut self, lhs: P<Expr>) -> P<Expr> {
|
||||||
let restrictions = self.restrictions & RESTRICTION_NO_STRUCT_LITERAL;
|
let restrictions = self.restrictions & RESTRICTION_NO_STRUCT_LITERAL;
|
||||||
@@ -2938,11 +2946,11 @@ impl<'a> Parser<'a> {
|
|||||||
self.mk_expr(span.lo, rhs_span.hi, assign_op)
|
self.mk_expr(span.lo, rhs_span.hi, assign_op)
|
||||||
}
|
}
|
||||||
// A range expression, either `expr..expr` or `expr..`.
|
// A range expression, either `expr..expr` or `expr..`.
|
||||||
token::DotDot if !self.restrictions.contains(RESTRICTION_NO_DOTS) => {
|
token::DotDot => {
|
||||||
self.bump();
|
self.bump();
|
||||||
|
|
||||||
let opt_end = if self.token.can_begin_expr() {
|
let opt_end = if self.is_at_start_of_range_notation_rhs() {
|
||||||
let end = self.parse_expr_res(RESTRICTION_NO_DOTS);
|
let end = self.parse_binops();
|
||||||
Some(end)
|
Some(end)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -2960,6 +2968,18 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_at_start_of_range_notation_rhs(&self) -> bool {
|
||||||
|
if self.token.can_begin_expr() {
|
||||||
|
// parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.
|
||||||
|
if self.token == token::OpenDelim(token::Brace) {
|
||||||
|
return !self.restrictions.contains(RESTRICTION_NO_STRUCT_LITERAL);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse an 'if' or 'if let' expression ('if' token already eaten)
|
/// Parse an 'if' or 'if let' expression ('if' token already eaten)
|
||||||
pub fn parse_if_expr(&mut self) -> P<Expr> {
|
pub fn parse_if_expr(&mut self) -> P<Expr> {
|
||||||
if self.token.is_keyword(keywords::Let) {
|
if self.token.is_keyword(keywords::Let) {
|
||||||
|
|||||||
16
src/test/compile-fail/range-3.rs
Normal file
16
src/test/compile-fail/range-3.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Test range syntax - syntax errors.
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let r = 1..2..3;
|
||||||
|
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
|
||||||
|
}
|
||||||
16
src/test/compile-fail/range-4.rs
Normal file
16
src/test/compile-fail/range-4.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Test range syntax - syntax errors.
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let r = ..1..2;
|
||||||
|
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
|
||||||
|
}
|
||||||
@@ -48,5 +48,12 @@ fn main() {
|
|||||||
assert!(x == &a[3..]);
|
assert!(x == &a[3..]);
|
||||||
|
|
||||||
for _i in 2+4..10-3 {}
|
for _i in 2+4..10-3 {}
|
||||||
|
|
||||||
|
let i = 42;
|
||||||
|
for _ in 1..i {}
|
||||||
|
for _ in 1.. { break; }
|
||||||
|
|
||||||
|
let x = [1]..[2];
|
||||||
|
assert!(x == (([1])..([2])));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user