move from_iter_insteam_of_collect to its own module
This commit is contained in:
parent
0c8d143515
commit
2561b7ea06
2 changed files with 69 additions and 60 deletions
67
clippy_lints/src/methods/from_iter_instead_of_collect.rs
Normal file
67
clippy_lints/src/methods/from_iter_instead_of_collect.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use crate::utils::{get_trait_def_id, implements_trait, paths, span_lint_and_sugg, sugg};
|
||||||
|
use if_chain::if_chain;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_lint::{LateContext, LintContext};
|
||||||
|
use rustc_middle::ty::Ty;
|
||||||
|
|
||||||
|
use super::FROM_ITER_INSTEAD_OF_COLLECT;
|
||||||
|
|
||||||
|
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||||
|
let ty = cx.typeck_results().expr_ty(expr);
|
||||||
|
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||||
|
|
||||||
|
if_chain! {
|
||||||
|
if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
|
||||||
|
if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR);
|
||||||
|
|
||||||
|
if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
|
||||||
|
then {
|
||||||
|
// `expr` implements `FromIterator` trait
|
||||||
|
let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
|
||||||
|
let turbofish = extract_turbofish(cx, expr, ty);
|
||||||
|
let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish);
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
FROM_ITER_INSTEAD_OF_COLLECT,
|
||||||
|
expr.span,
|
||||||
|
"usage of `FromIterator::from_iter`",
|
||||||
|
"use `.collect()` instead of `::from_iter()`",
|
||||||
|
sugg,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
|
||||||
|
if_chain! {
|
||||||
|
let call_site = expr.span.source_callsite();
|
||||||
|
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site);
|
||||||
|
let snippet_split = snippet.split("::").collect::<Vec<_>>();
|
||||||
|
if let Some((_, elements)) = snippet_split.split_last();
|
||||||
|
|
||||||
|
then {
|
||||||
|
// is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`)
|
||||||
|
if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) {
|
||||||
|
// remove the type specifier from the path elements
|
||||||
|
let without_ts = elements.iter().filter_map(|e| {
|
||||||
|
if e == type_specifier { None } else { Some((*e).to_string()) }
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
// join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`)
|
||||||
|
format!("{}{}", without_ts.join("::"), type_specifier)
|
||||||
|
} else {
|
||||||
|
// type is not explicitly specified so wildcards are needed
|
||||||
|
// i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
|
||||||
|
let ty_str = ty.to_string();
|
||||||
|
let start = ty_str.find('<').unwrap_or(0);
|
||||||
|
let end = ty_str.find('>').unwrap_or_else(|| ty_str.len());
|
||||||
|
let nb_wildcard = ty_str[start..end].split(',').count();
|
||||||
|
let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
|
||||||
|
format!("{}<{}>", elements.join("::"), wildcards)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ty.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ mod expect_used;
|
||||||
mod filetype_is_file;
|
mod filetype_is_file;
|
||||||
mod filter_map_identity;
|
mod filter_map_identity;
|
||||||
mod filter_next;
|
mod filter_next;
|
||||||
|
mod from_iter_instead_of_collect;
|
||||||
mod get_unwrap;
|
mod get_unwrap;
|
||||||
mod implicit_clone;
|
mod implicit_clone;
|
||||||
mod inefficient_to_string;
|
mod inefficient_to_string;
|
||||||
|
@ -1761,7 +1762,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||||
hir::ExprKind::Call(ref func, ref args) => {
|
hir::ExprKind::Call(ref func, ref args) => {
|
||||||
if let hir::ExprKind::Path(path) = &func.kind {
|
if let hir::ExprKind::Path(path) = &func.kind {
|
||||||
if match_qpath(path, &["from_iter"]) {
|
if match_qpath(path, &["from_iter"]) {
|
||||||
lint_from_iter(cx, expr, args);
|
from_iter_instead_of_collect::check(cx, expr, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3440,65 +3441,6 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
|
||||||
let ty = cx.typeck_results().expr_ty(expr);
|
|
||||||
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
|
|
||||||
|
|
||||||
if_chain! {
|
|
||||||
if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
|
|
||||||
if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR);
|
|
||||||
|
|
||||||
if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
|
|
||||||
then {
|
|
||||||
// `expr` implements `FromIterator` trait
|
|
||||||
let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
|
|
||||||
let turbofish = extract_turbofish(cx, expr, ty);
|
|
||||||
let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish);
|
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
|
||||||
FROM_ITER_INSTEAD_OF_COLLECT,
|
|
||||||
expr.span,
|
|
||||||
"usage of `FromIterator::from_iter`",
|
|
||||||
"use `.collect()` instead of `::from_iter()`",
|
|
||||||
sugg,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
|
|
||||||
if_chain! {
|
|
||||||
let call_site = expr.span.source_callsite();
|
|
||||||
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site);
|
|
||||||
let snippet_split = snippet.split("::").collect::<Vec<_>>();
|
|
||||||
if let Some((_, elements)) = snippet_split.split_last();
|
|
||||||
|
|
||||||
then {
|
|
||||||
// is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`)
|
|
||||||
if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) {
|
|
||||||
// remove the type specifier from the path elements
|
|
||||||
let without_ts = elements.iter().filter_map(|e| {
|
|
||||||
if e == type_specifier { None } else { Some((*e).to_string()) }
|
|
||||||
}).collect::<Vec<_>>();
|
|
||||||
// join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`)
|
|
||||||
format!("{}{}", without_ts.join("::"), type_specifier)
|
|
||||||
} else {
|
|
||||||
// type is not explicitly specified so wildcards are needed
|
|
||||||
// i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
|
|
||||||
let ty_str = ty.to_string();
|
|
||||||
let start = ty_str.find('<').unwrap_or(0);
|
|
||||||
let end = ty_str.find('>').unwrap_or_else(|| ty_str.len());
|
|
||||||
let nb_wildcard = ty_str[start..end].split(',').count();
|
|
||||||
let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
|
|
||||||
format!("{}<{}>", elements.join("::"), wildcards)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ty.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
|
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
|
||||||
expected.constness == actual.constness
|
expected.constness == actual.constness
|
||||||
&& expected.unsafety == actual.unsafety
|
&& expected.unsafety == actual.unsafety
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue