From 0bea3e76d4e83a802a2eba4b7b34cd171f250919 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 9 Jan 2017 16:59:55 +0100 Subject: [PATCH 1/6] fix explicit_into_iter_loop on references fixes #1398 --- clippy_lints/src/loops.rs | 67 ++++++++++++++++++++++++--------------- tests/ui/for_loop.rs | 3 ++ 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d7f952255fe2..d7aa37989404 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -644,48 +644,63 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) { } } +fn lint_iter_method(cx: &LateContext, args: &[Expr], arg: &Expr, expr: &Expr, method_name: &str) { + let object = snippet(cx, args[0].span, "_"); + let suggestion = format!("&{}{}", + if method_name == "iter_mut" { + "mut " + } else { + "" + }, + object); + span_lint_and_then(cx, + EXPLICIT_ITER_LOOP, + expr.span, + &format!("it is more idiomatic to loop over `{}` instead of `{}.{}()`", + suggestion, + object, + method_name), + |db| db.span_suggestion(arg.span, "to write this more concisely, try looping over", suggestion)); +} + fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) { let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used if let ExprMethodCall(ref method, _, ref args) = arg.node { // just the receiver, no arguments if args.len() == 1 { - let method_name = method.node; + let method_name = &*method.node.as_str(); // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x - if &*method_name.as_str() == "iter" || &*method_name.as_str() == "iter_mut" { + if method_name == "iter" || method_name == "iter_mut" { if is_ref_iterable_type(cx, &args[0]) { + lint_iter_method(cx, args, arg, expr, method_name); + } + } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) { + let method_call = ty::MethodCall::expr(arg.id); + let fn_ty = cx.tables + .method_map + .get(&method_call) + .map(|method_callee| method_callee.ty) + .expect("method calls need an entry in the method map"); + let fn_arg_tys = fn_ty.fn_args(); + assert_eq!(fn_arg_tys.skip_binder().len(), 1); + if fn_arg_tys.skip_binder()[0].is_region_ptr() { + lint_iter_method(cx, args, arg, expr, method_name); + } else { let object = snippet(cx, args[0].span, "_"); - let suggestion = format!("&{}{}", - if &*method_name.as_str() == "iter_mut" { - "mut " - } else { - "" - }, - object); span_lint_and_then(cx, - EXPLICIT_ITER_LOOP, + EXPLICIT_INTO_ITER_LOOP, arg.span, &format!("it is more idiomatic to loop over `{}` instead of `{}.{}()`", - suggestion, + object, object, method_name), |db| { - db.span_suggestion(arg.span, "to write this more concisely, try looping over", suggestion); + db.span_suggestion(arg.span, + "to write this more concisely, try looping over", + object.to_string()); }); } - } else if &*method_name.as_str() == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) { - let object = snippet(cx, args[0].span, "_"); - span_lint_and_then(cx, - EXPLICIT_INTO_ITER_LOOP, - arg.span, - &format!("it is more idiomatic to loop over `{}` instead of `{}.{}()`", - object, - object, - method_name), - |db| { - db.span_suggestion(arg.span, "to write this more concisely, try looping over", object.to_string()); - }); - - } else if &*method_name.as_str() == "next" && match_trait_method(cx, arg, &paths::ITERATOR) { + } else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) { span_lint(cx, ITER_NEXT_LOOP, expr.span, diff --git a/tests/ui/for_loop.rs b/tests/ui/for_loop.rs index 42599f851541..3819e944c4a6 100644 --- a/tests/ui/for_loop.rs +++ b/tests/ui/for_loop.rs @@ -302,6 +302,9 @@ fn main() { + let array = [1, 2, 3]; + for _v in array.into_iter() {} //~ERROR it is more idiomatic to loop over `&array` + for _v in &vec { } // these are fine for _v in &mut vec { } // these are fine From c1cf5a2cde64a47a974eb42dc6e02b5b513fb413 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 Jan 2017 12:43:27 +0100 Subject: [PATCH 2/6] cleanup and create another helper function that we should use more often --- clippy_lints/src/loops.rs | 33 +++++++++++++++------------------ clippy_lints/src/utils/mod.rs | 11 +++++++++++ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d7aa37989404..e802c99cd374 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -16,7 +16,7 @@ use utils::sugg; use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, multispan_sugg, in_external_macro, is_refutable, span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, higher, - last_path_segment}; + last_path_segment, span_lint_and_sugg}; use utils::paths; /// **What it does:** Checks for looping over the range of `0..len` of some @@ -644,23 +644,20 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) { } } -fn lint_iter_method(cx: &LateContext, args: &[Expr], arg: &Expr, expr: &Expr, method_name: &str) { +fn lint_iter_method(cx: &LateContext, args: &[Expr], arg: &Expr, method_name: &str) { let object = snippet(cx, args[0].span, "_"); - let suggestion = format!("&{}{}", - if method_name == "iter_mut" { - "mut " - } else { - "" - }, - object); - span_lint_and_then(cx, + let muta = if method_name == "iter_mut" { + "mut " + } else { + "" + }; + span_lint_and_sugg(cx, EXPLICIT_ITER_LOOP, - expr.span, - &format!("it is more idiomatic to loop over `{}` instead of `{}.{}()`", - suggestion, - object, - method_name), - |db| db.span_suggestion(arg.span, "to write this more concisely, try looping over", suggestion)); + arg.span, + "it is more idiomatic to loop over references to containers instead of using explicit \ + iteration methods", + "to write this more concisely, try looping over", + format!("&{}{}", muta, object)) } fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) { @@ -672,7 +669,7 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) { // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x if method_name == "iter" || method_name == "iter_mut" { if is_ref_iterable_type(cx, &args[0]) { - lint_iter_method(cx, args, arg, expr, method_name); + lint_iter_method(cx, args, arg, method_name); } } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) { let method_call = ty::MethodCall::expr(arg.id); @@ -684,7 +681,7 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) { let fn_arg_tys = fn_ty.fn_args(); assert_eq!(fn_arg_tys.skip_binder().len(), 1); if fn_arg_tys.skip_binder()[0].is_region_ptr() { - lint_iter_method(cx, args, arg, expr, method_name); + lint_iter_method(cx, args, arg, method_name); } else { let object = snippet(cx, args[0].span, "_"); span_lint_and_then(cx, diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index ce581febfe41..19fec7e03f6f 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -569,6 +569,17 @@ pub fn span_lint_and_then<'a, 'tcx: 'a, T: LintContext<'tcx>, F>( } } +pub fn span_lint_and_sugg<'a, 'tcx: 'a, T: LintContext<'tcx>>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, +) { + span_lint_and_then(cx, lint, sp, msg, |db| { db.span_suggestion(sp, help, sugg); }); +} + /// Create a suggestion made from several `span → replacement`. /// /// Note: in the JSON format (used by `compiletest_rs`), the help message will appear once per From c46178c5182b559ff03bd1e56b920756dae503ec Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 Jan 2017 13:06:07 +0100 Subject: [PATCH 3/6] rustfmt --- clippy_lints/src/utils/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 19fec7e03f6f..d5ae314a99bf 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -575,7 +575,7 @@ pub fn span_lint_and_sugg<'a, 'tcx: 'a, T: LintContext<'tcx>>( sp: Span, msg: &str, help: &str, - sugg: String, + sugg: String ) { span_lint_and_then(cx, lint, sp, msg, |db| { db.span_suggestion(sp, help, sugg); }); } From 21a0b16aee0e4b732f41a7214f129a511660e2d1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 30 Jan 2017 14:25:25 +0100 Subject: [PATCH 4/6] adjust messages in tests --- clippy_lints/src/loops.rs | 15 +++++---------- tests/ui/for_loop.rs | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index e802c99cd374..c0fb3b63ea9f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -684,18 +684,13 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) { lint_iter_method(cx, args, arg, method_name); } else { let object = snippet(cx, args[0].span, "_"); - span_lint_and_then(cx, + span_lint_and_sugg(cx, EXPLICIT_INTO_ITER_LOOP, arg.span, - &format!("it is more idiomatic to loop over `{}` instead of `{}.{}()`", - object, - object, - method_name), - |db| { - db.span_suggestion(arg.span, - "to write this more concisely, try looping over", - object.to_string()); - }); + "it is more idiomatic to loop over containers instead of using explicit \ + iteration methods`", + "to write this more concisely, try looping over", + object.to_string()); } } else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) { span_lint(cx, diff --git a/tests/ui/for_loop.rs b/tests/ui/for_loop.rs index 3819e944c4a6..76c8a82341d7 100644 --- a/tests/ui/for_loop.rs +++ b/tests/ui/for_loop.rs @@ -303,7 +303,7 @@ fn main() { let array = [1, 2, 3]; - for _v in array.into_iter() {} //~ERROR it is more idiomatic to loop over `&array` + for _v in array.into_iter() {} for _v in &vec { } // these are fine for _v in &mut vec { } // these are fine From 42c451c122be4a1017780c08ff14eee430036029 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 1 Feb 2017 09:21:14 +0100 Subject: [PATCH 5/6] update help message to match the suggestion --- clippy_lints/src/loops.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c0fb3b63ea9f..daddf6ea9b05 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -656,7 +656,7 @@ fn lint_iter_method(cx: &LateContext, args: &[Expr], arg: &Expr, method_name: &s arg.span, "it is more idiomatic to loop over references to containers instead of using explicit \ iteration methods", - "to write this more concisely, try looping over", + "to write this more concisely, try", format!("&{}{}", muta, object)) } @@ -689,7 +689,7 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) { arg.span, "it is more idiomatic to loop over containers instead of using explicit \ iteration methods`", - "to write this more concisely, try looping over", + "to write this more concisely, try", object.to_string()); } } else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) { From f2ff5e21334b8051305227f8ba5c400af47e6e26 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Feb 2017 12:13:44 +0100 Subject: [PATCH 6/6] Update ui test output --- tests/ui/for_loop.stderr | 185 ++++++++++++++++++++------------------- 1 file changed, 97 insertions(+), 88 deletions(-) diff --git a/tests/ui/for_loop.stderr b/tests/ui/for_loop.stderr index 2e9e5d1fe376..608ee7f2629f 100644 --- a/tests/ui/for_loop.stderr +++ b/tests/ui/for_loop.stderr @@ -381,7 +381,7 @@ error: this range is empty so this for loop will never run 268 | | } | |_____^ ...ending here -error: it is more idiomatic to loop over `&vec` instead of `vec.iter()` +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods --> $DIR/for_loop.rs:289:15 | 289 | for _v in vec.iter() { } @@ -392,19 +392,19 @@ note: lint level defined here | 90 | #[deny(needless_range_loop, explicit_iter_loop, explicit_into_iter_loop, iter_next_loop, reverse_range_loop, explicit_counter_loop, for_kv_map)] | ^^^^^^^^^^^^^^^^^^ -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &vec { } -error: it is more idiomatic to loop over `&mut vec` instead of `vec.iter_mut()` +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods --> $DIR/for_loop.rs:294:15 | 294 | for _v in vec.iter_mut() { } | ^^^^^^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &mut vec { } -error: it is more idiomatic to loop over `out_vec` instead of `out_vec.into_iter()` +error: it is more idiomatic to loop over containers instead of using explicit iteration methods` --> $DIR/for_loop.rs:300:15 | 300 | for _v in out_vec.into_iter() { } @@ -415,94 +415,103 @@ note: lint level defined here | 90 | #[deny(needless_range_loop, explicit_iter_loop, explicit_into_iter_loop, iter_next_loop, reverse_range_loop, explicit_counter_loop, for_kv_map)] | ^^^^^^^^^^^^^^^^^^^^^^^ -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in out_vec { } -error: it is more idiomatic to loop over `&[1, 2, 3]` instead of `[1, 2, 3].iter()` - --> $DIR/for_loop.rs:308:15 +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:306:15 | -308 | for _v in [1, 2, 3].iter() { } +306 | for _v in array.into_iter() {} + | ^^^^^^^^^^^^^^^^^ + | +help: to write this more concisely, try + | for _v in &array {} + +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:311:15 + | +311 | for _v in [1, 2, 3].iter() { } | ^^^^^^^^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &[1, 2, 3] { } -error: it is more idiomatic to loop over `&[0; 32]` instead of `[0; 32].iter()` - --> $DIR/for_loop.rs:315:15 +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:318:15 | -315 | for _v in [0; 32].iter() {} +318 | for _v in [0; 32].iter() {} | ^^^^^^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &[0; 32] {} -error: it is more idiomatic to loop over `&ll` instead of `ll.iter()` - --> $DIR/for_loop.rs:323:15 +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:326:15 | -323 | for _v in ll.iter() { } +326 | for _v in ll.iter() { } | ^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &ll { } -error: it is more idiomatic to loop over `&vd` instead of `vd.iter()` - --> $DIR/for_loop.rs:329:15 +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:332:15 | -329 | for _v in vd.iter() { } +332 | for _v in vd.iter() { } | ^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &vd { } -error: it is more idiomatic to loop over `&bh` instead of `bh.iter()` - --> $DIR/for_loop.rs:335:15 +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:338:15 | -335 | for _v in bh.iter() { } +338 | for _v in bh.iter() { } | ^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &bh { } -error: it is more idiomatic to loop over `&hm` instead of `hm.iter()` - --> $DIR/for_loop.rs:341:15 +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:344:15 | -341 | for _v in hm.iter() { } +344 | for _v in hm.iter() { } | ^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &hm { } -error: it is more idiomatic to loop over `&bt` instead of `bt.iter()` - --> $DIR/for_loop.rs:347:15 +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:350:15 | -347 | for _v in bt.iter() { } +350 | for _v in bt.iter() { } | ^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &bt { } -error: it is more idiomatic to loop over `&hs` instead of `hs.iter()` - --> $DIR/for_loop.rs:353:15 +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:356:15 | -353 | for _v in hs.iter() { } +356 | for _v in hs.iter() { } | ^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &hs { } -error: it is more idiomatic to loop over `&bs` instead of `bs.iter()` - --> $DIR/for_loop.rs:359:15 +error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop.rs:362:15 | -359 | for _v in bs.iter() { } +362 | for _v in bs.iter() { } | ^^^^^^^^^ | -help: to write this more concisely, try looping over +help: to write this more concisely, try | for _v in &bs { } error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop.rs:365:5 + --> $DIR/for_loop.rs:368:5 | -365 | for _v in vec.iter().next() { } +368 | for _v in vec.iter().next() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -512,9 +521,9 @@ note: lint level defined here | ^^^^^^^^^^^^^^ error: you are collect()ing an iterator and throwing away the result. Consider using an explicit for loop to exhaust the iterator - --> $DIR/for_loop.rs:372:5 + --> $DIR/for_loop.rs:375:5 | -372 | vec.iter().map(|x| out.push(x)).collect::>(); +375 | vec.iter().map(|x| out.push(x)).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -524,9 +533,9 @@ note: lint level defined here | ^^^^^^^^^^^^^^ error: the variable `_index` is used as a loop counter. Consider using `for (_index, item) in &vec.enumerate()` or similar iterators - --> $DIR/for_loop.rs:377:5 + --> $DIR/for_loop.rs:380:5 | -377 | for _v in &vec { _index += 1 } +380 | for _v in &vec { _index += 1 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: lint level defined here @@ -536,22 +545,22 @@ note: lint level defined here | ^^^^^^^^^^^^^^^^^^^^^ error: the variable `_index` is used as a loop counter. Consider using `for (_index, item) in &vec.enumerate()` or similar iterators - --> $DIR/for_loop.rs:381:5 + --> $DIR/for_loop.rs:384:5 | -381 | for _v in &vec { _index += 1 } +384 | for _v in &vec { _index += 1 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you seem to want to iterate on a map's values - --> $DIR/for_loop.rs:441:5 + --> $DIR/for_loop.rs:444:5 | -441 | for (_, v) in &m { +444 | for (_, v) in &m { | _____^ starting here... -442 | | -443 | | -444 | | 445 | | -446 | | let _v = v; -447 | | } +446 | | +447 | | +448 | | +449 | | let _v = v; +450 | | } | |_____^ ...ending here | note: lint level defined here @@ -563,70 +572,70 @@ help: use the corresponding method | for v in m.values() { error: you seem to want to iterate on a map's values - --> $DIR/for_loop.rs:450:5 + --> $DIR/for_loop.rs:453:5 | -450 | for (_, v) in &*m { +453 | for (_, v) in &*m { | _____^ starting here... -451 | | -452 | | -453 | | 454 | | -455 | | let _v = v; -456 | | // Here the `*` is not actually necesarry, but the test tests that we don't suggest -457 | | // `in *m.values()` as we used to -458 | | } +455 | | +456 | | +457 | | +458 | | let _v = v; +459 | | // Here the `*` is not actually necesarry, but the test tests that we don't suggest +460 | | // `in *m.values()` as we used to +461 | | } | |_____^ ...ending here | help: use the corresponding method | for v in (*m).values() { error: you seem to want to iterate on a map's values - --> $DIR/for_loop.rs:461:5 + --> $DIR/for_loop.rs:464:5 | -461 | for (_, v) in &mut m { +464 | for (_, v) in &mut m { | _____^ starting here... -462 | | -463 | | -464 | | 465 | | -466 | | let _v = v; -467 | | } +466 | | +467 | | +468 | | +469 | | let _v = v; +470 | | } | |_____^ ...ending here | help: use the corresponding method | for v in m.values_mut() { error: you seem to want to iterate on a map's values - --> $DIR/for_loop.rs:470:5 + --> $DIR/for_loop.rs:473:5 | -470 | for (_, v) in &mut *m { +473 | for (_, v) in &mut *m { | _____^ starting here... -471 | | -472 | | -473 | | 474 | | -475 | | let _v = v; -476 | | } +475 | | +476 | | +477 | | +478 | | let _v = v; +479 | | } | |_____^ ...ending here | help: use the corresponding method | for v in (*m).values_mut() { error: you seem to want to iterate on a map's keys - --> $DIR/for_loop.rs:480:5 + --> $DIR/for_loop.rs:483:5 | -480 | for (k, _value) in rm { +483 | for (k, _value) in rm { | _____^ starting here... -481 | | -482 | | -483 | | 484 | | -485 | | let _k = k; -486 | | } +485 | | +486 | | +487 | | +488 | | let _k = k; +489 | | } | |_____^ ...ending here | help: use the corresponding method | for k in rm.keys() { -error: aborting due to 47 previous errors +error: aborting due to 48 previous errors