about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSimonas Kazlauskas <git@kazlauskas.me>2015-10-15 15:51:30 +0300
committerSimonas Kazlauskas <git@kazlauskas.me>2015-10-27 21:55:04 +0200
commit471f5a1f9a8bf24eff35183031ad38b9e11eeb0a (patch)
tree01d9a1c7f23e05e0181ad624ab012d0f63143c22
parent540fd3aa715c8af7efb4b06b7f9ab6da398deb62 (diff)
downloadrust-471f5a1f9a8bf24eff35183031ad38b9e11eeb0a.tar.gz
rust-471f5a1f9a8bf24eff35183031ad38b9e11eeb0a.zip
Generalise associative operator parsing
This commit generalises parsing of associative operators from left-associative
only (with some ugly hacks to support right-associative assignment) to properly
left/right-associative operators.

Parsing still is not general enough to handle non-associative,
non-highest-precedence prefix or non-highest-precedence postfix operators (e.g.
`..` range syntax), though. That should be fixed in the future.

Lastly, this commit adds support for parsing right-associative `<-` (left arrow)
operator with precedence higher than assignment as the operator for placement-in
feature.
-rw-r--r--src/libsyntax/ast_util.rs20
-rw-r--r--src/libsyntax/lib.rs1
-rw-r--r--src/libsyntax/parse/parser.rs263
-rw-r--r--src/libsyntax/print/pprust.rs14
-rw-r--r--src/libsyntax/util/parser.rs191
-rw-r--r--src/test/compile-fail/feature-gate-placement-expr.rs2
-rw-r--r--src/test/compile-fail/issue-14084.rs2
-rw-r--r--src/test/parse-fail/removed-syntax-larrow-init.rs17
-rw-r--r--src/test/parse-fail/removed-syntax-larrow-move.rs18
9 files changed, 334 insertions, 194 deletions
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 8c3360512d5..f1c88232fc4 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -242,26 +242,6 @@ pub fn struct_field_visibility(field: ast::StructField) -> Visibility {
     }
 }
 
-/// Maps a binary operator to its precedence
-pub fn operator_prec(op: ast::BinOp_) -> usize {
-  match op {
-      // 'as' sits here with 12
-      BiMul | BiDiv | BiRem     => 11,
-      BiAdd | BiSub             => 10,
-      BiShl | BiShr             =>  9,
-      BiBitAnd                  =>  8,
-      BiBitXor                  =>  7,
-      BiBitOr                   =>  6,
-      BiLt | BiLe | BiGe | BiGt | BiEq | BiNe => 3,
-      BiAnd                     =>  2,
-      BiOr                      =>  1
-  }
-}
-
-/// Precedence of the `as` operator, which is a binary operator
-/// not appearing in the prior table.
-pub const AS_PREC: usize = 12;
-
 pub fn empty_generics() -> Generics {
     Generics {
         lifetimes: Vec::new(),
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 9adef08771d..406b763ca46 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -66,6 +66,7 @@ pub mod util {
     #[cfg(test)]
     pub mod parser_testing;
     pub mod small_vector;
+    pub mod parser;
 }
 
 pub mod diagnostics {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index e153f1665e3..f4e14a6ee36 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -15,7 +15,7 @@ use ast::BareFnTy;
 use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
 use ast::{Public, Unsafety};
 use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
-use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block};
+use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, Block};
 use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
 use ast::{Constness, ConstImplItem, ConstTraitItem, Crate, CrateConfig};
 use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn};
@@ -60,7 +60,7 @@ use ast::{UnnamedField, UnsafeBlock};
 use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
 use ast::{Visibility, WhereClause};
 use ast;
-use ast_util::{self, AS_PREC, ident_to_path, operator_prec};
+use ast_util::{self, ident_to_path};
 use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap};
 use diagnostic;
 use ext::tt::macro_parser;
@@ -73,6 +73,7 @@ use parse::obsolete::{ParserObsoleteMethods, ObsoleteSyntax};
 use parse::token::{self, MatchNt, SubstNt, SpecialVarNt, InternedString};
 use parse::token::{keywords, special_idents, SpecialMacroVar};
 use parse::{new_sub_parser_from_file, ParseSess};
+use util::parser::{AssocOp, Fixity};
 use print::pprust;
 use ptr::P;
 use owned_slice::OwnedSlice;
@@ -2597,7 +2598,7 @@ impl<'a> Parser<'a> {
         Ok(tts)
     }
 
-    /// Parse a prefix-operator expr
+    /// Parse a prefix-unary-operator expr
     pub fn parse_prefix_expr(&mut self) -> PResult<P<Expr>> {
         let lo = self.span.lo;
         let hi;
@@ -2634,8 +2635,10 @@ impl<'a> Parser<'a> {
               try!(self.bump());
               let place = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
               let blk = try!(self.parse_block());
-              hi = blk.span.hi;
-              let blk_expr = self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk));
+              let span = blk.span;
+              hi = span.hi;
+              let blk_expr = self.mk_expr(span.lo, span.hi, ExprBlock(blk));
+              self.span_warn(span, "in PLACE BLOCK syntax is deprecated and will be removed soon");
               ex = ExprInPlace(place, blk_expr);
           }
           token::Ident(..) if self.token.is_keyword(keywords::Box) => {
@@ -2649,65 +2652,146 @@ impl<'a> Parser<'a> {
         return Ok(self.mk_expr(lo, hi, ex));
     }
 
-    /// Parse an expression of binops
-    pub fn parse_binops(&mut self) -> PResult<P<Expr>> {
-        let prefix_expr = try!(self.parse_prefix_expr());
-        self.parse_more_binops(prefix_expr, 0)
+    /// Parse an associative expression
+    ///
+    /// This parses an expression accounting for associativity and precedence of the operators in
+    /// the expression.
+    pub fn parse_assoc_expr(&mut self) -> PResult<P<Expr>> {
+        if self.token == token::DotDot {
+            // prefix-form of range notation `..expr` and `..`
+            // This has the precedence just higher than assignment expressions (much lower than
+            // other prefix expressions) to be consistent with the postfix-form `expr..`
+            // If it isn’t clear yet, this is a hack of the worst kind (one that also probably
+            // can’t be fixed anymore because stability guarantees).
+            let lo = self.span.lo;
+            let mut hi = self.span.hi;
+            try!(self.bump());
+            let opt_end = if self.is_at_start_of_range_notation_rhs() {
+                // RHS must be parsed with more associativity than DotDot.
+                let next_prec = AssocOp::from_token(&token::DotDot).unwrap().precedence() + 1;
+                let end = try!(self.parse_assoc_expr_with(next_prec, None));
+                hi = end.span.hi;
+                Some(end)
+            } else {
+                None
+            };
+            let r = self.mk_range(None, opt_end);
+            Ok(self.mk_expr(lo, hi, r))
+        } else {
+            self.parse_assoc_expr_with(0, None)
+        }
     }
 
-    /// Parse an expression of binops of at least min_prec precedence
-    pub fn parse_more_binops(&mut self, lhs: P<Expr>, min_prec: usize) -> PResult<P<Expr>> {
-        if self.expr_is_complete(&*lhs) { return Ok(lhs); }
-
+    /// Parse an associative expression with operators of at least `min_prec` precedence
+    pub fn parse_assoc_expr_with(&mut self, min_prec: usize, lhs: Option<P<Expr>>) -> PResult<P<Expr>> {
+        let mut lhs = if lhs.is_some() {
+            lhs.unwrap()
+        } else {
+            try!(self.parse_prefix_expr())
+        };
+        if self.expr_is_complete(&*lhs) && min_prec == 0 {
+            // Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
+            return Ok(lhs);
+        }
+        let cur_op_span = self.span;
         self.expected_tokens.push(TokenType::Operator);
+        while let Some(op) = AssocOp::from_token(&self.token) {
+            if op.precedence() < min_prec {
+                break;
+            }
+            try!(self.bump());
+            if op.is_comparison() {
+                self.check_no_chained_comparison(&*lhs, &op);
+            }
+            // Special cases:
+            if op == AssocOp::As {
+                let rhs = try!(self.parse_ty_nopanic());
+                lhs = self.mk_expr(lhs.span.lo, rhs.span.hi, ExprCast(lhs, rhs));
+                continue
+            } else if op == AssocOp::DotDot {
+                    // If we didn’t have to handle `x..`, it would be pretty easy to generalise
+                    // here by simply doing something along the lines of
+                    //
+                    //     break_from_this_loop_after_setting_lhs = true;
+                    //     rhs = self.parse_assoc_expr_with(op.precedence() + 1, None);
+                    //
+                    // We have 2 alternatives here: `x..y` and `x..` The other two variants are
+                    // handled in `parse_assoc_expr`
+                    let rhs = if self.is_at_start_of_range_notation_rhs() {
+                        self.parse_assoc_expr_with(op.precedence() + 1, None).ok()
+                    } else {
+                        None
+                    };
+                    let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs {
+                        x.span
+                    } else {
+                        cur_op_span
+                    });
+                    let r = self.mk_range(Some(lhs), rhs);
+                    lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r);
+                    break
+            }
 
-        let cur_op_span = self.span;
-        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 {
-                    try!(self.bump());
-                    let expr = try!(self.parse_prefix_expr());
-                    let rhs = try!(self.parse_more_binops(expr, cur_prec + 1));
-                    let lhs_span = lhs.span;
-                    let rhs_span = rhs.span;
-                    let binary = self.mk_binary(codemap::respan(cur_op_span, cur_op), lhs, rhs);
-                    let bin = self.mk_expr(lhs_span.lo, rhs_span.hi, binary);
-                    self.parse_more_binops(bin, min_prec)
-                } else {
-                    Ok(lhs)
+            let rhs = try!(match op.fixity() {
+                Fixity::Right => self.parse_assoc_expr_with(op.precedence(), None),
+                Fixity::Left => self.parse_assoc_expr_with(op.precedence() + 1, None),
+                // We currently have no non-associative operators that are not handled above by
+                // the special cases. The code is here only for future convenience.
+                Fixity::None => self.parse_assoc_expr_with(op.precedence() + 1, None),
+            });
+
+            lhs = match op {
+                AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide |
+                AssocOp::Modulus | AssocOp::LAnd | AssocOp::LOr | AssocOp::BitXor |
+                AssocOp::BitAnd | AssocOp::BitOr | AssocOp::ShiftLeft | AssocOp::ShiftRight |
+                AssocOp::Equal | AssocOp::Less | AssocOp::LessEqual | AssocOp::NotEqual |
+                AssocOp::Greater | AssocOp::GreaterEqual => {
+                    let ast_op = op.to_ast_binop().unwrap();
+                    let (lhs_span, rhs_span) = (lhs.span, rhs.span);
+                    let binary = self.mk_binary(codemap::respan(cur_op_span, ast_op), lhs, rhs);
+                    self.mk_expr(lhs_span.lo, rhs_span.hi, binary)
                 }
-            }
-            None => {
-                if AS_PREC >= min_prec && try!(self.eat_keyword_noexpect(keywords::As) ){
-                    let rhs = try!(self.parse_ty_nopanic());
-                    let _as = self.mk_expr(lhs.span.lo,
-                                           rhs.span.hi,
-                                           ExprCast(lhs, rhs));
-                    self.parse_more_binops(_as, min_prec)
-                } else {
-                    Ok(lhs)
+                AssocOp::Assign =>
+                    self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs)),
+                AssocOp::Inplace =>
+                    self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs)),
+                AssocOp::AssignOp(k) => {
+                    let aop = match k {
+                        token::Plus =>    BiAdd,
+                        token::Minus =>   BiSub,
+                        token::Star =>    BiMul,
+                        token::Slash =>   BiDiv,
+                        token::Percent => BiRem,
+                        token::Caret =>   BiBitXor,
+                        token::And =>     BiBitAnd,
+                        token::Or =>      BiBitOr,
+                        token::Shl =>     BiShl,
+                        token::Shr =>     BiShr
+                    };
+                    let (lhs_span, rhs_span) = (lhs.span, rhs.span);
+                    let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
+                    self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr)
                 }
-            }
+                AssocOp::As | AssocOp::DotDot => self.bug("As or DotDot branch reached")
+            };
+
+            if op.fixity() == Fixity::None { break }
         }
+        Ok(lhs)
     }
 
     /// 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));
+    fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: &AssocOp) {
+        debug_assert!(outer_op.is_comparison());
         match lhs.node {
             ExprBinary(op, _, _) if ast_util::is_comparison_binop(op.node) => {
                 // respan to include both operators
                 let op_span = mk_sp(op.span.lo, self.span.hi);
                 self.span_err(op_span,
                     "chained comparison operators require parentheses");
-                if op.node == BiLt && outer_op == BiGt {
+                if op.node == BiLt && *outer_op == AssocOp::Greater {
                     self.fileline_help(op_span,
                         "use `::<...>` instead of `<...>` if you meant to specify type arguments");
                 }
@@ -2716,88 +2800,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse an assignment expression....
-    /// actually, this seems to be the main entry point for
-    /// parsing an arbitrary expression.
-    pub fn parse_assign_expr(&mut self) -> PResult<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;
-            let mut hi = self.span.hi;
-            try!(self.bump());
-            let opt_end = if self.is_at_start_of_range_notation_rhs() {
-                let end = try!(self.parse_binops());
-                hi = end.span.hi;
-                Some(end)
-            } else {
-                None
-            };
-            let ex = self.mk_range(None, opt_end);
-            Ok(self.mk_expr(lo, hi, ex))
-          }
-          _ => {
-            let lhs = try!(self.parse_binops());
-            self.parse_assign_expr_with(lhs)
-          }
-        }
-    }
-
-    pub fn parse_assign_expr_with(&mut self, lhs: P<Expr>) -> PResult<P<Expr>> {
-        let restrictions = self.restrictions & Restrictions::RESTRICTION_NO_STRUCT_LITERAL;
-        let op_span = self.span;
-        match self.token {
-          token::Eq => {
-              try!(self.bump());
-              let rhs = try!(self.parse_expr_res(restrictions));
-              Ok(self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs)))
-          }
-          token::BinOpEq(op) => {
-              try!(self.bump());
-              let rhs = try!(self.parse_expr_res(restrictions));
-              let aop = match op {
-                  token::Plus =>    BiAdd,
-                  token::Minus =>   BiSub,
-                  token::Star =>    BiMul,
-                  token::Slash =>   BiDiv,
-                  token::Percent => BiRem,
-                  token::Caret =>   BiBitXor,
-                  token::And =>     BiBitAnd,
-                  token::Or =>      BiBitOr,
-                  token::Shl =>     BiShl,
-                  token::Shr =>     BiShr
-              };
-              let rhs_span = rhs.span;
-              let span = lhs.span;
-              let assign_op = self.mk_assign_op(codemap::respan(op_span, aop), lhs, rhs);
-              Ok(self.mk_expr(span.lo, rhs_span.hi, assign_op))
-          }
-          // A range expression, either `expr..expr` or `expr..`.
-          token::DotDot => {
-            let lo = lhs.span.lo;
-            let mut hi = self.span.hi;
-            try!(self.bump());
-
-            let opt_end = if self.is_at_start_of_range_notation_rhs() {
-                let end = try!(self.parse_binops());
-                hi = end.span.hi;
-                Some(end)
-            } else {
-                None
-            };
-            let range = self.mk_range(Some(lhs), opt_end);
-            return Ok(self.mk_expr(lo, hi, range));
-          }
-
-          _ => {
-              Ok(lhs)
-          }
-        }
-    }
-
     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..{})`.
@@ -2982,7 +2984,7 @@ impl<'a> Parser<'a> {
     pub fn parse_expr_res(&mut self, r: Restrictions) -> PResult<P<Expr>> {
         let old = self.restrictions;
         self.restrictions = r;
-        let e = try!(self.parse_assign_expr());
+        let e = try!(self.parse_assoc_expr());
         self.restrictions = old;
         return Ok(e);
     }
@@ -3624,8 +3626,7 @@ impl<'a> Parser<'a> {
                             let e = self.mk_mac_expr(span.lo, span.hi,
                                                      mac.and_then(|m| m.node));
                             let e = try!(self.parse_dot_or_call_expr_with(e));
-                            let e = try!(self.parse_more_binops(e, 0));
-                            let e = try!(self.parse_assign_expr_with(e));
+                            let e = try!(self.parse_assoc_expr_with(0, Some(e)));
                             try!(self.handle_expression_like_statement(
                                 e,
                                 span,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 87cecdba28e..0bdbf132cb8 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -14,6 +14,7 @@ use abi;
 use ast;
 use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
 use ast_util;
+use util::parser::AssocOp;
 use attr;
 use owned_slice::OwnedSlice;
 use attr::{AttrMetaMethods, AttributeMethods};
@@ -445,7 +446,8 @@ fn needs_parentheses(expr: &ast::Expr) -> bool {
     match expr.node {
         ast::ExprAssign(..) | ast::ExprBinary(..) |
         ast::ExprClosure(..) |
-        ast::ExprAssignOp(..) | ast::ExprCast(..) => true,
+        ast::ExprAssignOp(..) | ast::ExprCast(..) |
+        ast::ExprInPlace(..) => true,
         _ => false,
     }
 }
@@ -1776,8 +1778,8 @@ impl<'a> State<'a> {
                                       binop: ast::BinOp) -> bool {
         match sub_expr.node {
             ast::ExprBinary(ref sub_op, _, _) => {
-                if ast_util::operator_prec(sub_op.node) <
-                    ast_util::operator_prec(binop.node) {
+                if AssocOp::from_ast_binop(sub_op.node).precedence() <
+                    AssocOp::from_ast_binop(binop.node).precedence() {
                     true
                 } else {
                     false
@@ -1802,10 +1804,10 @@ impl<'a> State<'a> {
     fn print_expr_in_place(&mut self,
                            place: &ast::Expr,
                            expr: &ast::Expr) -> io::Result<()> {
-        try!(self.word_space("in"));
-        try!(self.print_expr(place));
+        try!(self.print_expr_maybe_paren(place));
         try!(space(&mut self.s));
-        self.print_expr(expr)
+        try!(self.word_space("<-"));
+        self.print_expr_maybe_paren(expr)
     }
 
     fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) -> io::Result<()> {
diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs
new file mode 100644
index 00000000000..c12cd8e89db
--- /dev/null
+++ b/src/libsyntax/util/parser.rs
@@ -0,0 +1,191 @@
+use parse::token::{Token, BinOpToken, keywords};
+use ast;
+
+/// Associative operator with precedence.
+///
+/// This is the enum which specifies operator precedence and fixity to the parser.
+#[derive(Debug, PartialEq, Eq)]
+pub enum AssocOp {
+    /// `+`
+    Add,
+    /// `-`
+    Subtract,
+    /// `*`
+    Multiply,
+    /// `/`
+    Divide,
+    /// `%`
+    Modulus,
+    /// `&&`
+    LAnd,
+    /// `||`
+    LOr,
+    /// `^`
+    BitXor,
+    /// `&`
+    BitAnd,
+    /// `|`
+    BitOr,
+    /// `<<`
+    ShiftLeft,
+    /// `>>`
+    ShiftRight,
+    /// `==`
+    Equal,
+    /// `<`
+    Less,
+    /// `<=`
+    LessEqual,
+    /// `!=`
+    NotEqual,
+    /// `>`
+    Greater,
+    /// `>=`
+    GreaterEqual,
+    /// `=`
+    Assign,
+    /// `<-`
+    Inplace,
+    /// `?=` where ? is one of the BinOpToken
+    AssignOp(BinOpToken),
+    /// `as`
+    As,
+    /// `..` range
+    DotDot
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum Fixity {
+    /// The operator is left-associative
+    Left,
+    /// The operator is right-associative
+    Right,
+    /// The operator is not associative
+    None
+}
+
+impl AssocOp {
+    /// Create a new AssocOP from a token
+    pub fn from_token(t: &Token) -> Option<AssocOp> {
+        use self::AssocOp::*;
+        match *t {
+            Token::BinOpEq(k) => Some(AssignOp(k)),
+            Token::LArrow => Some(Inplace),
+            Token::Eq => Some(Assign),
+            Token::BinOp(BinOpToken::Star) => Some(Multiply),
+            Token::BinOp(BinOpToken::Slash) => Some(Divide),
+            Token::BinOp(BinOpToken::Percent) => Some(Modulus),
+            Token::BinOp(BinOpToken::Plus) => Some(Add),
+            Token::BinOp(BinOpToken::Minus) => Some(Subtract),
+            Token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
+            Token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
+            Token::BinOp(BinOpToken::And) => Some(BitAnd),
+            Token::BinOp(BinOpToken::Caret) => Some(BitXor),
+            Token::BinOp(BinOpToken::Or) => Some(BitOr),
+            Token::Lt => Some(Less),
+            Token::Le => Some(LessEqual),
+            Token::Ge => Some(GreaterEqual),
+            Token::Gt => Some(Greater),
+            Token::EqEq => Some(Equal),
+            Token::Ne => Some(NotEqual),
+            Token::AndAnd => Some(LAnd),
+            Token::OrOr => Some(LOr),
+            Token::DotDot => Some(DotDot),
+            _ if t.is_keyword(keywords::As) => Some(As),
+            _ => None
+        }
+    }
+
+    /// Create a new AssocOp from ast::BinOp_.
+    pub fn from_ast_binop(op: ast::BinOp_) -> Self {
+        use self::AssocOp::*;
+        match op {
+            ast::BiLt => Less,
+            ast::BiGt => Greater,
+            ast::BiLe => LessEqual,
+            ast::BiGe => GreaterEqual,
+            ast::BiEq => Equal,
+            ast::BiNe => NotEqual,
+            ast::BiMul => Multiply,
+            ast::BiDiv => Divide,
+            ast::BiRem => Modulus,
+            ast::BiAdd => Add,
+            ast::BiSub => Subtract,
+            ast::BiShl => ShiftLeft,
+            ast::BiShr => ShiftRight,
+            ast::BiBitAnd => BitAnd,
+            ast::BiBitXor => BitXor,
+            ast::BiBitOr => BitOr,
+            ast::BiAnd => LAnd,
+            ast::BiOr => LOr
+        }
+    }
+
+    /// Gets the precedence of this operator
+    pub fn precedence(&self) -> usize {
+        use self::AssocOp::*;
+        match *self {
+            As => 14,
+            Multiply | Divide | Modulus => 13,
+            Add | Subtract => 12,
+            ShiftLeft | ShiftRight => 11,
+            BitAnd => 10,
+            BitXor => 9,
+            BitOr => 8,
+            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
+            LAnd => 6,
+            LOr => 5,
+            DotDot => 4,
+            Inplace => 3,
+            Assign | AssignOp(_) => 2,
+        }
+    }
+
+    /// Gets the fixity of this operator
+    pub fn fixity(&self) -> Fixity {
+        use self::AssocOp::*;
+        // NOTE: it is a bug to have an operators that has same precedence but different fixities!
+        match *self {
+            Inplace | Assign | AssignOp(_) => Fixity::Right,
+            As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd |
+            BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual |
+            LAnd | LOr => Fixity::Left,
+            DotDot => Fixity::None
+        }
+    }
+
+    pub fn is_comparison(&self) -> bool {
+        use self::AssocOp::*;
+        match *self {
+            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
+            Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract |
+            ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot => false
+        }
+    }
+
+    pub fn to_ast_binop(&self) -> Option<ast::BinOp_> {
+        use self::AssocOp::*;
+        match *self {
+            Less => Some(ast::BiLt),
+            Greater => Some(ast::BiGt),
+            LessEqual => Some(ast::BiLe),
+            GreaterEqual => Some(ast::BiGe),
+            Equal => Some(ast::BiEq),
+            NotEqual => Some(ast::BiNe),
+            Multiply => Some(ast::BiMul),
+            Divide => Some(ast::BiDiv),
+            Modulus => Some(ast::BiRem),
+            Add => Some(ast::BiAdd),
+            Subtract => Some(ast::BiSub),
+            ShiftLeft => Some(ast::BiShl),
+            ShiftRight => Some(ast::BiShr),
+            BitAnd => Some(ast::BiBitAnd),
+            BitXor => Some(ast::BiBitXor),
+            BitOr => Some(ast::BiBitOr),
+            LAnd => Some(ast::BiAnd),
+            LOr => Some(ast::BiOr),
+            Inplace | Assign | AssignOp(_) | As | DotDot => None
+        }
+    }
+
+}
diff --git a/src/test/compile-fail/feature-gate-placement-expr.rs b/src/test/compile-fail/feature-gate-placement-expr.rs
index 47a25bf637c..080364c0855 100644
--- a/src/test/compile-fail/feature-gate-placement-expr.rs
+++ b/src/test/compile-fail/feature-gate-placement-expr.rs
@@ -19,6 +19,6 @@
 fn main() {
     use std::boxed::HEAP;
 
-    let x = in HEAP { 'c' }; //~ ERROR placement-in expression syntax is experimental
+    let x = HEAP <- 'c'; //~ ERROR placement-in expression syntax is experimental
     println!("x: {}", x);
 }
diff --git a/src/test/compile-fail/issue-14084.rs b/src/test/compile-fail/issue-14084.rs
index 6b19cb0b68f..8cbec549dda 100644
--- a/src/test/compile-fail/issue-14084.rs
+++ b/src/test/compile-fail/issue-14084.rs
@@ -12,6 +12,6 @@
 #![feature(placement_in_syntax)]
 
 fn main() {
-    in () { 0 };
+    () <- 0;
     //~^ ERROR: the trait `core::ops::Placer<_>` is not implemented
 }
diff --git a/src/test/parse-fail/removed-syntax-larrow-init.rs b/src/test/parse-fail/removed-syntax-larrow-init.rs
deleted file mode 100644
index 1388f8745df..00000000000
--- a/src/test/parse-fail/removed-syntax-larrow-init.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2013 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.
-
-// compile-flags: -Z parse-only
-
-fn removed_moves() {
-    let mut x = 0;
-    let y <- x;
-    //~^ ERROR expected one of `:`, `;`, `=`, or `@`, found `<-`
-}
diff --git a/src/test/parse-fail/removed-syntax-larrow-move.rs b/src/test/parse-fail/removed-syntax-larrow-move.rs
deleted file mode 100644
index 0736e483a49..00000000000
--- a/src/test/parse-fail/removed-syntax-larrow-move.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2013 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.
-
-// compile-flags: -Z parse-only
-
-fn removed_moves() {
-    let mut x = 0;
-    let y = 0;
-    y <- x;
-    //~^ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `<-`
-}