diff --git a/CHANGELOG.md b/CHANGELOG.md index 30d3108e0be4..5bdb6ac160d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1233,6 +1233,7 @@ Released 2018-09-13 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern +[`unneeded_try_convert`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_try_convert [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal diff --git a/README.md b/README.md index 922dbcd11380..c5106e074cbc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 333 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 334 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 00028ec19650..f17749021071 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -282,6 +282,7 @@ pub mod trivially_copy_pass_by_ref; pub mod try_err; pub mod types; pub mod unicode; +pub mod unneeded_try_convert; pub mod unsafe_removed_from_name; pub mod unused_io_amount; pub mod unused_label; @@ -757,6 +758,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, &unicode::ZERO_WIDTH_SPACE, + &unneeded_try_convert::UNNEEDED_TRY_CONVERT, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_label::UNUSED_LABEL, @@ -949,6 +951,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); store.register_late_pass(|| box exit::Exit); store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome); + store.register_late_pass(|| box unneeded_try_convert::UnneededTryConvert); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1274,6 +1277,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unneeded_try_convert::UNNEEDED_TRY_CONVERT), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_label::UNUSED_LABEL), @@ -1455,6 +1459,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf LintId::of(&types::UNIT_ARG), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), + LintId::of(&unneeded_try_convert::UNNEEDED_TRY_CONVERT), LintId::of(&unused_label::UNUSED_LABEL), LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/clippy_lints/src/unneeded_try_convert.rs b/clippy_lints/src/unneeded_try_convert.rs new file mode 100644 index 000000000000..0f324e84b972 --- /dev/null +++ b/clippy_lints/src/unneeded_try_convert.rs @@ -0,0 +1,235 @@ +use crate::utils::{ + is_ctor_or_promotable_const_function, match_def_path, match_type, paths, snippet_with_applicability, + span_lint_and_then, +}; +use if_chain::if_chain; +use rustc::hir::intravisit::Visitor; +use rustc::hir::{self, *}; +use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; +use rustc::ty::Ty; +use rustc::{declare_lint_pass, declare_tool_lint}; +use rustc_errors::Applicability; +use syntax_pos::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for usages of `option.ok_or_else(|| <..>::from(..))?` or + /// `result.map_err(|x| <..>::from(..))`. + /// + /// **Why is this bad?** The `?` operator will call `from` in the `Err` case, + /// so calling it manually is redundant. + /// + /// **Known problems:** The suggested fix does not correct any explicitly provided + /// type arguments in `ok_or_else` or `map_err`. + /// + /// **Example:** + /// ```rust + /// fn bar() -> Result { + /// let x = Some(52.3).ok_or_else(|| String::from("foo"))?; + /// Ok(42) + /// } + /// ``` + /// Could be written: + /// + /// ```rust + /// fn bar() -> Result { + /// let x = Some(52.3).ok_or("foo")?; + /// Ok(42) + /// } + /// ``` + pub UNNEEDED_TRY_CONVERT, + complexity, + "unneeded conversion inside `?`" +} + +declare_lint_pass!(UnneededTryConvert => [UNNEEDED_TRY_CONVERT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnneededTryConvert { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { + if_chain! { + if let ExprKind::Match(match_arg, arms, MatchSource::TryDesugar) = &expr.kind; + if let ExprKind::Call(_, args) = &match_arg.kind; + if let [try_arg] = &**args; + if let Some(fn_error_ty) = get_try_err_ty(cx, arms); + then { + check_option_ok_or_else(cx, fn_error_ty, try_arg); + check_result_map_err(cx, fn_error_ty, try_arg); + } + } + } +} + +/// Given the arms of a `match` expr from desugaring a `?`, return the error type of the `Try` type +fn get_try_err_ty<'tcx>(cx: &LateContext<'_, 'tcx>, match_arms: &[Arm]) -> Option> { + if_chain! { + if let [err_arm, _] = match_arms; + if let ExprKind::Ret(Some(ret_expr)) = &err_arm.body.kind; + if let ExprKind::Call(_, try_from_error_args) = &ret_expr.kind; + if let [try_from_error_arg] = &**try_from_error_args; + then { + return Some(cx.tables.expr_ty(try_from_error_arg)); + } + } + None +} + +fn check_option_ok_or_else<'tcx>(cx: &LateContext<'_, 'tcx>, fn_error_ty: Ty<'tcx>, expr: &Expr) { + if_chain! { + if let ExprKind::MethodCall(call_path, _, call_args) = &expr.kind; + if call_path.ident.as_str() == "ok_or_else"; + if let [receiver, closure_expr] = &**call_args; + if match_type(cx, cx.tables.expr_ty(receiver), &paths::OPTION); + if let Some((closure_body_span, conv_arg)) = check_closure_expr(cx, fn_error_ty, closure_expr); + then { + let mut applicability = Applicability::MachineApplicable; + let conv_arg_snip = snippet_with_applicability(cx, conv_arg.span, "..", &mut applicability); + let (sugg_span, sugg_snip) = if is_trivial_expr(cx, conv_arg) { + // suggest inlining the closure and using `ok_or` + let receiver_snip = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); + (expr.span, format!("{}.ok_or({})", receiver_snip, conv_arg_snip)) + } else { + // suggest removing the conversion in the closure + (closure_body_span, conv_arg_snip.into_owned()) + }; + emit_lint(cx, closure_body_span, sugg_span, sugg_snip, applicability); + } + } +} + +fn check_result_map_err<'tcx>(cx: &LateContext<'_, 'tcx>, fn_error_ty: Ty<'tcx>, expr: &Expr) { + if_chain! { + if let ExprKind::MethodCall(call_path, _, call_args) = &expr.kind; + if call_path.ident.as_str() == "map_err"; + if let [receiver, mapper_expr] = &**call_args; + let receiver_ty = cx.tables.expr_ty(receiver); + if match_type(cx, receiver_ty, &paths::RESULT); + then { + if let Some((closure_body_span, conv_arg)) = check_closure_expr(cx, fn_error_ty, mapper_expr) { + // suggest removing just the conversion in the closure + let mut applicability = Applicability::MachineApplicable; + let conv_arg_snip = snippet_with_applicability(cx, conv_arg.span, "..", &mut applicability); + emit_lint( + cx, + closure_body_span, + closure_body_span, + conv_arg_snip.into_owned(), + applicability, + ); + return; + } + if_chain! { + if let ExprKind::Path(qpath) = &mapper_expr.kind; + if let def::Res::Def(_, def_id) = cx.tables.qpath_res(qpath, mapper_expr.hir_id); + if match_def_path(cx, def_id, &paths::FROM_FROM) + || match_def_path(cx, def_id, &paths::INTO_INTO); + if *cx.tables.expr_ty(mapper_expr).fn_sig(cx.tcx).output().skip_binder() == fn_error_ty; + then { + // suggest removing the entire `map_err(..)` call + let mut applicability = Applicability::MachineApplicable; + let receiver_snip = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); + emit_lint( + cx, + mapper_expr.span, + expr.span, + receiver_snip.into_owned(), + applicability, + ); + } + } + } + } +} + +fn emit_lint(cx: &LateContext<'_, '_>, lint_span: Span, sugg_span: Span, sugg: String, applicability: Applicability) { + span_lint_and_then( + cx, + UNNEEDED_TRY_CONVERT, + lint_span, + "unneeded conversion inside `?`", + move |db| { + db.note("the `?` operator will automatically call `from` in the `Err` case"); + db.span_suggestion(sugg_span, "remove the conversion", sugg, applicability); + }, + ); +} + +/// If `closure_expr` is a closure whose body is a conversion to `fn_error_ty`, +/// return (the span of the conversion call, the argument of the conversion call) +fn check_closure_expr<'tcx>( + cx: &LateContext<'_, 'tcx>, + fn_error_ty: Ty<'tcx>, + closure_expr: &Expr, +) -> Option<(Span, &'tcx Expr)> { + if_chain! { + if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind; + let closure_body = &cx.tcx.hir().body(body_id).value; + if let Some(conv_arg) = conversion_subject(cx, closure_body); + if cx.tables.expr_ty(closure_body) == fn_error_ty; + then { + return Some((closure_body.span, conv_arg)); + } + } + None +} + +/// If `expr` is `From::from()` or `().into()`, returns ``. +fn conversion_subject<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &'tcx Expr) -> Option<&'tcx Expr> { + if_chain! { + if let ExprKind::Call(fn_expr, from_args) = &expr.kind; + if let ExprKind::Path(fn_qpath) = &fn_expr.kind; + if let def::Res::Def(def::DefKind::Method, fn_did) = cx.tables.qpath_res(fn_qpath, fn_expr.hir_id); + if match_def_path(cx, fn_did, &paths::FROM_FROM); + if let [from_arg] = &**from_args; + then { + return Some(from_arg); + } + } + if_chain! { + if let ExprKind::MethodCall(_, _, args) = &expr.kind; + if let Some(call_did) = cx.tables.type_dependent_def_id(expr.hir_id); + if match_def_path(cx, call_did, &paths::INTO_INTO); + if let [receiver] = &**args; + then { + return Some(receiver); + } + } + None +} + +/// Is this expression "trivial" such that a closure containing it could be inlined? +/// (currently very conservative) +fn is_trivial_expr<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &'tcx Expr) -> bool { + struct TrivialVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + trivial: bool, + } + + impl<'a, 'tcx> intravisit::Visitor<'tcx> for TrivialVisitor<'a, 'tcx> { + fn visit_expr(&mut self, expr: &'tcx hir::Expr) { + // whitelist of definitely trivial expressions + self.trivial &= match &expr.kind { + hir::ExprKind::Call(..) => is_ctor_or_promotable_const_function(self.cx, expr), + hir::ExprKind::Tup(..) + | hir::ExprKind::Lit(..) + | hir::ExprKind::Cast(..) + | hir::ExprKind::Field(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::Path(..) + | hir::ExprKind::AddrOf(..) + | hir::ExprKind::Struct(..) => true, + _ => false, + }; + + if self.trivial { + intravisit::walk_expr(self, expr); + } + } + + fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> { + intravisit::NestedVisitorMap::None + } + } + + let mut visitor = TrivialVisitor { cx, trivial: true }; + visitor.visit_expr(expr); + visitor.trivial +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 042ca4ee7fdc..87fc9b07b628 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -40,6 +40,7 @@ pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INTO: [&str; 3] = ["core", "convert", "Into"]; +pub const INTO_INTO: [&str; 4] = ["core", "convert", "Into", "into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0b301b6be963..951579b31d0c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -6,7 +6,7 @@ pub use lint::Lint; pub use lint::LINT_LEVELS; // begin lint list, do not remove this comment, it’s used in `update_lints` -pub const ALL_LINTS: [Lint; 333] = [ +pub const ALL_LINTS: [Lint; 334] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -2086,6 +2086,13 @@ pub const ALL_LINTS: [Lint; 333] = [ deprecation: None, module: "misc_early", }, + Lint { + name: "unneeded_try_convert", + group: "complexity", + desc: "unneeded conversion inside `?`", + deprecation: None, + module: "unneeded_try_convert", + }, Lint { name: "unneeded_wildcard_pattern", group: "complexity", diff --git a/tests/ui/unneeded_try_convert.fixed b/tests/ui/unneeded_try_convert.fixed new file mode 100644 index 000000000000..a942232277c4 --- /dev/null +++ b/tests/ui/unneeded_try_convert.fixed @@ -0,0 +1,63 @@ +// run-rustfix +#![deny(clippy::unneeded_try_convert)] +#![allow(dead_code, unused_imports, clippy::redundant_closure)] + +use std::convert::Into; + +fn result_string() -> Result<(), String> { + let option = Some(3); + option.ok_or("foo")?; + option.ok_or_else(|| complex_computation())?; + // type arg not fixed + // option.ok_or_else::(|| From::from(complex_computation()))?; + // type arg not fixed + // option.ok_or_else::(|| "foo".into())?; + + let result: Result<_, &'static str> = Ok(3); + result.map_err(|_| "foo")?; + result.map_err(|_| complex_computation())?; + // type arg not fixed + // result.map_err::(|_| "foo".into())?; + result.map_err(|x| x)?; + result.map_err(|x| x.trim())?; + result?; + result?; + result?; + + Ok(()) +} + +fn in_closure() { + let option = Some(3); + let _ = || -> Result<_, String> { Ok(option.ok_or("foo")?) }; +} + +#[allow(clippy::option_option)] +fn trivial_closure() { + let option = Some(3); + let _ = || -> Result<_, i32> { Ok(option.ok_or(0_u8)?) }; + let x: u8 = 0; + let _ = || -> Result<_, i32> { Ok(option.ok_or(x)?) }; + const X: u8 = 0; + let _ = || -> Result<_, i32> { Ok(option.ok_or(X)?) }; + let _ = + || -> Result<_, Option>> { Ok(option.ok_or(Some(x as i32))?) }; +} + +fn result_opt_string() -> Result<(), Option> { + // can't convert &str -> Option in one step + let option = Some(3); + option.ok_or_else(|| String::from("foo"))?; + + let result: Result<_, &'static str> = Ok(3); + result.map_err(|_| String::from("foo"))?; + result.map_err(String::from)?; + + Ok(()) +} + +fn complex_computation() -> &'static str { + "bar" +} + +fn main() {} diff --git a/tests/ui/unneeded_try_convert.rs b/tests/ui/unneeded_try_convert.rs new file mode 100644 index 000000000000..9c6b2ba52fac --- /dev/null +++ b/tests/ui/unneeded_try_convert.rs @@ -0,0 +1,63 @@ +// run-rustfix +#![deny(clippy::unneeded_try_convert)] +#![allow(dead_code, unused_imports, clippy::redundant_closure)] + +use std::convert::Into; + +fn result_string() -> Result<(), String> { + let option = Some(3); + option.ok_or_else(|| String::from("foo"))?; + option.ok_or_else(|| String::from(complex_computation()))?; + // type arg not fixed + // option.ok_or_else::(|| From::from(complex_computation()))?; + // type arg not fixed + // option.ok_or_else::(|| "foo".into())?; + + let result: Result<_, &'static str> = Ok(3); + result.map_err(|_| String::from("foo"))?; + result.map_err(|_| String::from(complex_computation()))?; + // type arg not fixed + // result.map_err::(|_| "foo".into())?; + result.map_err(|x| String::from(x))?; + result.map_err(|x| String::from(x.trim()))?; + result.map_err(String::from)?; + result.map_err::(From::from)?; + result.map_err::(Into::into)?; + + Ok(()) +} + +fn in_closure() { + let option = Some(3); + let _ = || -> Result<_, String> { Ok(option.ok_or_else(|| String::from("foo"))?) }; +} + +#[allow(clippy::option_option)] +fn trivial_closure() { + let option = Some(3); + let _ = || -> Result<_, i32> { Ok(option.ok_or_else(|| i32::from(0_u8))?) }; + let x: u8 = 0; + let _ = || -> Result<_, i32> { Ok(option.ok_or_else(|| i32::from(x))?) }; + const X: u8 = 0; + let _ = || -> Result<_, i32> { Ok(option.ok_or_else(|| i32::from(X))?) }; + let _ = + || -> Result<_, Option>> { Ok(option.ok_or_else(|| Option::>::from(Some(x as i32)))?) }; +} + +fn result_opt_string() -> Result<(), Option> { + // can't convert &str -> Option in one step + let option = Some(3); + option.ok_or_else(|| String::from("foo"))?; + + let result: Result<_, &'static str> = Ok(3); + result.map_err(|_| String::from("foo"))?; + result.map_err(String::from)?; + + Ok(()) +} + +fn complex_computation() -> &'static str { + "bar" +} + +fn main() {} diff --git a/tests/ui/unneeded_try_convert.stderr b/tests/ui/unneeded_try_convert.stderr new file mode 100644 index 000000000000..23a84965997f --- /dev/null +++ b/tests/ui/unneeded_try_convert.stderr @@ -0,0 +1,119 @@ +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:9:26 + | +LL | option.ok_or_else(|| String::from("foo"))?; + | ---------------------^^^^^^^^^^^^^^^^^^^- help: remove the conversion: `option.ok_or("foo")` + | +note: lint level defined here + --> $DIR/unneeded_try_convert.rs:2:9 + | +LL | #![deny(clippy::unneeded_try_convert)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:10:26 + | +LL | option.ok_or_else(|| String::from(complex_computation()))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the conversion: `complex_computation()` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:17:24 + | +LL | result.map_err(|_| String::from("foo"))?; + | ^^^^^^^^^^^^^^^^^^^ help: remove the conversion: `"foo"` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:18:24 + | +LL | result.map_err(|_| String::from(complex_computation()))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the conversion: `complex_computation()` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:21:24 + | +LL | result.map_err(|x| String::from(x))?; + | ^^^^^^^^^^^^^^^ help: remove the conversion: `x` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:22:24 + | +LL | result.map_err(|x| String::from(x.trim()))?; + | ^^^^^^^^^^^^^^^^^^^^^^ help: remove the conversion: `x.trim()` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:23:20 + | +LL | result.map_err(String::from)?; + | ---------------^^^^^^^^^^^^- help: remove the conversion: `result` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:24:33 + | +LL | result.map_err::(From::from)?; + | ----------------------------^^^^^^^^^^- help: remove the conversion: `result` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:25:33 + | +LL | result.map_err::(Into::into)?; + | ----------------------------^^^^^^^^^^- help: remove the conversion: `result` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:32:63 + | +LL | let _ = || -> Result<_, String> { Ok(option.ok_or_else(|| String::from("foo"))?) }; + | ---------------------^^^^^^^^^^^^^^^^^^^- help: remove the conversion: `option.ok_or("foo")` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:38:60 + | +LL | let _ = || -> Result<_, i32> { Ok(option.ok_or_else(|| i32::from(0_u8))?) }; + | ---------------------^^^^^^^^^^^^^^^- help: remove the conversion: `option.ok_or(0_u8)` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:40:60 + | +LL | let _ = || -> Result<_, i32> { Ok(option.ok_or_else(|| i32::from(x))?) }; + | ---------------------^^^^^^^^^^^^- help: remove the conversion: `option.ok_or(x)` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:42:60 + | +LL | let _ = || -> Result<_, i32> { Ok(option.ok_or_else(|| i32::from(X))?) }; + | ---------------------^^^^^^^^^^^^- help: remove the conversion: `option.ok_or(X)` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: unneeded conversion inside `?` + --> $DIR/unneeded_try_convert.rs:44:72 + | +LL | || -> Result<_, Option>> { Ok(option.ok_or_else(|| Option::>::from(Some(x as i32)))?) }; + | ---------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: remove the conversion: `option.ok_or(Some(x as i32))` + | + = note: the `?` operator will automatically call `from` in the `Err` case + +error: aborting due to 14 previous errors +