Skip to content

Commit b932587

Browse files
committed
Add better turbofish extractor
1 parent 233fe11 commit b932587

File tree

4 files changed

+120
-23
lines changed

4 files changed

+120
-23
lines changed

Diff for: clippy_lints/src/methods/mod.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -4095,7 +4095,8 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<
40954095
then {
40964096
// `expr` implements `FromIterator` trait
40974097
let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
4098-
let sugg = format!("{}.collect::<{}>()", iter_expr, ty);
4098+
let turbofish = extract_turbofish(cx, expr, ty);
4099+
let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish);
40994100
span_lint_and_sugg(
41004101
cx,
41014102
FROM_ITER_INSTEAD_OF_COLLECT,
@@ -4109,6 +4110,38 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<
41094110
}
41104111
}
41114112

4113+
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
4114+
if_chain! {
4115+
let call_site = expr.span.source_callsite();
4116+
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site);
4117+
let snippet_split = snippet.split("::").collect::<Vec<_>>();
4118+
if let Some((_, elements)) = snippet_split.split_last();
4119+
4120+
then {
4121+
// is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`)
4122+
if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) {
4123+
// remove the type specifier from the path elements
4124+
let without_ts = elements.iter().filter_map(|e| {
4125+
if e == type_specifier { None } else { Some((*e).to_string()) }
4126+
}).collect::<Vec<_>>();
4127+
// join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`)
4128+
format!("{}{}", without_ts.join("::"), type_specifier)
4129+
} else {
4130+
// type is not explicitly specified so wildcards are needed
4131+
// i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
4132+
let ty_str = ty.to_string();
4133+
let start = ty_str.find('<').unwrap_or(0);
4134+
let end = ty_str.find('>').unwrap_or_else(|| ty_str.len());
4135+
let nb_wildcard = ty_str[start..end].split(',').count();
4136+
let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
4137+
format!("{}<{}>", elements.join("::"), wildcards)
4138+
}
4139+
} else {
4140+
ty.to_string()
4141+
}
4142+
}
4143+
}
4144+
41124145
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
41134146
expected.constness == actual.constness
41144147
&& expected.unsafety == actual.unsafety

Diff for: tests/ui/from_iter_instead_of_collect.fixed

+22-8
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,41 @@ use std::iter::FromIterator;
88

99
fn main() {
1010
let iter_expr = std::iter::repeat(5).take(5);
11-
let _ = iter_expr.collect::<std::vec::Vec<i32>>();
11+
let _ = iter_expr.collect::<Vec<_>>();
1212

13-
let _ = vec![5, 5, 5, 5].iter().enumerate().collect::<std::collections::HashMap<usize, &i8>>();
13+
let _ = vec![5, 5, 5, 5].iter().enumerate().collect::<HashMap<usize, &i8>>();
1414

1515
Vec::from_iter(vec![42u32]);
1616

1717
let a = vec![0, 1, 2];
18-
assert_eq!(a, (0..3).collect::<std::vec::Vec<i32>>());
18+
assert_eq!(a, (0..3).collect::<Vec<_>>());
19+
assert_eq!(a, (0..3).collect::<Vec<i32>>());
1920

20-
let mut b = (0..3).collect::<std::collections::VecDeque<i32>>();
21+
let mut b = (0..3).collect::<VecDeque<_>>();
2122
b.push_back(4);
2223

24+
let mut b = (0..3).collect::<VecDeque<i32>>();
25+
b.push_back(4);
26+
27+
{
28+
use std::collections;
29+
let mut b = (0..3).collect::<collections::VecDeque<i32>>();
30+
b.push_back(4);
31+
}
32+
2333
let values = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')];
24-
let bm = values.iter().cloned().collect::<std::collections::BTreeMap<i32, char>>();
25-
let mut bar = bm.range(0..2).collect::<std::collections::BTreeMap<&i32, &char>>();
34+
let bm = values.iter().cloned().collect::<BTreeMap<_, _>>();
35+
let mut bar = bm.range(0..2).collect::<BTreeMap<_, _>>();
2636
bar.insert(&4, &'e');
2737

28-
let mut bts = (0..3).collect::<std::collections::BTreeSet<i32>>();
38+
let mut bts = (0..3).collect::<BTreeSet<_>>();
2939
bts.insert(2);
3040
{
3141
use std::collections;
32-
let _ = (0..3).collect::<std::collections::BTreeSet<i32>>();
42+
let _ = (0..3).collect::<collections::BTreeSet<_>>();
43+
let _ = (0..3).collect::<collections::BTreeSet<u32>>();
3344
}
45+
46+
for _i in [1, 2, 3].iter().collect::<Vec<_>>() {}
47+
for _i in [1, 2, 3].iter().collect::<Vec<&i32>>() {}
3448
}

Diff for: tests/ui/from_iter_instead_of_collect.rs

+14
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,20 @@ fn main() {
1616

1717
let a = vec![0, 1, 2];
1818
assert_eq!(a, Vec::from_iter(0..3));
19+
assert_eq!(a, Vec::<i32>::from_iter(0..3));
1920

2021
let mut b = VecDeque::from_iter(0..3);
2122
b.push_back(4);
2223

24+
let mut b = VecDeque::<i32>::from_iter(0..3);
25+
b.push_back(4);
26+
27+
{
28+
use std::collections;
29+
let mut b = collections::VecDeque::<i32>::from_iter(0..3);
30+
b.push_back(4);
31+
}
32+
2333
let values = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')];
2434
let bm = BTreeMap::from_iter(values.iter().cloned());
2535
let mut bar = BTreeMap::from_iter(bm.range(0..2));
@@ -30,5 +40,9 @@ fn main() {
3040
{
3141
use std::collections;
3242
let _ = collections::BTreeSet::from_iter(0..3);
43+
let _ = collections::BTreeSet::<u32>::from_iter(0..3);
3344
}
45+
46+
for _i in Vec::from_iter([1, 2, 3].iter()) {}
47+
for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {}
3448
}

Diff for: tests/ui/from_iter_instead_of_collect.stderr

+50-14
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,87 @@ error: usage of `FromIterator::from_iter`
22
--> $DIR/from_iter_instead_of_collect.rs:11:13
33
|
44
LL | let _ = Vec::from_iter(iter_expr);
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::<std::vec::Vec<i32>>()`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::<Vec<_>>()`
66
|
77
= note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings`
88

99
error: usage of `FromIterator::from_iter`
1010
--> $DIR/from_iter_instead_of_collect.rs:13:13
1111
|
1212
LL | let _ = HashMap::<usize, &i8>::from_iter(vec![5, 5, 5, 5].iter().enumerate());
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect::<std::collections::HashMap<usize, &i8>>()`
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect::<HashMap<usize, &i8>>()`
1414

1515
error: usage of `FromIterator::from_iter`
1616
--> $DIR/from_iter_instead_of_collect.rs:18:19
1717
|
1818
LL | assert_eq!(a, Vec::from_iter(0..3));
19-
| ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<std::vec::Vec<i32>>()`
19+
| ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<Vec<_>>()`
2020

2121
error: usage of `FromIterator::from_iter`
22-
--> $DIR/from_iter_instead_of_collect.rs:20:17
22+
--> $DIR/from_iter_instead_of_collect.rs:19:19
23+
|
24+
LL | assert_eq!(a, Vec::<i32>::from_iter(0..3));
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<Vec<i32>>()`
26+
27+
error: usage of `FromIterator::from_iter`
28+
--> $DIR/from_iter_instead_of_collect.rs:21:17
2329
|
2430
LL | let mut b = VecDeque::from_iter(0..3);
25-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<std::collections::VecDeque<i32>>()`
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<VecDeque<_>>()`
32+
33+
error: usage of `FromIterator::from_iter`
34+
--> $DIR/from_iter_instead_of_collect.rs:24:17
35+
|
36+
LL | let mut b = VecDeque::<i32>::from_iter(0..3);
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<VecDeque<i32>>()`
38+
39+
error: usage of `FromIterator::from_iter`
40+
--> $DIR/from_iter_instead_of_collect.rs:29:21
41+
|
42+
LL | let mut b = collections::VecDeque::<i32>::from_iter(0..3);
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<collections::VecDeque<i32>>()`
2644

2745
error: usage of `FromIterator::from_iter`
28-
--> $DIR/from_iter_instead_of_collect.rs:24:14
46+
--> $DIR/from_iter_instead_of_collect.rs:34:14
2947
|
3048
LL | let bm = BTreeMap::from_iter(values.iter().cloned());
31-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `values.iter().cloned().collect::<std::collections::BTreeMap<i32, char>>()`
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `values.iter().cloned().collect::<BTreeMap<_, _>>()`
3250

3351
error: usage of `FromIterator::from_iter`
34-
--> $DIR/from_iter_instead_of_collect.rs:25:19
52+
--> $DIR/from_iter_instead_of_collect.rs:35:19
3553
|
3654
LL | let mut bar = BTreeMap::from_iter(bm.range(0..2));
37-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `bm.range(0..2).collect::<std::collections::BTreeMap<&i32, &char>>()`
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `bm.range(0..2).collect::<BTreeMap<_, _>>()`
3856

3957
error: usage of `FromIterator::from_iter`
40-
--> $DIR/from_iter_instead_of_collect.rs:28:19
58+
--> $DIR/from_iter_instead_of_collect.rs:38:19
4159
|
4260
LL | let mut bts = BTreeSet::from_iter(0..3);
43-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<std::collections::BTreeSet<i32>>()`
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<BTreeSet<_>>()`
4462

4563
error: usage of `FromIterator::from_iter`
46-
--> $DIR/from_iter_instead_of_collect.rs:32:17
64+
--> $DIR/from_iter_instead_of_collect.rs:42:17
4765
|
4866
LL | let _ = collections::BTreeSet::from_iter(0..3);
49-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<std::collections::BTreeSet<i32>>()`
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<collections::BTreeSet<_>>()`
68+
69+
error: usage of `FromIterator::from_iter`
70+
--> $DIR/from_iter_instead_of_collect.rs:43:17
71+
|
72+
LL | let _ = collections::BTreeSet::<u32>::from_iter(0..3);
73+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::<collections::BTreeSet<u32>>()`
74+
75+
error: usage of `FromIterator::from_iter`
76+
--> $DIR/from_iter_instead_of_collect.rs:46:15
77+
|
78+
LL | for _i in Vec::from_iter([1, 2, 3].iter()) {}
79+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::<Vec<_>>()`
80+
81+
error: usage of `FromIterator::from_iter`
82+
--> $DIR/from_iter_instead_of_collect.rs:47:15
83+
|
84+
LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {}
85+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::<Vec<&i32>>()`
5086

51-
error: aborting due to 8 previous errors
87+
error: aborting due to 14 previous errors
5288

0 commit comments

Comments
 (0)