about summary refs log tree commit diff
path: root/src/libsyntax/parse
diff options
context:
space:
mode:
authorDavid Wood <david@davidtw.co>2018-08-31 15:59:42 +0200
committerDavid Wood <david@davidtw.co>2018-08-31 16:13:42 +0200
commit2ce56c5ebf4b63c274da678c367a4d85a13994a8 (patch)
treee9cb2588f31e1d4a79f5008ebabc35f3d5566f4b /src/libsyntax/parse
parent685fb543174f8f2cadc38ec0b2c6df635eb1c087 (diff)
downloadrust-2ce56c5ebf4b63c274da678c367a4d85a13994a8.tar.gz
rust-2ce56c5ebf4b63c274da678c367a4d85a13994a8.zip
Added warning/error for if-let-chain ambiguity.
With eRFC 2497, previously accepted ambigious syntax regarding use of
`&&` and `||` in if-let and while-let statements should now warn
or error depending on the edition.

This commit takes a naive approach to detecting ambigious use of `&&`
or `||` and will probably need fine tuned to handle all cases.
Diffstat (limited to 'src/libsyntax/parse')
-rw-r--r--src/libsyntax/parse/parser.rs53
1 files changed, 53 insertions, 0 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 1695d3a8f96..f0e6dfb8775 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -3327,6 +3327,8 @@ impl<'a> Parser<'a> {
         let pats = self.parse_pats()?;
         self.expect(&token::Eq)?;
         let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+        self.while_if_let_ambiguity(&expr);
+
         let thn = self.parse_block()?;
         let (hi, els) = if self.eat_keyword(keywords::Else) {
             let expr = self.parse_else_expr()?;
@@ -3337,6 +3339,56 @@ impl<'a> Parser<'a> {
         Ok(self.mk_expr(lo.to(hi), ExprKind::IfLet(pats, expr, thn, els), attrs))
     }
 
+    /// With eRFC 2497, we need to check whether an expression is ambigious and warn or error
+    /// depending on the edition, this function handles that.
+    fn while_if_let_ambiguity(&self, expr: &P<Expr>) {
+        if let Some((span, op_kind)) = self.while_if_let_expr_ambiguity(&expr) {
+            let message = format!("ambigious use of `{}`", op_kind.to_string());
+            let mut err = if self.span.edition() >= Edition::Edition2018 {
+                self.diagnostic().struct_span_err(span, &message)
+            } else {
+                self.diagnostic().struct_span_warn(span, &message)
+            };
+
+            let note = if self.span.edition() >= Edition::Edition2018 {
+                "This will be a error until the `let_chains` feature is stabilized."
+            } else {
+                "This will be a error in Rust 2018 until the `let_chains` feature is stabilized."
+            };
+            err.note(note);
+
+            if let Ok(snippet) = self.sess.source_map().span_to_snippet(span) {
+                err.span_suggestion(
+                    span, "consider adding parenthesis", format!("({})", snippet),
+                );
+            }
+
+            err.emit();
+        }
+    }
+
+    /// With eRFC 2497 adding if-let chains, there is a requirement that the parsing of
+    /// `&&` and `||` in a if-let statement be unambigious. This function returns a span and
+    /// a `BinOpKind` (either `&&` or `||` depending on what was ambigious) if it is determined
+    /// that the current expression parsed is ambigious and will break in future.
+    fn while_if_let_expr_ambiguity(&self, expr: &P<Expr>) -> Option<(Span, BinOpKind)> {
+        debug!("while_if_let_expr_ambiguity: expr.node: {:?}", expr.node);
+        match &expr.node {
+            ExprKind::Binary(op, _, _) if op.node == BinOpKind::And || op.node == BinOpKind::Or => {
+                Some((expr.span, op.node))
+            },
+            ExprKind::Range(ref lhs, ref rhs, _) => {
+                let lhs_ambigious = lhs.as_ref()
+                    .and_then(|lhs| self.while_if_let_expr_ambiguity(lhs));
+                let rhs_ambigious = rhs.as_ref()
+                    .and_then(|rhs| self.while_if_let_expr_ambiguity(rhs));
+
+                lhs_ambigious.or(rhs_ambigious)
+            }
+            _ => None,
+        }
+    }
+
     // `move |args| expr`
     fn parse_lambda_expr(&mut self,
                              attrs: ThinVec<Attribute>)
@@ -3437,6 +3489,7 @@ impl<'a> Parser<'a> {
         let pats = self.parse_pats()?;
         self.expect(&token::Eq)?;
         let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
+        self.while_if_let_ambiguity(&expr);
         let (iattrs, body) = self.parse_inner_attrs_and_block()?;
         attrs.extend(iattrs);
         let span = span_lo.to(body.span);