diff --git a/marker_api/src/ast/expr.rs b/marker_api/src/ast/expr.rs index c7bedd11..65d13a39 100644 --- a/marker_api/src/ast/expr.rs +++ b/marker_api/src/ast/expr.rs @@ -61,7 +61,7 @@ pub enum ExprKind<'ast> { UnaryOp(&'ast UnaryOpExpr<'ast>), Ref(&'ast RefExpr<'ast>), BinaryOp(&'ast BinaryOpExpr<'ast>), - QuestionMark(&'ast QuestionMarkExpr<'ast>), + Try(&'ast TryExpr<'ast>), Assign(&'ast AssignExpr<'ast>), As(&'ast AsExpr<'ast>), Path(&'ast PathExpr<'ast>), @@ -180,7 +180,7 @@ pub enum ExprPrecedence { Fn = 0x1000_0000, Index = 0x1000_0001, - QuestionMark = 0x0F00_0000, + Try = 0x0F00_0000, /// The unary `-` operator Neg = 0x0E00_0000, @@ -249,7 +249,7 @@ macro_rules! impl_expr_kind_fn { impl_expr_kind_fn!((ExprKind) $method() -> $return_ty, IntLit, FloatLit, StrLit, CharLit, BoolLit, Block, Closure, - UnaryOp, Ref, BinaryOp, QuestionMark, As, Assign, + UnaryOp, Ref, BinaryOp, Try, As, Assign, Path, Index, Field, Call, Method, Array, Tuple, Ctor, Range, @@ -384,7 +384,7 @@ mod test { assert_eq!(40, size_of::>(), "UnaryOpExpr<'_>"); assert_eq!(40, size_of::>(), "RefExpr<'_>"); assert_eq!(56, size_of::>(), "BinaryOpExpr<'_>"); - assert_eq!(32, size_of::>(), "QuestionMarkExpr<'_>"); + assert_eq!(32, size_of::>(), "TryExpr<'_>"); assert_eq!(80, size_of::>(), "AssignExpr<'_>"); assert_eq!(48, size_of::>(), "AsExpr<'_>"); assert_eq!(96, size_of::>(), "PathExpr<'_>"); diff --git a/marker_api/src/ast/expr/op_exprs.rs b/marker_api/src/ast/expr/op_exprs.rs index 0c37232f..75ae5391 100644 --- a/marker_api/src/ast/expr/op_exprs.rs +++ b/marker_api/src/ast/expr/op_exprs.rs @@ -142,23 +142,57 @@ impl<'ast> RefExpr<'ast> { } } +/// The `?` operator that unwraps valid values or propagates erroneous values to +/// the the calling function. +/// +/// Here is an example of the operator: +/// +/// ``` +/// fn try_option_example(opt: Option) -> Option { +/// // The `?` operator unwraps the value if `opt` is `Some` or +/// // propagates `None` if `opt` is empty. +/// // v +/// let value = opt?; +/// // `value` has the type `i32` +/// +/// // [...] +/// # Some(value.to_string()) +/// } +/// +/// fn try_result_example(res: Result) -> Result { +/// // The `?` operator unwraps the value if `res` is `Ok` or +/// // propagates the value of `Err` if `res` is an error. +/// // v +/// let value = res?; +/// // `value` has the type `i32` +/// +/// // [...] +/// # Ok(value.to_string()) +/// } +/// ``` +/// +/// This operator is also known as the *question mark* or *error propagation* operator. +/// +/// See #[repr(C)] #[derive(Debug)] -pub struct QuestionMarkExpr<'ast> { +pub struct TryExpr<'ast> { data: CommonExprData<'ast>, expr: ExprKind<'ast>, } -impl<'ast> QuestionMarkExpr<'ast> { +impl<'ast> TryExpr<'ast> { + /// The expression that might produce an error, that would be propagated by + /// this operator. pub fn expr(&self) -> ExprKind<'ast> { self.expr } } -super::impl_expr_data!(QuestionMarkExpr<'ast>, QuestionMark); +super::impl_expr_data!(TryExpr<'ast>, Try); #[cfg(feature = "driver-api")] -impl<'ast> QuestionMarkExpr<'ast> { +impl<'ast> TryExpr<'ast> { pub fn new(data: CommonExprData<'ast>, expr: ExprKind<'ast>) -> Self { Self { data, expr } } diff --git a/marker_rustc_driver/src/conversion/marker/expr.rs b/marker_rustc_driver/src/conversion/marker/expr.rs index 27018a0c..ecd265a5 100644 --- a/marker_rustc_driver/src/conversion/marker/expr.rs +++ b/marker_rustc_driver/src/conversion/marker/expr.rs @@ -4,9 +4,9 @@ use marker_api::{ ArrayExpr, AsExpr, AssignExpr, AwaitExpr, BinaryOpExpr, BinaryOpKind, BlockExpr, BoolLitExpr, BreakExpr, CallExpr, CaptureKind, CharLitExpr, ClosureExpr, ClosureParam, CommonExprData, ConstExpr, ContinueExpr, CtorExpr, CtorField, ExprKind, ExprPrecedence, FieldExpr, FloatLitExpr, FloatSuffix, ForExpr, IfExpr, - IndexExpr, IntLitExpr, IntSuffix, LetExpr, LoopExpr, MatchArm, MatchExpr, MethodExpr, PathExpr, - QuestionMarkExpr, RangeExpr, RefExpr, ReturnExpr, StrLitData, StrLitExpr, TupleExpr, UnaryOpExpr, - UnaryOpKind, UnstableExpr, WhileExpr, + IndexExpr, IntLitExpr, IntSuffix, LetExpr, LoopExpr, MatchArm, MatchExpr, MethodExpr, PathExpr, RangeExpr, + RefExpr, ReturnExpr, StrLitData, StrLitExpr, TryExpr, TupleExpr, UnaryOpExpr, UnaryOpKind, UnstableExpr, + WhileExpr, }, pat::PatKind, Ident, Safety, Syncness, @@ -210,7 +210,7 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { ExprKind::Match(self.alloc(MatchExpr::new(data, self.to_expr(scrutinee), self.to_match_arms(arms)))) }, hir::ExprKind::Match(_scrutinee, [_early_return, _continue], hir::MatchSource::TryDesugar(_)) => { - ExprKind::QuestionMark(self.alloc(self.to_try_expr_from_desugar(expr))) + ExprKind::Try(self.alloc(self.to_try_expr_from_desugar(expr))) }, hir::ExprKind::Match(_scrutinee, [_awaitee_arm], hir::MatchSource::AwaitDesugar) => { ExprKind::Await(self.alloc(self.to_await_expr_from_desugar(expr))) @@ -552,10 +552,10 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { } } - fn to_try_expr_from_desugar(&self, try_desugar: &hir::Expr<'tcx>) -> QuestionMarkExpr<'ast> { + fn to_try_expr_from_desugar(&self, try_desugar: &hir::Expr<'tcx>) -> TryExpr<'ast> { if let hir::ExprKind::Match(scrutinee, [_ret, _con], hir::MatchSource::TryDesugar(_)) = try_desugar.kind { if let hir::ExprKind::Call(_try_path, [tested_expr]) = scrutinee.kind { - return QuestionMarkExpr::new( + return TryExpr::new( CommonExprData::new(self.to_expr_id(try_desugar.hir_id), self.to_span_id(try_desugar.span)), self.to_expr(tested_expr), ); diff --git a/marker_uilints/tests/ui/print_cond_expr.stderr b/marker_uilints/tests/ui/print_cond_expr.stderr index 47ee1986..969d0ddd 100644 --- a/marker_uilints/tests/ui/print_cond_expr.stderr +++ b/marker_uilints/tests/ui/print_cond_expr.stderr @@ -1018,8 +1018,8 @@ warning: print test 48 | let _print_option_match = x?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: QuestionMark( - QuestionMarkExpr { + = note: Try( + TryExpr { data: CommonExprData { _lifetime: PhantomData<&()>, id: ExprId(..), @@ -1063,8 +1063,8 @@ warning: print test 54 | let _print_result_match = x?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: QuestionMark( - QuestionMarkExpr { + = note: Try( + TryExpr { data: CommonExprData { _lifetime: PhantomData<&()>, id: ExprId(..), diff --git a/marker_utils/src/visitor.rs b/marker_utils/src/visitor.rs index 112e10d8..ba024405 100644 --- a/marker_utils/src/visitor.rs +++ b/marker_utils/src/visitor.rs @@ -248,7 +248,7 @@ pub fn traverse_expr<'ast, B>( traverse_expr(cx, visitor, e.left())?; traverse_expr(cx, visitor, e.right())?; }, - ExprKind::QuestionMark(e) => { + ExprKind::Try(e) => { traverse_expr(cx, visitor, e.expr())?; }, ExprKind::Assign(e) => { @@ -448,14 +448,14 @@ impl_traversable_for!(&'ast Body<'ast>, traverse_body); pub trait BoolTraversable<'ast>: Traversable<'ast, bool> { /// Checks if the given node contains an early return, in the form of an /// [`ReturnExpr`](marker_api::ast::expr::ReturnExpr) or - /// [`QuestionMarkExpr`](marker_api::ast::expr::QuestionMarkExpr). + /// [`TryExpr`](marker_api::ast::expr::TryExpr). /// /// This function is useful, for lints which suggest moving code snippets into /// a closure or different function. Return statements might prevent the suggested /// refactoring. fn contains_return(&self, cx: &'ast AstContext<'ast>) -> bool { self.for_each_expr(cx, |expr| { - if matches!(expr, ExprKind::Return(_) | ExprKind::QuestionMark(_)) { + if matches!(expr, ExprKind::Return(_) | ExprKind::Try(_)) { ControlFlow::Break(true) } else { ControlFlow::Continue(())