diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 1695d3a8f96b..f0e6dfb8775f 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) { + 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) -> 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) @@ -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); diff --git a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.rs b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.rs new file mode 100644 index 000000000000..06ce23eda7d9 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.rs @@ -0,0 +1,50 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2015 +// compile-pass + +// Enabling `ireffutable_let_patterns` isn't necessary for what this tests, but it makes coming up +// with examples easier. +#![feature(irrefutable_let_patterns)] + +#[allow(irrefutable_let_patterns)] +fn main() { + use std::ops::Range; + + if let Range { start: _, end: _ } = true..true && false { } + //~^ WARN error in 2018 + + if let Range { start: _, end: _ } = true..true || false { } + //~^ WARN error in 2018 + + while let Range { start: _, end: _ } = true..true && false { } + //~^ WARN error in 2018 + + while let Range { start: _, end: _ } = true..true || false { } + //~^ WARN error in 2018 + + if let true = false && false { } + //~^ WARN error in 2018 + + while let true = (1 == 2) && false { } + //~^ WARN error in 2018 + + // The following cases are not an error as parenthesis are used to + // clarify intent: + + if let Range { start: _, end: _ } = true..(true || false) { } + + if let Range { start: _, end: _ } = true..(true && false) { } + + while let Range { start: _, end: _ } = true..(true || false) { } + + while let Range { start: _, end: _ } = true..(true && false) { } +} diff --git a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.stderr b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.stderr new file mode 100644 index 000000000000..a4c3be62ec4c --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2015.stderr @@ -0,0 +1,48 @@ +warning: ambigious use of `&&` + --> $DIR/syntax-ambiguity-2015.rs:22:47 + | +LL | if let Range { start: _, end: _ } = true..true && false { } + | ^^^^^^^^^^^^^ help: consider adding parenthesis: `(true && false)` + | + = note: This will be a error in Rust 2018 until the `let_chains` feature is stabilized. + +warning: ambigious use of `||` + --> $DIR/syntax-ambiguity-2015.rs:25:47 + | +LL | if let Range { start: _, end: _ } = true..true || false { } + | ^^^^^^^^^^^^^ help: consider adding parenthesis: `(true || false)` + | + = note: This will be a error in Rust 2018 until the `let_chains` feature is stabilized. + +warning: ambigious use of `&&` + --> $DIR/syntax-ambiguity-2015.rs:28:50 + | +LL | while let Range { start: _, end: _ } = true..true && false { } + | ^^^^^^^^^^^^^ help: consider adding parenthesis: `(true && false)` + | + = note: This will be a error in Rust 2018 until the `let_chains` feature is stabilized. + +warning: ambigious use of `||` + --> $DIR/syntax-ambiguity-2015.rs:31:50 + | +LL | while let Range { start: _, end: _ } = true..true || false { } + | ^^^^^^^^^^^^^ help: consider adding parenthesis: `(true || false)` + | + = note: This will be a error in Rust 2018 until the `let_chains` feature is stabilized. + +warning: ambigious use of `&&` + --> $DIR/syntax-ambiguity-2015.rs:34:19 + | +LL | if let true = false && false { } + | ^^^^^^^^^^^^^^ help: consider adding parenthesis: `(false && false)` + | + = note: This will be a error in Rust 2018 until the `let_chains` feature is stabilized. + +warning: ambigious use of `&&` + --> $DIR/syntax-ambiguity-2015.rs:37:22 + | +LL | while let true = (1 == 2) && false { } + | ^^^^^^^^^^^^^^^^^ help: consider adding parenthesis: `((1 == 2) && false)` + | + = note: This will be a error in Rust 2018 until the `let_chains` feature is stabilized. + diff --git a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.rs b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.rs new file mode 100644 index 000000000000..baa90bcf8e97 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.rs @@ -0,0 +1,49 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +// Enabling `ireffutable_let_patterns` isn't necessary for what this tests, but it makes coming up +// with examples easier. +#![feature(irrefutable_let_patterns)] + +#[allow(irrefutable_let_patterns)] +fn main() { + use std::ops::Range; + + if let Range { start: _, end: _ } = true..true && false { } + //~^ ERROR ambigious use of `&&` + + if let Range { start: _, end: _ } = true..true || false { } + //~^ ERROR ambigious use of `||` + + while let Range { start: _, end: _ } = true..true && false { } + //~^ ERROR ambigious use of `&&` + + while let Range { start: _, end: _ } = true..true || false { } + //~^ ERROR ambigious use of `||` + + if let true = false && false { } + //~^ ERROR ambigious use of `&&` + + while let true = (1 == 2) && false { } + //~^ ERROR ambigious use of `&&` + + // The following cases are not an error as parenthesis are used to + // clarify intent: + + if let Range { start: _, end: _ } = true..(true || false) { } + + if let Range { start: _, end: _ } = true..(true && false) { } + + while let Range { start: _, end: _ } = true..(true || false) { } + + while let Range { start: _, end: _ } = true..(true && false) { } +} diff --git a/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.stderr b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.stderr new file mode 100644 index 000000000000..c472a8904bf6 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/syntax-ambiguity-2018.stderr @@ -0,0 +1,50 @@ +error: ambigious use of `&&` + --> $DIR/syntax-ambiguity-2018.rs:21:47 + | +LL | if let Range { start: _, end: _ } = true..true && false { } + | ^^^^^^^^^^^^^ help: consider adding parenthesis: `(true && false)` + | + = note: This will be a error until the `let_chains` feature is stabilized. + +error: ambigious use of `||` + --> $DIR/syntax-ambiguity-2018.rs:24:47 + | +LL | if let Range { start: _, end: _ } = true..true || false { } + | ^^^^^^^^^^^^^ help: consider adding parenthesis: `(true || false)` + | + = note: This will be a error until the `let_chains` feature is stabilized. + +error: ambigious use of `&&` + --> $DIR/syntax-ambiguity-2018.rs:27:50 + | +LL | while let Range { start: _, end: _ } = true..true && false { } + | ^^^^^^^^^^^^^ help: consider adding parenthesis: `(true && false)` + | + = note: This will be a error until the `let_chains` feature is stabilized. + +error: ambigious use of `||` + --> $DIR/syntax-ambiguity-2018.rs:30:50 + | +LL | while let Range { start: _, end: _ } = true..true || false { } + | ^^^^^^^^^^^^^ help: consider adding parenthesis: `(true || false)` + | + = note: This will be a error until the `let_chains` feature is stabilized. + +error: ambigious use of `&&` + --> $DIR/syntax-ambiguity-2018.rs:33:19 + | +LL | if let true = false && false { } + | ^^^^^^^^^^^^^^ help: consider adding parenthesis: `(false && false)` + | + = note: This will be a error until the `let_chains` feature is stabilized. + +error: ambigious use of `&&` + --> $DIR/syntax-ambiguity-2018.rs:36:22 + | +LL | while let true = (1 == 2) && false { } + | ^^^^^^^^^^^^^^^^^ help: consider adding parenthesis: `((1 == 2) && false)` + | + = note: This will be a error until the `let_chains` feature is stabilized. + +error: aborting due to 6 previous errors +