Skip to content

Commit 1cc69c4

Browse files
committed
RFC 558: Require parentheses for chained comparisons
Fixes #20724.
1 parent 9e4e524 commit 1cc69c4

File tree

4 files changed

+58
-4
lines changed

4 files changed

+58
-4
lines changed

src/libsyntax/ast_util.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ pub fn is_shift_binop(b: BinOp) -> bool {
8585
}
8686
}
8787

88+
pub fn is_comparison_binop(b: BinOp) -> bool {
89+
match b {
90+
BiEq | BiLt | BiLe | BiNe | BiGt | BiGe => true,
91+
_ => false
92+
}
93+
}
94+
8895
/// Returns `true` if the binary operator takes its arguments by value
8996
pub fn is_by_value_binop(b: BinOp) -> bool {
9097
match b {
@@ -317,8 +324,7 @@ pub fn operator_prec(op: ast::BinOp) -> uint {
317324
BiBitAnd => 8u,
318325
BiBitXor => 7u,
319326
BiBitOr => 6u,
320-
BiLt | BiLe | BiGe | BiGt => 4u,
321-
BiEq | BiNe => 3u,
327+
BiLt | BiLe | BiGe | BiGt | BiEq | BiNe => 3u,
322328
BiAnd => 2u,
323329
BiOr => 1u
324330
}

src/libsyntax/parse/parser.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use ast::{AssociatedType, BareFnTy};
1616
use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
1717
use ast::{ProvidedMethod, Public, Unsafety};
1818
use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
19-
use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, Block};
19+
use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block};
2020
use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
2121
use ast::{Crate, CrateConfig, Decl, DeclItem};
2222
use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf};
@@ -2906,6 +2906,9 @@ impl<'a> Parser<'a> {
29062906
let cur_opt = self.token.to_binop();
29072907
match cur_opt {
29082908
Some(cur_op) => {
2909+
if ast_util::is_comparison_binop(cur_op) {
2910+
self.check_no_chained_comparison(&*lhs, cur_op)
2911+
}
29092912
let cur_prec = operator_prec(cur_op);
29102913
if cur_prec > min_prec {
29112914
self.bump();
@@ -2934,6 +2937,25 @@ impl<'a> Parser<'a> {
29342937
}
29352938
}
29362939

2940+
/// Produce an error if comparison operators are chained (RFC #558).
2941+
/// We only need to check lhs, not rhs, because all comparison ops
2942+
/// have same precedence and are left-associative
2943+
fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: ast::BinOp) {
2944+
debug_assert!(ast_util::is_comparison_binop(outer_op));
2945+
match lhs.node {
2946+
ExprBinary(op, _, _) if ast_util::is_comparison_binop(op) => {
2947+
let op_span = self.span;
2948+
self.span_err(op_span,
2949+
"Chained comparison operators require parentheses");
2950+
if op == BiLt && outer_op == BiGt {
2951+
self.span_help(op_span,
2952+
"Use ::< instead of < if you meant to specify type arguments.");
2953+
}
2954+
}
2955+
_ => {}
2956+
}
2957+
}
2958+
29372959
/// Parse an assignment expression....
29382960
/// actually, this seems to be the main entry point for
29392961
/// parsing an arbitrary expression.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2015 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+
fn f<T>() {}
12+
13+
fn main() {
14+
false == false == false;
15+
//~^ ERROR: Chained comparison operators require parentheses
16+
17+
false == 0 < 2;
18+
//~^ ERROR: Chained comparison operators require parentheses
19+
20+
f<X>();
21+
//~^ ERROR: Chained comparison operators require parentheses
22+
//~^^ HELP: Use ::< instead of < if you meant to specify type arguments.
23+
}

src/test/compile-fail/unsized2.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@
1313
fn f<X>() {}
1414

1515
pub fn main() {
16-
f<type>(); //~ ERROR expected identifier, found keyword `type`
16+
f<type>();
17+
//~^ ERROR expected identifier, found keyword `type`
18+
//~^^ ERROR: Chained comparison operators require parentheses
19+
//~^^^ HELP: Use ::< instead of < if you meant to specify type arguments.
1720
}

0 commit comments

Comments
 (0)