implement the ?
operator
The `?` postfix operator is sugar equivalent to the try! macro, but is more amenable to chaining: `File::open("foo")?.metadata()?.is_dir()`. `?` is accepted on any *expression* that can return a `Result`, e.g. `x()?`, `y!()?`, `{z}?`, `(w)?`, etc. And binds more tightly than unary operators, e.g. `!x?` is parsed as `!(x?)`. cc #31436
This commit is contained in:
parent
c116ae35cf
commit
210dd611aa
@ -344,6 +344,10 @@ fn check_arms(cx: &MatchCheckCtxt,
|
||||
hir::MatchSource::Normal => {
|
||||
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern")
|
||||
},
|
||||
|
||||
hir::MatchSource::TryDesugar => {
|
||||
cx.tcx.sess.span_bug(pat.span, "unreachable try pattern")
|
||||
},
|
||||
}
|
||||
}
|
||||
Useful => (),
|
||||
|
@ -835,6 +835,7 @@ pub enum MatchSource {
|
||||
},
|
||||
WhileLetDesugar,
|
||||
ForLoopDesugar,
|
||||
TryDesugar,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
|
||||
|
@ -1605,6 +1605,63 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
|
||||
});
|
||||
}
|
||||
|
||||
// Desugar ExprKind::Try
|
||||
// From: `<expr>?`
|
||||
ExprKind::Try(ref sub_expr) => {
|
||||
// to:
|
||||
//
|
||||
// {
|
||||
// match <expr> {
|
||||
// Ok(val) => val,
|
||||
// Err(err) => {
|
||||
// return Err(From::from(err))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return cache_ids(lctx, e.id, |lctx| {
|
||||
// expand <expr>
|
||||
let sub_expr = lower_expr(lctx, sub_expr);
|
||||
|
||||
// Ok(val) => val
|
||||
let ok_arm = {
|
||||
let val_ident = lctx.str_to_ident("val");
|
||||
let val_pat = pat_ident(lctx, e.span, val_ident);
|
||||
let val_expr = expr_ident(lctx, e.span, val_ident, None);
|
||||
let ok_pat = pat_ok(lctx, e.span, val_pat);
|
||||
|
||||
arm(hir_vec![ok_pat], val_expr)
|
||||
};
|
||||
|
||||
// Err(err) => return Err(From::from(err))
|
||||
let err_arm = {
|
||||
let err_ident = lctx.str_to_ident("err");
|
||||
let from_expr = {
|
||||
let path = std_path(lctx, &["convert", "From", "from"]);
|
||||
let path = path_global(e.span, path);
|
||||
let from = expr_path(lctx, path, None);
|
||||
let err_expr = expr_ident(lctx, e.span, err_ident, None);
|
||||
|
||||
expr_call(lctx, e.span, from, hir_vec![err_expr], None)
|
||||
};
|
||||
let err_expr = {
|
||||
let path = std_path(lctx, &["result", "Result", "Err"]);
|
||||
let path = path_global(e.span, path);
|
||||
let err_ctor = expr_path(lctx, path, None);
|
||||
expr_call(lctx, e.span, err_ctor, hir_vec![from_expr], None)
|
||||
};
|
||||
let err_pat = pat_err(lctx, e.span, pat_ident(lctx, e.span, err_ident));
|
||||
let ret_expr = expr(lctx, e.span,
|
||||
hir::Expr_::ExprRet(Some(err_expr)), None);
|
||||
|
||||
arm(hir_vec![err_pat], ret_expr)
|
||||
};
|
||||
|
||||
expr_match(lctx, e.span, sub_expr, hir_vec![err_arm, ok_arm],
|
||||
hir::MatchSource::TryDesugar, None)
|
||||
})
|
||||
}
|
||||
|
||||
ExprKind::Mac(_) => panic!("Shouldn't exist here"),
|
||||
},
|
||||
span: e.span,
|
||||
@ -1819,6 +1876,18 @@ fn block_all(lctx: &LoweringContext,
|
||||
})
|
||||
}
|
||||
|
||||
fn pat_ok(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
|
||||
let ok = std_path(lctx, &["result", "Result", "Ok"]);
|
||||
let path = path_global(span, ok);
|
||||
pat_enum(lctx, span, path, hir_vec![pat])
|
||||
}
|
||||
|
||||
fn pat_err(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
|
||||
let err = std_path(lctx, &["result", "Result", "Err"]);
|
||||
let path = path_global(span, err);
|
||||
pat_enum(lctx, span, path, hir_vec![pat])
|
||||
}
|
||||
|
||||
fn pat_some(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
|
||||
let some = std_path(lctx, &["option", "Option", "Some"]);
|
||||
let path = path_global(span, some);
|
||||
|
@ -1022,6 +1022,9 @@ pub enum ExprKind {
|
||||
|
||||
/// No-op: used solely so we can pretty-print faithfully
|
||||
Paren(P<Expr>),
|
||||
|
||||
/// `expr?`
|
||||
Try(P<Expr>),
|
||||
}
|
||||
|
||||
/// The explicit Self type in a "qualified path". The actual
|
||||
|
@ -245,6 +245,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
|
||||
|
||||
// a...b and ...b
|
||||
("inclusive_range_syntax", "1.7.0", Some(28237), Active),
|
||||
|
||||
// `expr?`
|
||||
("question_mark", "1.9.0", Some(31436), Active)
|
||||
];
|
||||
// (changing above list without updating src/doc/reference.md makes @cmr sad)
|
||||
|
||||
@ -570,6 +573,7 @@ pub struct Features {
|
||||
pub staged_api: bool,
|
||||
pub stmt_expr_attributes: bool,
|
||||
pub deprecated: bool,
|
||||
pub question_mark: bool,
|
||||
}
|
||||
|
||||
impl Features {
|
||||
@ -603,6 +607,7 @@ impl Features {
|
||||
staged_api: false,
|
||||
stmt_expr_attributes: false,
|
||||
deprecated: false,
|
||||
question_mark: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1001,6 +1006,9 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
|
||||
e.span,
|
||||
"inclusive range syntax is experimental");
|
||||
}
|
||||
ast::ExprKind::Try(..) => {
|
||||
self.gate_feature("question_mark", e.span, "the `?` operator is not stable");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
visit::walk_expr(self, e);
|
||||
@ -1203,6 +1211,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
|
||||
staged_api: cx.has_feature("staged_api"),
|
||||
stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
|
||||
deprecated: cx.has_feature("deprecated"),
|
||||
question_mark: cx.has_feature("question_mark"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1332,7 +1332,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
|
||||
fields.move_map(|x| folder.fold_field(x)),
|
||||
maybe_expr.map(|x| folder.fold_expr(x)))
|
||||
},
|
||||
ExprKind::Paren(ex) => ExprKind::Paren(folder.fold_expr(ex))
|
||||
ExprKind::Paren(ex) => ExprKind::Paren(folder.fold_expr(ex)),
|
||||
ExprKind::Try(ex) => ExprKind::Try(folder.fold_expr(ex)),
|
||||
},
|
||||
span: folder.new_span(span),
|
||||
attrs: attrs.map_thin_attrs(|v| fold_attrs(v, folder)),
|
||||
|
@ -2534,6 +2534,12 @@ impl<'a> Parser<'a> {
|
||||
let mut e = e0;
|
||||
let mut hi;
|
||||
loop {
|
||||
// expr?
|
||||
while self.eat(&token::Question) {
|
||||
let hi = self.span.hi;
|
||||
e = self.mk_expr(lo, hi, ExprKind::Try(e), None);
|
||||
}
|
||||
|
||||
// expr.f
|
||||
if self.eat(&token::Dot) {
|
||||
match self.token {
|
||||
@ -2907,7 +2913,6 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if self.expr_is_complete(&lhs) {
|
||||
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
|
||||
return Ok(lhs);
|
||||
|
@ -2277,6 +2277,10 @@ impl<'a> State<'a> {
|
||||
try!(self.print_inner_attributes_inline(attrs));
|
||||
try!(self.print_expr(&e));
|
||||
try!(self.pclose());
|
||||
},
|
||||
ast::ExprKind::Try(ref e) => {
|
||||
try!(self.print_expr(e));
|
||||
try!(word(&mut self.s, "?"))
|
||||
}
|
||||
}
|
||||
try!(self.ann.post(self, NodeExpr(expr)));
|
||||
|
@ -793,6 +793,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
|
||||
visitor.visit_expr(&output.expr)
|
||||
}
|
||||
}
|
||||
ExprKind::Try(ref subexpression) => {
|
||||
visitor.visit_expr(subexpression)
|
||||
}
|
||||
}
|
||||
|
||||
visitor.visit_expr_post(expression)
|
||||
|
20
src/test/compile-fail/feature-gate-try-operator.rs
Normal file
20
src/test/compile-fail/feature-gate-try-operator.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// 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 <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.
|
||||
|
||||
macro_rules! id {
|
||||
($e:expr) => { $e }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
id!(x?); //~ error: the `?` operator is not stable (see issue #31436)
|
||||
//~^ help: add #![feature(question_mark)] to the crate attributes to enable
|
||||
y?; //~ error: the `?` operator is not stable (see issue #31436)
|
||||
//~^ help: add #![feature(question_mark)] to the crate attributes to enable
|
||||
}
|
34
src/test/run-pass/try-operator-hygiene.rs
Normal file
34
src/test/run-pass/try-operator-hygiene.rs
Normal file
@ -0,0 +1,34 @@
|
||||
// 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 <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.
|
||||
|
||||
// `expr?` expands to:
|
||||
//
|
||||
// match expr {
|
||||
// Ok(val) => val,
|
||||
// Err(err) => return From::from(err),
|
||||
// }
|
||||
//
|
||||
// This test verifies that the expansion is hygienic, i.e. it's not affected by other `val` and
|
||||
// `err` bindings that may be in scope.
|
||||
|
||||
#![feature(question_mark)]
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
fn main() {
|
||||
assert_eq!(parse(), Ok(1));
|
||||
}
|
||||
|
||||
fn parse() -> Result<i32, ParseIntError> {
|
||||
const val: char = 'a';
|
||||
const err: char = 'b';
|
||||
|
||||
Ok("1".parse::<i32>()?)
|
||||
}
|
200
src/test/run-pass/try-operator.rs
Normal file
200
src/test/run-pass/try-operator.rs
Normal file
@ -0,0 +1,200 @@
|
||||
// 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 <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.
|
||||
|
||||
#![feature(question_mark)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Read, self};
|
||||
use std::num::ParseIntError;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn on_method() -> Result<i32, ParseIntError> {
|
||||
Ok("1".parse::<i32>()? + "2".parse::<i32>()?)
|
||||
}
|
||||
|
||||
fn in_chain() -> Result<String, ParseIntError> {
|
||||
Ok("3".parse::<i32>()?.to_string())
|
||||
}
|
||||
|
||||
fn on_call() -> Result<i32, ParseIntError> {
|
||||
fn parse<T: FromStr>(s: &str) -> Result<T, T::Err> {
|
||||
s.parse()
|
||||
}
|
||||
|
||||
Ok(parse("4")?)
|
||||
}
|
||||
|
||||
fn nested() -> Result<i32, ParseIntError> {
|
||||
Ok("5".parse::<i32>()?.to_string().parse()?)
|
||||
}
|
||||
|
||||
fn on_path() -> Result<i32, ParseIntError> {
|
||||
let x = "6".parse::<i32>();
|
||||
|
||||
Ok(x?)
|
||||
}
|
||||
|
||||
fn on_macro() -> Result<i32, ParseIntError> {
|
||||
macro_rules! id {
|
||||
($e:expr) => { $e }
|
||||
}
|
||||
|
||||
Ok(id!("7".parse::<i32>())?)
|
||||
}
|
||||
|
||||
fn on_parens() -> Result<i32, ParseIntError> {
|
||||
let x = "8".parse::<i32>();
|
||||
|
||||
Ok((x)?)
|
||||
}
|
||||
|
||||
fn on_block() -> Result<i32, ParseIntError> {
|
||||
let x = "9".parse::<i32>();
|
||||
|
||||
Ok({x}?)
|
||||
}
|
||||
|
||||
fn on_field() -> Result<i32, ParseIntError> {
|
||||
struct Pair<A, B> { a: A, b: B }
|
||||
|
||||
let x = Pair { a: "10".parse::<i32>(), b: 0 };
|
||||
|
||||
Ok(x.a?)
|
||||
}
|
||||
|
||||
fn on_tuple_field() -> Result<i32, ParseIntError> {
|
||||
let x = ("11".parse::<i32>(), 0);
|
||||
|
||||
Ok(x.0?)
|
||||
}
|
||||
|
||||
fn on_try() -> Result<i32, ParseIntError> {
|
||||
let x = "12".parse::<i32>().map(|i| i.to_string().parse::<i32>());
|
||||
|
||||
Ok(x??)
|
||||
}
|
||||
|
||||
fn on_binary_op() -> Result<i32, ParseIntError> {
|
||||
let x = 13 - "14".parse::<i32>()?;
|
||||
let y = "15".parse::<i32>()? - 16;
|
||||
let z = "17".parse::<i32>()? - "18".parse::<i32>()?;
|
||||
|
||||
Ok(x + y + z)
|
||||
}
|
||||
|
||||
fn on_index() -> Result<i32, ParseIntError> {
|
||||
let x = [19];
|
||||
let y = "0".parse::<usize>();
|
||||
|
||||
Ok(x[y?])
|
||||
}
|
||||
|
||||
fn on_args() -> Result<i32, ParseIntError> {
|
||||
fn sub(x: i32, y: i32) -> i32 { x - y }
|
||||
|
||||
let x = "20".parse();
|
||||
let y = "21".parse();
|
||||
|
||||
Ok(sub(x?, y?))
|
||||
}
|
||||
|
||||
fn on_if() -> Result<i32, ParseIntError> {
|
||||
Ok(if true {
|
||||
"22".parse::<i32>()
|
||||
} else {
|
||||
"23".parse::<i32>()
|
||||
}?)
|
||||
}
|
||||
|
||||
fn on_if_let() -> Result<i32, ParseIntError> {
|
||||
Ok(if let Ok(..) = "24".parse::<i32>() {
|
||||
"25".parse::<i32>()
|
||||
} else {
|
||||
"26".parse::<i32>()
|
||||
}?)
|
||||
}
|
||||
|
||||
fn on_match() -> Result<i32, ParseIntError> {
|
||||
Ok(match "27".parse::<i32>() {
|
||||
Err(..) => "28".parse::<i32>(),
|
||||
Ok(..) => "29".parse::<i32>(),
|
||||
}?)
|
||||
}
|
||||
|
||||
fn tight_binding() -> Result<bool, ()> {
|
||||
fn ok<T>(x: T) -> Result<T, ()> { Ok(x) }
|
||||
|
||||
let x = ok(true);
|
||||
Ok(!x?)
|
||||
}
|
||||
|
||||
// just type check
|
||||
fn merge_error() -> Result<i32, Error> {
|
||||
let mut s = String::new();
|
||||
|
||||
File::open("foo.txt")?.read_to_string(&mut s)?;
|
||||
|
||||
Ok(s.parse::<i32>()? + 1)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(Ok(3), on_method());
|
||||
|
||||
assert_eq!(Ok("3".to_string()), in_chain());
|
||||
|
||||
assert_eq!(Ok(4), on_call());
|
||||
|
||||
assert_eq!(Ok(5), nested());
|
||||
|
||||
assert_eq!(Ok(6), on_path());
|
||||
|
||||
assert_eq!(Ok(7), on_macro());
|
||||
|
||||
assert_eq!(Ok(8), on_parens());
|
||||
|
||||
assert_eq!(Ok(9), on_block());
|
||||
|
||||
assert_eq!(Ok(10), on_field());
|
||||
|
||||
assert_eq!(Ok(11), on_tuple_field());
|
||||
|
||||
assert_eq!(Ok(12), on_try());
|
||||
|
||||
assert_eq!(Ok(-3), on_binary_op());
|
||||
|
||||
assert_eq!(Ok(19), on_index());
|
||||
|
||||
assert_eq!(Ok(-1), on_args());
|
||||
|
||||
assert_eq!(Ok(22), on_if());
|
||||
|
||||
assert_eq!(Ok(25), on_if_let());
|
||||
|
||||
assert_eq!(Ok(29), on_match());
|
||||
|
||||
assert_eq!(Ok(false), tight_binding());
|
||||
}
|
||||
|
||||
enum Error {
|
||||
Io(io::Error),
|
||||
Parse(ParseIntError),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Error {
|
||||
Error::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for Error {
|
||||
fn from(e: ParseIntError) -> Error {
|
||||
Error::Parse(e)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user