From 0dfd9c30f2c61458343e0816c66f448019e826d1 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Sat, 24 Jun 2017 18:26:04 +0000 Subject: [PATCH] syntax: allow negative integer literal expression to be interpolated as pattern --- src/librustc_lint/builtin.rs | 10 ++---- src/librustc_passes/ast_validation.rs | 26 ++++++++++++++ src/libsyntax/parse/parser.rs | 4 ++- .../compile-fail/patkind-litrange-no-expr.rs | 36 +++++++++++++++++++ src/test/run-pass/macro-pat-neg-lit.rs | 35 ++++++++++++++++++ 5 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 src/test/compile-fail/patkind-litrange-no-expr.rs create mode 100644 src/test/run-pass/macro-pat-neg-lit.rs diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 9800012917c5..57843047f51a 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -684,13 +684,9 @@ fn fl_lit_check_expr(cx: &EarlyContext, expr: &ast::Expr) { // These may occur in patterns // and can maybe contain float literals ExprKind::Unary(_, ref f) => fl_lit_check_expr(cx, f), - // These may occur in patterns - // and can't contain float literals - ExprKind::Path(..) => (), - // If something unhandled is encountered, we need to expand the - // search or ignore more ExprKinds. - _ => span_bug!(expr.span, "Unhandled expression {:?} in float lit pattern lint", - expr.node), + // Other kinds of exprs can't occur in patterns so we don't have to check them + // (ast_validation will emit an error if they occur) + _ => (), } } diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 7c443a4ac752..6ad03186dc77 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -93,6 +93,17 @@ impl<'a> AstValidator<'a> { } } } + + /// matches '-' lit | lit (cf. parser::Parser::parse_pat_literal_maybe_minus) + fn check_expr_within_pat(&self, expr: &Expr) { + match expr.node { + ExprKind::Lit(..) | ExprKind::Path(..) => {} + ExprKind::Unary(UnOp::Neg, ref inner) + if match inner.node { ExprKind::Lit(_) => true, _ => false } => {} + _ => self.err_handler().span_err(expr.span, "arbitrary expressions aren't allowed \ + in patterns") + } + } } impl<'a> Visitor<'a> for AstValidator<'a> { @@ -308,6 +319,21 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } visit::walk_generics(self, g) } + + fn visit_pat(&mut self, pat: &'a Pat) { + match pat.node { + PatKind::Lit(ref expr) => { + self.check_expr_within_pat(expr); + } + PatKind::Range(ref start, ref end, _) => { + self.check_expr_within_pat(start); + self.check_expr_within_pat(end); + } + _ => {} + } + + visit::walk_pat(self, pat) + } } pub fn check_crate(session: &Session, krate: &Crate) { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 851a638e1484..5b0031b2f179 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1659,8 +1659,10 @@ impl<'a> Parser<'a> { Ok(codemap::Spanned { node: lit, span: lo.to(self.prev_span) }) } - /// matches '-' lit | lit + /// matches '-' lit | lit (cf. ast_validation::AstValidator::check_expr_within_pat) pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<'a, P> { + maybe_whole_expr!(self); + let minus_lo = self.span; let minus_present = self.eat(&token::BinOp(token::Minus)); let lo = self.span; diff --git a/src/test/compile-fail/patkind-litrange-no-expr.rs b/src/test/compile-fail/patkind-litrange-no-expr.rs new file mode 100644 index 000000000000..afb2cbb7db39 --- /dev/null +++ b/src/test/compile-fail/patkind-litrange-no-expr.rs @@ -0,0 +1,36 @@ +// Copyright 2017 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. + +macro_rules! enum_number { + ($name:ident { $($variant:ident = $value:expr, )* }) => { + enum $name { + $($variant = $value,)* + } + + fn foo(value: i32) -> Option<$name> { + match value { + $( $value => Some($name::$variant), )* // PatKind::Lit + $( $value ... 42 => Some($name::$variant), )* // PatKind::Range + _ => None + } + } + } +} + +enum_number!(Change { + Pos = 1, + Neg = -1, + Arith = 1 + 1, //~ ERROR arbitrary expressions aren't allowed in patterns + //~^ ERROR arbitrary expressions aren't allowed in patterns + //~^^ ERROR only char and numeric types are allowed in range patterns +}); + +fn main() {} + diff --git a/src/test/run-pass/macro-pat-neg-lit.rs b/src/test/run-pass/macro-pat-neg-lit.rs new file mode 100644 index 000000000000..43ac697edced --- /dev/null +++ b/src/test/run-pass/macro-pat-neg-lit.rs @@ -0,0 +1,35 @@ +// Copyright 2017 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. + +macro_rules! enum_number { + ($name:ident { $($variant:ident = $value:expr, )* }) => { + enum $name { + $($variant = $value,)* + } + + fn foo(value: i32) -> Option<$name> { + match value { + $( $value => Some($name::$variant), )* + _ => None + } + } + } +} + +enum_number!(Change { + Down = -1, + None = 0, + Up = 1, +}); + +fn main() { + if let Some(Change::Down) = foo(-1) {} else { panic!() } +} +