]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
New upstream version 1.60.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / methods / from_iter_instead_of_collect.rs
CommitLineData
cdc7bbd5 1use clippy_utils::diagnostics::span_lint_and_sugg;
94222f64 2use clippy_utils::source::snippet_opt;
cdc7bbd5
XL
3use clippy_utils::ty::implements_trait;
4use clippy_utils::{is_expr_path_def_path, paths, sugg};
f20569fa
XL
5use if_chain::if_chain;
6use rustc_errors::Applicability;
7use rustc_hir as hir;
94222f64 8use rustc_lint::LateContext;
f20569fa 9use rustc_middle::ty::Ty;
cdc7bbd5 10use rustc_span::sym;
f20569fa
XL
11
12use super::FROM_ITER_INSTEAD_OF_COLLECT;
13
cdc7bbd5 14pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) {
f20569fa 15 if_chain! {
cdc7bbd5
XL
16 if is_expr_path_def_path(cx, func, &paths::FROM_ITERATOR_METHOD);
17 let ty = cx.typeck_results().expr_ty(expr);
18 let arg_ty = cx.typeck_results().expr_ty(&args[0]);
19 if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
f20569fa 20
cdc7bbd5 21 if implements_trait(cx, arg_ty, iter_id, &[]);
f20569fa
XL
22 then {
23 // `expr` implements `FromIterator` trait
24 let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
25 let turbofish = extract_turbofish(cx, expr, ty);
26 let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish);
27 span_lint_and_sugg(
28 cx,
29 FROM_ITER_INSTEAD_OF_COLLECT,
30 expr.span,
31 "usage of `FromIterator::from_iter`",
32 "use `.collect()` instead of `::from_iter()`",
33 sugg,
34 Applicability::MaybeIncorrect,
35 );
36 }
37 }
38}
39
5099ac24 40fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String {
17df50a5
XL
41 fn strip_angle_brackets(s: &str) -> Option<&str> {
42 s.strip_prefix('<')?.strip_suffix('>')
43 }
44
cdc7bbd5 45 let call_site = expr.span.source_callsite();
f20569fa 46 if_chain! {
94222f64 47 if let Some(snippet) = snippet_opt(cx, call_site);
f20569fa
XL
48 let snippet_split = snippet.split("::").collect::<Vec<_>>();
49 if let Some((_, elements)) = snippet_split.split_last();
50
51 then {
17df50a5
XL
52 if_chain! {
53 if let [type_specifier, _] = snippet_split.as_slice();
54 if let Some(type_specifier) = strip_angle_brackets(type_specifier);
55 if let Some((type_specifier, ..)) = type_specifier.split_once(" as ");
56 then {
57 type_specifier.to_string()
58 } else {
59 // is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`)
60 if let Some(type_specifier) = snippet_split.iter().find(|e| strip_angle_brackets(e).is_some()) {
61 // remove the type specifier from the path elements
62 let without_ts = elements.iter().filter_map(|e| {
63 if e == type_specifier { None } else { Some((*e).to_string()) }
64 }).collect::<Vec<_>>();
65 // join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`)
66 format!("{}{}", without_ts.join("::"), type_specifier)
67 } else {
68 // type is not explicitly specified so wildcards are needed
69 // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
70 let ty_str = ty.to_string();
71 let start = ty_str.find('<').unwrap_or(0);
a2a8927a 72 let end = ty_str.find('>').unwrap_or(ty_str.len());
17df50a5
XL
73 let nb_wildcard = ty_str[start..end].split(',').count();
74 let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
75 format!("{}<{}>", elements.join("::"), wildcards)
76 }
77 }
f20569fa
XL
78 }
79 } else {
80 ty.to_string()
81 }
82 }
83}