diff --git a/CHANGELOG.md b/CHANGELOG.md index c1032204a22..45321751ba7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2039,6 +2039,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5a40c00bd67..7f5c4f56f9e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -687,6 +687,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::FOR_KV_MAP, &loops::FOR_LOOPS_OVER_FALLIBLES, &loops::ITER_NEXT_LOOP, + &loops::MANUAL_FLATTEN, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, &loops::NEEDLESS_COLLECT, @@ -1491,6 +1492,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(&loops::ITER_NEXT_LOOP), + LintId::of(&loops::MANUAL_FLATTEN), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::NEEDLESS_COLLECT), @@ -1822,6 +1824,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), + LintId::of(&loops::MANUAL_FLATTEN), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_LET_LOOP), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 5211ca7da32..663c2df23e2 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -5,10 +5,10 @@ use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, - last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, single_segment_path, snippet, - snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, sugg, SpanlessEq, + indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_ok_ctor, is_refutable, is_some_ctor, + is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, + single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -494,8 +494,40 @@ declare_clippy_lint! { "there is no reason to have a single element loop" } +declare_clippy_lint! { + /// **What it does:** Check for unnecessary `if let` usage in a for loop + /// where only the `Some` or `Ok` variant of the iterator element is used. + /// + /// **Why is this bad?** It is verbose and can be simplified + /// by first calling the `flatten` method on the `Iterator`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x = vec![Some(1), Some(2), Some(3)]; + /// for n in x { + /// if let Some(n) = n { + /// println!("{}", n); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// let x = vec![Some(1), Some(2), Some(3)]; + /// for n in x.into_iter().flatten() { + /// println!("{}", n); + /// } + /// ``` + pub MANUAL_FLATTEN, + complexity, + "for loops over `Option`s or `Result`s with a single expression can be simplified" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, + MANUAL_FLATTEN, NEEDLESS_RANGE_LOOP, EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, @@ -517,14 +549,14 @@ declare_lint_pass!(Loops => [ impl<'tcx> LateLintPass<'tcx> for Loops { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((pat, arg, body)) = higher::for_loop(expr) { + if let Some((pat, arg, body, span)) = higher::for_loop(expr) { // we don't want to check expanded macros // this check is not at the top of the function // since higher::for_loop expressions are marked as expansions if body.span.from_expansion() { return; } - check_for_loop(cx, pat, arg, body, expr); + check_for_loop(cx, pat, arg, body, expr, span); } // we don't want to check expanded macros @@ -819,6 +851,7 @@ fn check_for_loop<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, + span: Span, ) { let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr); if !is_manual_memcpy_triggered { @@ -830,6 +863,7 @@ fn check_for_loop<'tcx>( check_for_mut_range_bound(cx, arg, body); check_for_single_element_loop(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr); + check_manual_flatten(cx, pat, arg, body, span); } // this function assumes the given expression is a `for` loop. @@ -1953,6 +1987,79 @@ fn check_for_single_element_loop<'tcx>( } } +/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the +/// iterator element is used. +fn check_manual_flatten<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + span: Span, +) { + if let ExprKind::Block(ref block, _) = body.kind { + // Ensure the `if let` statement is the only expression or statement in the for-loop + let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() { + let match_stmt = &block.stmts[0]; + if let StmtKind::Semi(inner_expr) = match_stmt.kind { + Some(inner_expr) + } else { + None + } + } else if block.stmts.is_empty() { + block.expr + } else { + None + }; + + if_chain! { + if let Some(inner_expr) = inner_expr; + if let ExprKind::Match( + ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } + ) = inner_expr.kind; + // Ensure match_expr in `if let` statement is the same as the pat from the for-loop + if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; + if let ExprKind::Path(QPath::Resolved(None, match_expr_path)) = match_expr.kind; + if let Res::Local(match_expr_path_id) = match_expr_path.res; + if pat_hir_id == match_expr_path_id; + // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` + if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; + let some_ctor = is_some_ctor(cx, path.res); + let ok_ctor = is_ok_ctor(cx, path.res); + if some_ctor || ok_ctor; + let if_let_type = if some_ctor { "Some" } else { "Ok" }; + + then { + // Prepare the error message + let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); + + // Prepare the help message + let mut applicability = Applicability::MaybeIncorrect; + let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); + + span_lint_and_then( + cx, + MANUAL_FLATTEN, + span, + &msg, + |diag| { + let sugg = format!("{}.flatten()", arg_snippet); + diag.span_suggestion( + arg.span, + "try", + sugg, + Applicability::MaybeIncorrect, + ); + diag.span_help( + inner_expr.span, + "...and remove the `if let` statement in the for loop", + ); + } + ); + } + } + } +} + struct MutatePairDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, hir_id_low: Option, diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 2f3cdb894f0..d7239b328bb 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -52,7 +52,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { return; } - if let Some((_, arg, body)) = higher::for_loop(expr) { + if let Some((_, arg, body, _)) = higher::for_loop(expr) { // A `for` loop lowers to: // ```rust // match ::std::iter::Iterator::next(&mut iter) { diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 9e9b79ee1cf..fe8d4d07abc 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,8 +1,6 @@ use rustc_errors::Applicability; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::DefIdTree; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -160,7 +158,7 @@ fn is_some_or_ok_call<'a>( // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; - if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + if utils::is_some_ctor(cx, path.res) || utils::is_ok_ctor(cx, path.res); // Extract inner expression from ARGUMENT if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; @@ -208,25 +206,3 @@ fn is_some_or_ok_call<'a>( fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool { return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr); } - -fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == ok_id; - } - } - } - false -} - -fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == some_id; - } - } - } - false -} diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 3e454eecd97..59503817c0f 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -442,7 +442,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { let mut cur_expr = expr; while let Some(parent_expr) = get_parent_expr(cx, cur_expr) { match higher::for_loop(parent_expr) { - Some((_, args, _)) if args.hir_id == expr.hir_id => return true, + Some((_, args, _, _)) if args.hir_id == expr.hir_id => return true, _ => cur_expr = parent_expr, } } diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index 340d340d6d3..df7f0f95782 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -9,6 +9,7 @@ use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::LateContext; +use rustc_span::source_map::Span; /// Converts a hir binary operator to the corresponding `ast` type. #[must_use] @@ -133,11 +134,11 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { false } -/// Recover the essential nodes of a desugared for loop: -/// `for pat in arg { body }` becomes `(pat, arg, body)`. +/// Recover the essential nodes of a desugared for loop as well as the entire span: +/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. pub fn for_loop<'tcx>( expr: &'tcx hir::Expr<'tcx>, -) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> { +) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> { if_chain! { if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind; if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind; @@ -148,7 +149,7 @@ pub fn for_loop<'tcx>( if let hir::StmtKind::Local(ref local) = let_stmt.kind; if let hir::StmtKind::Expr(ref expr) = body.kind; then { - return Some((&*local.pat, &iterargs[0], expr)); + return Some((&*local.pat, &iterargs[0], expr, arms[0].span)); } } None diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index cccad243e1b..f5c3109560d 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -841,15 +841,13 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { // implementations of native types. Check lang items. let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); let lang_items = cx.tcx.lang_items(); - for lang_item in lang_items.items() { - if let Some(def_id) = lang_item { - let lang_item_path = cx.get_def_path(*def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - for child in cx.tcx.item_children(*def_id) { - if child.ident.name == *item { - return true; - } + for item_def_id in lang_items.items().iter().flatten() { + let lang_item_path = cx.get_def_path(*item_def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + for child in cx.tcx.item_children(*item_def_id) { + if child.ident.name == *item { + return true; } } } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index ef45f9fdcd5..cf93ee0a7a5 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -37,7 +37,7 @@ use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; @@ -50,7 +50,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -1703,6 +1703,30 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { } } +/// Check if the resolution of a given path is an `Ok` variant of `Result`. +pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == ok_id; + } + } + } + false +} + +/// Check if the resolution of a given path is a `Some` variant of `Option`. +pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == some_id; + } + } + } + false +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 149cceb39dd..c132e4de4f6 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { // search for `for _ in vec![…]` if_chain! { - if let Some((_, arg, _)) = higher::for_loop(expr); + if let Some((_, arg, _, _)) = higher::for_loop(expr); if let Some(vec_args) = higher::vec_macro(cx, arg); if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg))); then { diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs new file mode 100644 index 00000000000..cff68eca933 --- /dev/null +++ b/tests/ui/manual_flatten.rs @@ -0,0 +1,76 @@ +#![warn(clippy::manual_flatten)] + +fn main() { + // Test for loop over implicitly adjusted `Iterator` with `if let` expression + let x = vec![Some(1), Some(2), Some(3)]; + for n in x { + if let Some(y) = n { + println!("{}", y); + } + } + + // Test for loop over implicitly implicitly adjusted `Iterator` with `if let` statement + let y: Vec> = vec![]; + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + }; + } + + // Test for loop over by reference + for n in &y { + if let Ok(n) = n { + println!("{}", n); + } + } + + // Test for loop over an implicit reference + // Note: if `clippy::manual_flatten` is made autofixable, this case will + // lead to a follow-up lint `clippy::into_iter_on_ref` + let z = &y; + for n in z { + if let Ok(n) = n { + println!("{}", n); + } + } + + // Test for loop over `Iterator` with `if let` expression + let z = vec![Some(1), Some(2), Some(3)]; + let z = z.iter(); + for n in z { + if let Some(m) = n { + println!("{}", m); + } + } + + // Using the `None` variant should not trigger the lint + // Note: for an autofixable suggestion, the binding in the for loop has to take the + // name of the binding in the `if let` + let z = vec![Some(1), Some(2), Some(3)]; + for n in z { + if n.is_none() { + println!("Nada."); + } + } + + // Using the `Err` variant should not trigger the lint + for n in y.clone() { + if let Err(e) = n { + println!("Oops: {}!", e); + } + } + + // Having an else clause should not trigger the lint + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } else { + println!("Oops!"); + } + } + + // Using manual flatten should not trigger the lint + for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { + println!("{}", n); + } +} diff --git a/tests/ui/manual_flatten.stderr b/tests/ui/manual_flatten.stderr new file mode 100644 index 00000000000..855dd9130e2 --- /dev/null +++ b/tests/ui/manual_flatten.stderr @@ -0,0 +1,108 @@ +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:6:5 + | +LL | for n in x { + | ^ - help: try: `x.into_iter().flatten()` + | _____| + | | +LL | | if let Some(y) = n { +LL | | println!("{}", y); +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::manual-flatten` implied by `-D warnings` +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:7:9 + | +LL | / if let Some(y) = n { +LL | | println!("{}", y); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used + --> $DIR/manual_flatten.rs:14:5 + | +LL | for n in y.clone() { + | ^ --------- help: try: `y.clone().into_iter().flatten()` + | _____| + | | +LL | | if let Ok(n) = n { +LL | | println!("{}", n); +LL | | }; +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:15:9 + | +LL | / if let Ok(n) = n { +LL | | println!("{}", n); +LL | | }; + | |_________^ + +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used + --> $DIR/manual_flatten.rs:21:5 + | +LL | for n in &y { + | ^ -- help: try: `y.iter().flatten()` + | _____| + | | +LL | | if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:22:9 + | +LL | / if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used + --> $DIR/manual_flatten.rs:31:5 + | +LL | for n in z { + | ^ - help: try: `z.into_iter().flatten()` + | _____| + | | +LL | | if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:32:9 + | +LL | / if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:40:5 + | +LL | for n in z { + | ^ - help: try: `z.flatten()` + | _____| + | | +LL | | if let Some(m) = n { +LL | | println!("{}", m); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:41:9 + | +LL | / if let Some(m) = n { +LL | | println!("{}", m); +LL | | } + | |_________^ + +error: aborting due to 5 previous errors +