Skip to content

Commit 2561b7e

Browse files
committed
move from_iter_insteam_of_collect to its own module
1 parent 0c8d143 commit 2561b7e

File tree

2 files changed

+69
-60
lines changed

2 files changed

+69
-60
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use crate::utils::{get_trait_def_id, implements_trait, paths, span_lint_and_sugg, sugg};
2+
use if_chain::if_chain;
3+
use rustc_errors::Applicability;
4+
use rustc_hir as hir;
5+
use rustc_lint::{LateContext, LintContext};
6+
use rustc_middle::ty::Ty;
7+
8+
use super::FROM_ITER_INSTEAD_OF_COLLECT;
9+
10+
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
11+
let ty = cx.typeck_results().expr_ty(expr);
12+
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
13+
14+
if_chain! {
15+
if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
16+
if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR);
17+
18+
if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
19+
then {
20+
// `expr` implements `FromIterator` trait
21+
let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
22+
let turbofish = extract_turbofish(cx, expr, ty);
23+
let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish);
24+
span_lint_and_sugg(
25+
cx,
26+
FROM_ITER_INSTEAD_OF_COLLECT,
27+
expr.span,
28+
"usage of `FromIterator::from_iter`",
29+
"use `.collect()` instead of `::from_iter()`",
30+
sugg,
31+
Applicability::MaybeIncorrect,
32+
);
33+
}
34+
}
35+
}
36+
37+
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
38+
if_chain! {
39+
let call_site = expr.span.source_callsite();
40+
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site);
41+
let snippet_split = snippet.split("::").collect::<Vec<_>>();
42+
if let Some((_, elements)) = snippet_split.split_last();
43+
44+
then {
45+
// is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`)
46+
if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) {
47+
// remove the type specifier from the path elements
48+
let without_ts = elements.iter().filter_map(|e| {
49+
if e == type_specifier { None } else { Some((*e).to_string()) }
50+
}).collect::<Vec<_>>();
51+
// join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`)
52+
format!("{}{}", without_ts.join("::"), type_specifier)
53+
} else {
54+
// type is not explicitly specified so wildcards are needed
55+
// i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
56+
let ty_str = ty.to_string();
57+
let start = ty_str.find('<').unwrap_or(0);
58+
let end = ty_str.find('>').unwrap_or_else(|| ty_str.len());
59+
let nb_wildcard = ty_str[start..end].split(',').count();
60+
let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
61+
format!("{}<{}>", elements.join("::"), wildcards)
62+
}
63+
} else {
64+
ty.to_string()
65+
}
66+
}
67+
}

clippy_lints/src/methods/mod.rs

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod expect_used;
55
mod filetype_is_file;
66
mod filter_map_identity;
77
mod filter_next;
8+
mod from_iter_instead_of_collect;
89
mod get_unwrap;
910
mod implicit_clone;
1011
mod inefficient_to_string;
@@ -1761,7 +1762,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
17611762
hir::ExprKind::Call(ref func, ref args) => {
17621763
if let hir::ExprKind::Path(path) = &func.kind {
17631764
if match_qpath(path, &["from_iter"]) {
1764-
lint_from_iter(cx, expr, args);
1765+
from_iter_instead_of_collect::check(cx, expr, args);
17651766
}
17661767
}
17671768
},
@@ -3440,65 +3441,6 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool {
34403441
}
34413442
}
34423443

3443-
fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
3444-
let ty = cx.typeck_results().expr_ty(expr);
3445-
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
3446-
3447-
if_chain! {
3448-
if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
3449-
if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR);
3450-
3451-
if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
3452-
then {
3453-
// `expr` implements `FromIterator` trait
3454-
let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
3455-
let turbofish = extract_turbofish(cx, expr, ty);
3456-
let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish);
3457-
span_lint_and_sugg(
3458-
cx,
3459-
FROM_ITER_INSTEAD_OF_COLLECT,
3460-
expr.span,
3461-
"usage of `FromIterator::from_iter`",
3462-
"use `.collect()` instead of `::from_iter()`",
3463-
sugg,
3464-
Applicability::MaybeIncorrect,
3465-
);
3466-
}
3467-
}
3468-
}
3469-
3470-
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
3471-
if_chain! {
3472-
let call_site = expr.span.source_callsite();
3473-
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site);
3474-
let snippet_split = snippet.split("::").collect::<Vec<_>>();
3475-
if let Some((_, elements)) = snippet_split.split_last();
3476-
3477-
then {
3478-
// is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`)
3479-
if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) {
3480-
// remove the type specifier from the path elements
3481-
let without_ts = elements.iter().filter_map(|e| {
3482-
if e == type_specifier { None } else { Some((*e).to_string()) }
3483-
}).collect::<Vec<_>>();
3484-
// join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`)
3485-
format!("{}{}", without_ts.join("::"), type_specifier)
3486-
} else {
3487-
// type is not explicitly specified so wildcards are needed
3488-
// i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
3489-
let ty_str = ty.to_string();
3490-
let start = ty_str.find('<').unwrap_or(0);
3491-
let end = ty_str.find('>').unwrap_or_else(|| ty_str.len());
3492-
let nb_wildcard = ty_str[start..end].split(',').count();
3493-
let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
3494-
format!("{}<{}>", elements.join("::"), wildcards)
3495-
}
3496-
} else {
3497-
ty.to_string()
3498-
}
3499-
}
3500-
}
3501-
35023444
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
35033445
expected.constness == actual.constness
35043446
&& expected.unsafety == actual.unsafety

0 commit comments

Comments
 (0)