about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-01-07 17:19:55 -0800
committerAlex Crichton <alex@alexcrichton.com>2015-01-07 17:19:55 -0800
commitf6a7dc5528a9a9ac36867bbca2a6044b7be5bce2 (patch)
tree859fba6e5162e3f46fe8bf62900d1674f15683be /src
parentcb344be99d6b491017558ffbd015b9a0b86e8558 (diff)
parent1cc69c484eadec5e54f178a9fcfaff21b5d25cf4 (diff)
downloadrust-f6a7dc5528a9a9ac36867bbca2a6044b7be5bce2.tar.gz
rust-f6a7dc5528a9a9ac36867bbca2a6044b7be5bce2.zip
rollup merge of #20726: dgrunwald/require-parens-for-chained-comparison
[Rendered RFC](https://github.com/rust-lang/rfcs/blob/master/text/0558-require-parentheses-for-chained-comparisons.md)
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax/ast_util.rs10
-rw-r--r--src/libsyntax/parse/parser.rs24
-rw-r--r--src/test/compile-fail/require-parens-for-chained-comparison.rs23
-rw-r--r--src/test/compile-fail/unsized2.rs5
4 files changed, 58 insertions, 4 deletions
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 871f1237aee..836deeffde1 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -85,6 +85,13 @@ pub fn is_shift_binop(b: BinOp) -> bool {
     }
 }
 
+pub fn is_comparison_binop(b: BinOp) -> bool {
+    match b {
+        BiEq | BiLt | BiLe | BiNe | BiGt | BiGe => true,
+        _ => false
+    }
+}
+
 /// Returns `true` if the binary operator takes its arguments by value
 pub fn is_by_value_binop(b: BinOp) -> bool {
     match b {
@@ -317,8 +324,7 @@ pub fn operator_prec(op: ast::BinOp) -> uint {
       BiBitAnd                  =>  8u,
       BiBitXor                  =>  7u,
       BiBitOr                   =>  6u,
-      BiLt | BiLe | BiGe | BiGt =>  4u,
-      BiEq | BiNe               =>  3u,
+      BiLt | BiLe | BiGe | BiGt | BiEq | BiNe => 3u,
       BiAnd                     =>  2u,
       BiOr                      =>  1u
   }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 9c16dbb2c5c..3bb9e6692a0 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -16,7 +16,7 @@ use ast::{AssociatedType, BareFnTy};
 use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
 use ast::{ProvidedMethod, Public, Unsafety};
 use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
-use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, Block};
+use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block};
 use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
 use ast::{Crate, CrateConfig, Decl, DeclItem};
 use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf};
@@ -2906,6 +2906,9 @@ impl<'a> Parser<'a> {
         let cur_opt = self.token.to_binop();
         match cur_opt {
             Some(cur_op) => {
+                if ast_util::is_comparison_binop(cur_op) {
+                    self.check_no_chained_comparison(&*lhs, cur_op)
+                }
                 let cur_prec = operator_prec(cur_op);
                 if cur_prec > min_prec {
                     self.bump();
@@ -2934,6 +2937,25 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// Produce an error if comparison operators are chained (RFC #558).
+    /// We only need to check lhs, not rhs, because all comparison ops
+    /// have same precedence and are left-associative
+    fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: ast::BinOp) {
+        debug_assert!(ast_util::is_comparison_binop(outer_op));
+        match lhs.node {
+            ExprBinary(op, _, _) if ast_util::is_comparison_binop(op) => {
+                let op_span = self.span;
+                self.span_err(op_span,
+                    "Chained comparison operators require parentheses");
+                if op == BiLt && outer_op == BiGt {
+                    self.span_help(op_span,
+                        "Use ::< instead of < if you meant to specify type arguments.");
+                }
+            }
+            _ => {}
+        }
+    }
+
     /// Parse an assignment expression....
     /// actually, this seems to be the main entry point for
     /// parsing an arbitrary expression.
diff --git a/src/test/compile-fail/require-parens-for-chained-comparison.rs b/src/test/compile-fail/require-parens-for-chained-comparison.rs
new file mode 100644
index 00000000000..7513815ad73
--- /dev/null
+++ b/src/test/compile-fail/require-parens-for-chained-comparison.rs
@@ -0,0 +1,23 @@
+// Copyright 2015 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.
+
+fn f<T>() {}
+
+fn main() {
+    false == false == false;
+    //~^ ERROR: Chained comparison operators require parentheses
+
+    false == 0 < 2;
+    //~^ ERROR: Chained comparison operators require parentheses
+
+    f<X>();
+    //~^ ERROR: Chained comparison operators require parentheses
+    //~^^ HELP: Use ::< instead of < if you meant to specify type arguments.
+}
diff --git a/src/test/compile-fail/unsized2.rs b/src/test/compile-fail/unsized2.rs
index c5f9e8d5991..604f7ba3255 100644
--- a/src/test/compile-fail/unsized2.rs
+++ b/src/test/compile-fail/unsized2.rs
@@ -13,5 +13,8 @@
 fn f<X>() {}
 
 pub fn main() {
-    f<type>(); //~ ERROR expected identifier, found keyword `type`
+    f<type>();
+    //~^ ERROR expected identifier, found keyword `type`
+    //~^^ ERROR: Chained comparison operators require parentheses
+    //~^^^ HELP: Use ::< instead of < if you meant to specify type arguments.
 }