Skip to content

Commit a95b31d

Browse files
committed
working implementation of iter to par_iter transforms minues some feature and 1 known bug
1 parent b82cfd5 commit a95b31d

File tree

4 files changed

+159
-60
lines changed

4 files changed

+159
-60
lines changed

lints/par_iter/src/lib.rs

+50-22
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
#![warn(unused_extern_crates)]
33
#![feature(let_chains)]
44

5+
extern crate rustc_errors;
56
extern crate rustc_hir;
67
extern crate rustc_span;
78

8-
use clippy_utils::{get_trait_def_id, sym, ty::implements_trait};
9+
use clippy_utils::{get_trait_def_id, ty::implements_trait};
10+
use rustc_errors::Applicability;
911
use rustc_hir::{
1012
def::Res,
1113
intravisit::{walk_expr, Visitor},
1214
Expr, ExprKind, Node,
1315
};
1416
use rustc_lint::{LateContext, LateLintPass, LintContext};
15-
use rustc_span::source_map::SourceMap;
17+
use rustc_span::sym;
1618
use utils::span_to_snippet_macro;
1719

1820
dylint_linting::declare_late_lint! {
@@ -52,17 +54,26 @@ impl<'a, 'tcx> Visitor<'_> for ClosureVisitor<'a, 'tcx> {
5254
let res: Res = self.cx.typeck_results().qpath_res(&path, ex.hir_id);
5355

5456
if let Res::Local(hir_id) = res {
55-
let ty = self.cx.tcx.type_of(hir_id);
56-
57-
let hir = self.cx.tcx.hir();
58-
if let Some(node) = hir.find(hir_id) {
59-
if let rustc_hir::Node::Pat(pat) = node {
60-
let ty = self.cx.tcx.pat_ty(pat);
61-
let ty = typeck_results.pat_ty(pat);
62-
63-
implements_trait(self.cx, ty, trait_id, args);
64-
self.is_valid = true;
57+
let parent = self.cx.tcx.hir().get_parent(hir_id);
58+
match parent {
59+
Node::Local(local) => {
60+
if let Some(expr) = local.init {
61+
let ty =
62+
self.cx.tcx.typeck(expr.hir_id.owner).node_type(expr.hir_id);
63+
let implements_send = self
64+
.cx
65+
.tcx
66+
.get_diagnostic_item(sym::Send)
67+
.map_or(false, |id| implements_trait(self.cx, ty, id, &[]));
68+
let implements_sync = self
69+
.cx
70+
.tcx
71+
.get_diagnostic_item(sym::Sync)
72+
.map_or(false, |id| implements_trait(self.cx, ty, id, &[]));
73+
self.is_valid = self.is_valid && implements_send && implements_sync;
74+
};
6575
}
76+
_ => {}
6677
}
6778
}
6879
}
@@ -83,6 +94,7 @@ impl<'a, 'tcx> Visitor<'_> for Validator<'a, 'tcx> {
8394
is_valid: true,
8495
};
8596
closure_visitor.visit_expr(expr);
97+
self.is_valid = self.is_valid && closure_visitor.is_valid;
8698
}
8799
}
88100
_ => walk_expr(self, ex),
@@ -92,11 +104,18 @@ impl<'a, 'tcx> Visitor<'_> for Validator<'a, 'tcx> {
92104

93105
impl<'tcx> LateLintPass<'tcx> for ParIter {
94106
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
95-
if let ExprKind::MethodCall(path, _recv, _args, _span) = &expr.kind
96-
&& (path.ident.name == sym!(into_iter)
97-
|| path.ident.name == sym!(iter)
98-
|| path.ident.name == sym!(iter_mut))
99-
{
107+
if let ExprKind::MethodCall(path, _recv, _args, _span) = &expr.kind {
108+
let ident_name = &path.ident.name.to_string()[..];
109+
let src_map = cx.sess().source_map();
110+
let mut suggestion = span_to_snippet_macro(src_map, expr.span);
111+
match ident_name {
112+
"into_iter" => suggestion = suggestion.replace("into_iter", "into_par_iter"),
113+
114+
"iter" => suggestion = suggestion.replace("iter", "par_iter"),
115+
"iter_mut" => suggestion = suggestion.replace("iter_mut", "par_iter_mut"),
116+
_ => return,
117+
}
118+
100119
let ty = cx.typeck_results().expr_ty(expr);
101120

102121
let mut implements_par_iter = false;
@@ -119,13 +138,11 @@ impl<'tcx> LateLintPass<'tcx> for ParIter {
119138
if !implements_par_iter {
120139
return;
121140
}
122-
dbg!("implements_par_iter");
123141

124-
// @todo check that all types inside the closures are Send and sync or Copy
142+
// check that all types inside the closures are Send and sync or Copy
125143
let mut validator = Validator { cx, is_valid: true };
126-
let hir = cx.tcx.hir();
127144

128-
let parent_node = hir.get_parent(expr.hir_id);
145+
let parent_node = cx.tcx.hir().get_parent(expr.hir_id);
129146
match parent_node {
130147
Node::Expr(expr) => {
131148
validator.visit_expr(expr);
@@ -137,7 +154,18 @@ impl<'tcx> LateLintPass<'tcx> for ParIter {
137154
return;
138155
}
139156

140-
dbg!("the END");
157+
cx.struct_span_lint(
158+
PAR_ITER,
159+
expr.span,
160+
"found iterator that can be parallelized",
161+
|diag| {
162+
diag.multipart_suggestion(
163+
"try using a parallel iterator",
164+
vec![(expr.span, suggestion)],
165+
Applicability::MachineApplicable,
166+
)
167+
},
168+
);
141169
}
142170
}
143171
}

lints/par_iter/ui/main.fixed

+41-21
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,38 @@
33
#[allow(unused_imports)]
44
use rayon::prelude::*;
55
// use std::collections::LinkedList;
6-
// use std::rc::Rc;
6+
use std::rc::Rc;
77

88
fn main() {
9-
// warn_par_iter_simple();
9+
warn_par_iter_simple();
1010
// warn_par_iter_simple_no_send();
11-
// warn_par_iter_simple_no_send_in_closure_body();
12-
// move_inside_closure();
11+
warn_par_iter_simple_no_send_in_closure_body();
12+
move_inside_closure();
1313
// warn_par_iter_simple_into_parallel_ref_iterator();
1414
// warn_par_iter();
1515
warn_par_complex();
16+
warn_par_complex_no_send()
1617
}
1718

18-
// fn warn_par_iter_simple() {
19-
// (0..100).into_iter().for_each(|x| println!("{:?}", x));
20-
// }
19+
fn warn_par_iter_simple() {
20+
(0..100).into_par_iter().for_each(|x| println!("{:?}", x));
21+
}
2122

22-
// fn move_inside_closure() {
23-
// let y = 100;
24-
// (0..100)
25-
// .into_iter()
26-
// .for_each(|x| println!("{:?}{:?}", x, y));
27-
// }
23+
fn move_inside_closure() {
24+
let y = 100;
25+
(0..100)
26+
.into_par_iter()
27+
.for_each(|x| println!("{:?}{:?}", x, y));
28+
}
2829
// fn warn_par_iter_simple_no_send() {
2930
// let list: Vec<Rc<i32>> = (0..100).map(Rc::new).collect();
3031
// list.iter().for_each(|y| println!("{:?}", y));
3132
// }
3233

33-
// fn warn_par_iter_simple_no_send_in_closure_body() {
34-
// let mut list: Vec<Rc<i32>> = (0..100).map(Rc::new).collect();
35-
// (0..100).into_iter().for_each(|x| list.push(Rc::new(x)));
36-
// }
34+
fn warn_par_iter_simple_no_send_in_closure_body() {
35+
let mut list: Vec<Rc<i32>> = (0..100).map(Rc::new).collect();
36+
(0..100).into_iter().for_each(|x| list.push(Rc::new(x)));
37+
}
3738

3839
// fn warn_par_iter_simple_into_parallel_ref_iterator() {
3940
// let list: LinkedList<i32> = (0..100).collect();
@@ -57,18 +58,37 @@ fn warn_par_complex() {
5758
age: 30,
5859
};
5960

60-
vec.iter().for_each(|x| {
61+
(0..10).into_par_iter().for_each(|x| {
6162
let sum = x + a + b;
6263
let message = if e { c } else { "Goodbye" };
63-
let product = d * (*x as f64);
64+
let product = d * (x as f64);
6465
let person_info = format!("{} is {} years old.", person.name, person.age);
6566
println!(
66-
"Sum: {:?}, Message: {:?}, Product: {:?}, Person: {:?}",
67-
sum, message, product, person_info
67+
"Sum: {sum}, Message: {message}, Product: {product}, Person: {person_info}, Vec: {vec:?}",
6868
);
6969
});
7070
}
7171

72+
fn warn_par_complex_no_send() {
73+
let a = Rc::new(10);
74+
let b = Rc::new(20);
75+
let c = Rc::new("Hello");
76+
let d = Rc::new(3.14);
77+
let e = Rc::new(true);
78+
let person = Rc::new(Person {
79+
name: String::from("Alice"),
80+
age: 30,
81+
});
82+
83+
(0..10).into_iter().for_each(|x| {
84+
let sum = *a + *b;
85+
let message = if *e { *c } else { "Goodbye" };
86+
let product = *d * (x as f64);
87+
let person_info = format!("{} is {} years old.", person.name, person.age);
88+
println!("Sum: {sum}, Message: {message}, Product: {product}, Person: {person_info}",);
89+
});
90+
}
91+
7292
// struct LocalQueue {}
7393

7494
// fn warn_par_iter() {

lints/par_iter/ui/main.rs

+38-17
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,38 @@
33
#[allow(unused_imports)]
44
use rayon::prelude::*;
55
// use std::collections::LinkedList;
6-
// use std::rc::Rc;
6+
use std::rc::Rc;
77

88
fn main() {
9-
// warn_par_iter_simple();
9+
warn_par_iter_simple();
1010
// warn_par_iter_simple_no_send();
11-
// warn_par_iter_simple_no_send_in_closure_body();
12-
// move_inside_closure();
11+
warn_par_iter_simple_no_send_in_closure_body();
12+
move_inside_closure();
1313
// warn_par_iter_simple_into_parallel_ref_iterator();
1414
// warn_par_iter();
1515
warn_par_complex();
16+
warn_par_complex_no_send()
1617
}
1718

18-
// fn warn_par_iter_simple() {
19-
// (0..100).into_iter().for_each(|x| println!("{:?}", x));
20-
// }
19+
fn warn_par_iter_simple() {
20+
(0..100).into_iter().for_each(|x| println!("{:?}", x));
21+
}
2122

22-
// fn move_inside_closure() {
23-
// let y = 100;
24-
// (0..100)
25-
// .into_iter()
26-
// .for_each(|x| println!("{:?}{:?}", x, y));
27-
// }
23+
fn move_inside_closure() {
24+
let y = 100;
25+
(0..100)
26+
.into_iter()
27+
.for_each(|x| println!("{:?}{:?}", x, y));
28+
}
2829
// fn warn_par_iter_simple_no_send() {
2930
// let list: Vec<Rc<i32>> = (0..100).map(Rc::new).collect();
3031
// list.iter().for_each(|y| println!("{:?}", y));
3132
// }
3233

33-
// fn warn_par_iter_simple_no_send_in_closure_body() {
34-
// let mut list: Vec<Rc<i32>> = (0..100).map(Rc::new).collect();
35-
// (0..100).into_iter().for_each(|x| list.push(Rc::new(x)));
36-
// }
34+
fn warn_par_iter_simple_no_send_in_closure_body() {
35+
let mut list: Vec<Rc<i32>> = (0..100).map(Rc::new).collect();
36+
(0..100).into_iter().for_each(|x| list.push(Rc::new(x)));
37+
}
3738

3839
// fn warn_par_iter_simple_into_parallel_ref_iterator() {
3940
// let list: LinkedList<i32> = (0..100).collect();
@@ -68,6 +69,26 @@ fn warn_par_complex() {
6869
});
6970
}
7071

72+
fn warn_par_complex_no_send() {
73+
let a = Rc::new(10);
74+
let b = Rc::new(20);
75+
let c = Rc::new("Hello");
76+
let d = Rc::new(3.14);
77+
let e = Rc::new(true);
78+
let person = Rc::new(Person {
79+
name: String::from("Alice"),
80+
age: 30,
81+
});
82+
83+
(0..10).into_iter().for_each(|x| {
84+
let sum = *a + *b;
85+
let message = if *e { *c } else { "Goodbye" };
86+
let product = *d * (x as f64);
87+
let person_info = format!("{} is {} years old.", person.name, person.age);
88+
println!("Sum: {sum}, Message: {message}, Product: {product}, Person: {person_info}",);
89+
});
90+
}
91+
7192
// struct LocalQueue {}
7293

7394
// fn warn_par_iter() {

lints/par_iter/ui/main.stderr

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error: found iterator that can be parallelized
2+
--> $DIR/main.rs:20:5
3+
|
4+
LL | (0..100).into_iter().for_each(|x| println!("{:?}", x));
5+
| ^^^^^^^^^^^^^^^^^^^^ help: try using a parallel iterator: `(0..100).into_par_iter()`
6+
|
7+
= note: `-D par-iter` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(par_iter)]`
9+
10+
error: found iterator that can be parallelized
11+
--> $DIR/main.rs:25:5
12+
|
13+
LL | / (0..100)
14+
LL | | .into_iter()
15+
| |____________________^
16+
|
17+
help: try using a parallel iterator
18+
|
19+
LL ~ (0..100)
20+
LL + .into_par_iter()
21+
|
22+
23+
error: found iterator that can be parallelized
24+
--> $DIR/main.rs:61:5
25+
|
26+
LL | (0..10).into_iter().for_each(|x| {
27+
| ^^^^^^^^^^^^^^^^^^^ help: try using a parallel iterator: `(0..10).into_par_iter()`
28+
29+
error: aborting due to 3 previous errors
30+

0 commit comments

Comments
 (0)