Auto merge of #6657 - ThibsG:FromIterParens, r=llogiq

Fix suggestions that need parens in `from_iter_instead_of_collect` lint

Fixes broken suggestions that need parens (i.e.: range)

Fixes: #6648

changelog: none
This commit is contained in:
bors 2021-02-10 19:37:27 +00:00
commit 3784cdf98e
4 changed files with 202 additions and 13 deletions

View file

@ -4142,20 +4142,54 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<
if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
then {
// `expr` implements `FromIterator` trait
let iter_expr = snippet(cx, args[0].span, "..");
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()`",
format!("{}.collect()", iter_expr),
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 {
expected.constness == actual.constness
&& expected.unsafety == actual.unsafety