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:
Daniel Grunwald
2015-01-18 21:43:03 +01:00
parent b7930d93d9
commit db013f9f45
6 changed files with 80 additions and 23 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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) {

View 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 `..`
}

View 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 `..`
}

View File

@@ -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])));
} }