]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
94222f64 | 2 | use clippy_utils::source::snippet_opt; |
cdc7bbd5 XL |
3 | use clippy_utils::ty::implements_trait; |
4 | use clippy_utils::{is_expr_path_def_path, paths, sugg}; | |
f20569fa XL |
5 | use if_chain::if_chain; |
6 | use rustc_errors::Applicability; | |
7 | use rustc_hir as hir; | |
94222f64 | 8 | use rustc_lint::LateContext; |
f20569fa | 9 | use rustc_middle::ty::Ty; |
cdc7bbd5 | 10 | use rustc_span::sym; |
f20569fa XL |
11 | |
12 | use super::FROM_ITER_INSTEAD_OF_COLLECT; | |
13 | ||
cdc7bbd5 | 14 | pub(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 | 40 | fn 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 | } |