Skip to content

Commit db013f9

Browse files
committed
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
1 parent b7930d9 commit db013f9

File tree

6 files changed

+80
-23
lines changed

6 files changed

+80
-23
lines changed

src/doc/reference.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -3135,18 +3135,17 @@ The precedence of Rust binary operators is ordered as follows, going from
31353135
strong to weak:
31363136

31373137
```{.text .precedence}
3138-
* / %
31393138
as
3139+
* / %
31403140
+ -
31413141
<< >>
31423142
&
31433143
^
31443144
|
3145-
< > <= >=
3146-
== !=
3145+
== != < > <= >=
31473146
&&
31483147
||
3149-
=
3148+
= ..
31503149
```
31513150

31523151
Operators at the same precedence level are evaluated left-to-right. [Unary

src/libsyntax/ast_util.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,7 @@ pub fn operator_prec(op: ast::BinOp) -> usize {
336336

337337
/// Precedence of the `as` operator, which is a binary operator
338338
/// not appearing in the prior table.
339-
#[allow(non_upper_case_globals)]
340-
pub static as_prec: usize = 12us;
339+
pub const AS_PREC: usize = 12us;
341340

342341
pub fn empty_generics() -> Generics {
343342
Generics {

src/libsyntax/parse/parser.rs

+37-17
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use ast::{UnnamedField, UnsafeBlock};
6262
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
6363
use ast::{Visibility, WhereClause};
6464
use ast;
65-
use ast_util::{self, as_prec, ident_to_path, operator_prec};
65+
use ast_util::{self, AS_PREC, ident_to_path, operator_prec};
6666
use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp};
6767
use diagnostic;
6868
use ext::tt::macro_parser;
@@ -93,7 +93,6 @@ bitflags! {
9393
const RESTRICTION_STMT_EXPR = 0b0001,
9494
const RESTRICTION_NO_BAR_OP = 0b0010,
9595
const RESTRICTION_NO_STRUCT_LITERAL = 0b0100,
96-
const RESTRICTION_NO_DOTS = 0b1000,
9796
}
9897
}
9998

@@ -2775,13 +2774,6 @@ impl<'a> Parser<'a> {
27752774
hi = e.span.hi;
27762775
ex = ExprAddrOf(m, e);
27772776
}
2778-
token::DotDot if !self.restrictions.contains(RESTRICTION_NO_DOTS) => {
2779-
// A range, closed above: `..expr`.
2780-
self.bump();
2781-
let e = self.parse_expr();
2782-
hi = e.span.hi;
2783-
ex = self.mk_range(None, Some(e));
2784-
}
27852777
token::Ident(_, _) => {
27862778
if !self.token.is_keyword(keywords::Box) {
27872779
return self.parse_dot_or_call_expr();
@@ -2855,10 +2847,10 @@ impl<'a> Parser<'a> {
28552847
self.check_no_chained_comparison(&*lhs, cur_op)
28562848
}
28572849
let cur_prec = operator_prec(cur_op);
2858-
if cur_prec > min_prec {
2850+
if cur_prec >= min_prec {
28592851
self.bump();
28602852
let expr = self.parse_prefix_expr();
2861-
let rhs = self.parse_more_binops(expr, cur_prec);
2853+
let rhs = self.parse_more_binops(expr, cur_prec + 1);
28622854
let lhs_span = lhs.span;
28632855
let rhs_span = rhs.span;
28642856
let binary = self.mk_binary(cur_op, lhs, rhs);
@@ -2869,7 +2861,7 @@ impl<'a> Parser<'a> {
28692861
}
28702862
}
28712863
None => {
2872-
if as_prec > min_prec && self.eat_keyword(keywords::As) {
2864+
if AS_PREC >= min_prec && self.eat_keyword(keywords::As) {
28732865
let rhs = self.parse_ty();
28742866
let _as = self.mk_expr(lhs.span.lo,
28752867
rhs.span.hi,
@@ -2905,8 +2897,24 @@ impl<'a> Parser<'a> {
29052897
/// actually, this seems to be the main entry point for
29062898
/// parsing an arbitrary expression.
29072899
pub fn parse_assign_expr(&mut self) -> P<Expr> {
2908-
let lhs = self.parse_binops();
2909-
self.parse_assign_expr_with(lhs)
2900+
match self.token {
2901+
token::DotDot => {
2902+
// prefix-form of range notation '..expr'
2903+
// This has the same precedence as assignment expressions
2904+
// (much lower than other prefix expressions) to be consistent
2905+
// with the postfix-form 'expr..'
2906+
let lo = self.span.lo;
2907+
self.bump();
2908+
let rhs = self.parse_binops();
2909+
let hi = rhs.span.hi;
2910+
let ex = self.mk_range(None, Some(rhs));
2911+
self.mk_expr(lo, hi, ex)
2912+
}
2913+
_ => {
2914+
let lhs = self.parse_binops();
2915+
self.parse_assign_expr_with(lhs)
2916+
}
2917+
}
29102918
}
29112919

29122920
pub fn parse_assign_expr_with(&mut self, lhs: P<Expr>) -> P<Expr> {
@@ -2938,11 +2946,11 @@ impl<'a> Parser<'a> {
29382946
self.mk_expr(span.lo, rhs_span.hi, assign_op)
29392947
}
29402948
// A range expression, either `expr..expr` or `expr..`.
2941-
token::DotDot if !self.restrictions.contains(RESTRICTION_NO_DOTS) => {
2949+
token::DotDot => {
29422950
self.bump();
29432951

2944-
let opt_end = if self.token.can_begin_expr() {
2945-
let end = self.parse_expr_res(RESTRICTION_NO_DOTS);
2952+
let opt_end = if self.is_at_start_of_range_notation_rhs() {
2953+
let end = self.parse_binops();
29462954
Some(end)
29472955
} else {
29482956
None
@@ -2960,6 +2968,18 @@ impl<'a> Parser<'a> {
29602968
}
29612969
}
29622970

2971+
fn is_at_start_of_range_notation_rhs(&self) -> bool {
2972+
if self.token.can_begin_expr() {
2973+
// parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.
2974+
if self.token == token::OpenDelim(token::Brace) {
2975+
return !self.restrictions.contains(RESTRICTION_NO_STRUCT_LITERAL);
2976+
}
2977+
true
2978+
} else {
2979+
false
2980+
}
2981+
}
2982+
29632983
/// Parse an 'if' or 'if let' expression ('if' token already eaten)
29642984
pub fn parse_if_expr(&mut self) -> P<Expr> {
29652985
if self.token.is_keyword(keywords::Let) {

src/test/compile-fail/range-3.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test range syntax - syntax errors.
12+
13+
pub fn main() {
14+
let r = 1..2..3;
15+
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
16+
}

src/test/compile-fail/range-4.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test range syntax - syntax errors.
12+
13+
pub fn main() {
14+
let r = ..1..2;
15+
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
16+
}

src/test/run-pass/ranges-precedence.rs

+7
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,12 @@ fn main() {
4848
assert!(x == &a[3..]);
4949

5050
for _i in 2+4..10-3 {}
51+
52+
let i = 42;
53+
for _ in 1..i {}
54+
for _ in 1.. { break; }
55+
56+
let x = [1]..[2];
57+
assert!(x == (([1])..([2])));
5158
}
5259

0 commit comments

Comments
 (0)