Skip to content

Commit a2bf404

Browse files
committed
Auto merge of #6101 - pitiK3U:from_iter_instead_of_collect, r=flip1995
Add lint: from_iter_instead_of_collect Fixes #5679 This implements lint for `::from_iter()` from #5679 not the general issue (`std::ops::Add::add`, etc.). This lint checks if expression is function call with `from_iter` name and if it's implementation of the `std::iter::FromIterator` trait. changelog: Introduce from_iter_instead_of_collect lint
2 parents 3ee9c2e + ddf23d6 commit a2bf404

File tree

10 files changed

+111
-7
lines changed

10 files changed

+111
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,7 @@ Released 2018-09-13
17321732
[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
17331733
[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
17341734
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
1735+
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
17351736
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
17361737
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
17371738
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
693693
&methods::FILTER_NEXT,
694694
&methods::FIND_MAP,
695695
&methods::FLAT_MAP_IDENTITY,
696+
&methods::FROM_ITER_INSTEAD_OF_COLLECT,
696697
&methods::GET_UNWRAP,
697698
&methods::INEFFICIENT_TO_STRING,
698699
&methods::INTO_ITER_ON_REF,
@@ -1422,6 +1423,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14221423
LintId::of(&methods::EXPECT_FUN_CALL),
14231424
LintId::of(&methods::FILTER_NEXT),
14241425
LintId::of(&methods::FLAT_MAP_IDENTITY),
1426+
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
14251427
LintId::of(&methods::INTO_ITER_ON_REF),
14261428
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
14271429
LintId::of(&methods::ITER_CLONED_COLLECT),
@@ -1620,6 +1622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
16201622
LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
16211623
LintId::of(&methods::CHARS_LAST_CMP),
16221624
LintId::of(&methods::CHARS_NEXT_CMP),
1625+
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
16231626
LintId::of(&methods::INTO_ITER_ON_REF),
16241627
LintId::of(&methods::ITER_CLONED_COLLECT),
16251628
LintId::of(&methods::ITER_NEXT_SLICE),

clippy_lints/src/lifetimes.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use rustc_middle::hir::map::Map;
1616
use rustc_session::{declare_lint_pass, declare_tool_lint};
1717
use rustc_span::source_map::Span;
1818
use rustc_span::symbol::{kw, Symbol};
19-
use std::iter::FromIterator;
2019

2120
declare_clippy_lint! {
2221
/// **What it does:** Checks for lifetime annotations which can be removed by
@@ -214,14 +213,15 @@ fn could_use_elision<'tcx>(
214213
}
215214

216215
if allowed_lts
217-
.intersection(&FxHashSet::from_iter(
218-
input_visitor
216+
.intersection(
217+
&input_visitor
219218
.nested_elision_site_lts
220219
.iter()
221220
.chain(output_visitor.nested_elision_site_lts.iter())
222221
.cloned()
223-
.filter(|v| matches!(v, RefLt::Named(_))),
224-
))
222+
.filter(|v| matches!(v, RefLt::Named(_)))
223+
.collect(),
224+
)
225225
.next()
226226
.is_some()
227227
{

clippy_lints/src/methods/mod.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,39 @@ declare_clippy_lint! {
13711371
"using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
13721372
}
13731373

1374+
declare_clippy_lint! {
1375+
/// **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator`
1376+
/// trait.
1377+
///
1378+
/// **Why is this bad?** It is recommended style to use collect. See
1379+
/// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
1380+
///
1381+
/// **Known problems:** None.
1382+
///
1383+
/// **Example:**
1384+
///
1385+
/// ```rust
1386+
/// use std::iter::FromIterator;
1387+
///
1388+
/// let five_fives = std::iter::repeat(5).take(5);
1389+
///
1390+
/// let v = Vec::from_iter(five_fives);
1391+
///
1392+
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
1393+
/// ```
1394+
/// Use instead:
1395+
/// ```rust
1396+
/// let five_fives = std::iter::repeat(5).take(5);
1397+
///
1398+
/// let v: Vec<i32> = five_fives.collect();
1399+
///
1400+
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
1401+
/// ```
1402+
pub FROM_ITER_INSTEAD_OF_COLLECT,
1403+
style,
1404+
"use `.collect()` instead of `::from_iter()`"
1405+
}
1406+
13741407
declare_lint_pass!(Methods => [
13751408
UNWRAP_USED,
13761409
EXPECT_USED,
@@ -1421,6 +1454,7 @@ declare_lint_pass!(Methods => [
14211454
OPTION_AS_REF_DEREF,
14221455
UNNECESSARY_LAZY_EVALUATIONS,
14231456
MAP_COLLECT_RESULT_UNIT,
1457+
FROM_ITER_INSTEAD_OF_COLLECT,
14241458
]);
14251459

14261460
impl<'tcx> LateLintPass<'tcx> for Methods {
@@ -1507,6 +1541,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
15071541
}
15081542

15091543
match expr.kind {
1544+
hir::ExprKind::Call(ref func, ref args) => {
1545+
if let hir::ExprKind::Path(path) = &func.kind {
1546+
if match_qpath(path, &["from_iter"]) {
1547+
lint_from_iter(cx, expr, args);
1548+
}
1549+
}
1550+
},
15101551
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
15111552
lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
15121553
lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
@@ -3856,6 +3897,28 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir
38563897
span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg);
38573898
}
38583899

3900+
fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
3901+
let ty = cx.typeck_results().expr_ty(expr);
3902+
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
3903+
3904+
let from_iter_id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap();
3905+
let iter_id = get_trait_def_id(cx, &paths::ITERATOR).unwrap();
3906+
3907+
if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]) {
3908+
// `expr` implements `FromIterator` trait
3909+
let iter_expr = snippet(cx, args[0].span, "..");
3910+
span_lint_and_sugg(
3911+
cx,
3912+
FROM_ITER_INSTEAD_OF_COLLECT,
3913+
expr.span,
3914+
"usage of `FromIterator::from_iter`",
3915+
"use `.collect()` instead of `::from_iter()`",
3916+
format!("{}.collect()", iter_expr),
3917+
Applicability::MaybeIncorrect,
3918+
);
3919+
}
3920+
}
3921+
38593922
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
38603923
expected.constness == actual.constness
38613924
&& expected.unsafety == actual.unsafety

clippy_lints/src/utils/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"];
4444
pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
4545
pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
4646
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
47+
pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
4748
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
4849
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
4950
pub const HASH: [&str; 3] = ["core", "hash", "Hash"];

src/lintlist/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,13 @@ vec![
753753
deprecation: None,
754754
module: "drop_forget_ref",
755755
},
756+
Lint {
757+
name: "from_iter_instead_of_collect",
758+
group: "style",
759+
desc: "use `.collect()` instead of `::from_iter()`",
760+
deprecation: None,
761+
module: "methods",
762+
},
756763
Lint {
757764
name: "future_not_send",
758765
group: "nursery",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![warn(clippy::from_iter_instead_of_collect)]
2+
3+
use std::collections::HashMap;
4+
use std::iter::FromIterator;
5+
6+
fn main() {
7+
let iter_expr = std::iter::repeat(5).take(5);
8+
Vec::from_iter(iter_expr);
9+
10+
HashMap::<usize, &i8>::from_iter(vec![5, 5, 5, 5].iter().enumerate());
11+
12+
Vec::from_iter(vec![42u32]);
13+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: usage of `FromIterator::from_iter`
2+
--> $DIR/from_iter_instead_of_collect.rs:8:5
3+
|
4+
LL | Vec::from_iter(iter_expr);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect()`
6+
|
7+
= note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings`
8+
9+
error: usage of `FromIterator::from_iter`
10+
--> $DIR/from_iter_instead_of_collect.rs:10:5
11+
|
12+
LL | HashMap::<usize, &i8>::from_iter(vec![5, 5, 5, 5].iter().enumerate());
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect()`
14+
15+
error: aborting due to 2 previous errors
16+

tests/ui/get_unwrap.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// run-rustfix
2-
#![allow(unused_mut)]
2+
#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
33
#![deny(clippy::get_unwrap)]
44

55
use std::collections::BTreeMap;

tests/ui/get_unwrap.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// run-rustfix
2-
#![allow(unused_mut)]
2+
#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
33
#![deny(clippy::get_unwrap)]
44

55
use std::collections::BTreeMap;

0 commit comments

Comments
 (0)