Skip to content

Commit 75f40e4

Browse files
committed
cleanup code in par_iter
1 parent e201ee0 commit 75f40e4

File tree

1 file changed

+105
-105
lines changed

1 file changed

+105
-105
lines changed

lints/par_iter/src/lib.rs

+105-105
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
extern crate rustc_errors;
66
extern crate rustc_hir;
7+
extern crate rustc_middle;
78
extern crate rustc_span;
89

910
use clippy_utils::{get_trait_def_id, ty::implements_trait};
@@ -14,8 +15,8 @@ use rustc_hir::{
1415
Expr, ExprKind, Node,
1516
};
1617
use rustc_lint::{LateContext, LateLintPass, LintContext};
17-
use rustc_span::sym;
18-
use utils::span_to_snippet_macro;
18+
use rustc_middle::ty::Ty;
19+
use rustc_span::{sym, Symbol};
1920

2021
dylint_linting::declare_late_lint! {
2122
/// ### What it does
@@ -38,126 +39,45 @@ dylint_linting::declare_late_lint! {
3839
"suggest using par iter"
3940
}
4041

41-
struct ClosureVisitor<'a, 'tcx> {
42-
cx: &'a LateContext<'tcx>,
43-
is_valid: bool,
44-
}
45-
46-
struct Validator<'a, 'tcx> {
47-
cx: &'a LateContext<'tcx>,
48-
is_valid: bool,
49-
}
50-
impl<'a, 'tcx> Visitor<'_> for ClosureVisitor<'a, 'tcx> {
51-
fn visit_expr(&mut self, ex: &Expr) {
52-
match ex.kind {
53-
ExprKind::Path(path) => {
54-
let res: Res = self.cx.typeck_results().qpath_res(&path, ex.hir_id);
55-
56-
if let Res::Local(hir_id) = res {
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-
let is_copy: bool = self
74-
.cx
75-
.tcx
76-
.get_diagnostic_item(sym::Copy)
77-
.map_or(false, |id| implements_trait(self.cx, ty, id, &[]));
78-
let valid = is_copy || (implements_send && implements_sync);
79-
self.is_valid = self.is_valid && valid;
80-
};
81-
}
82-
_ => {}
83-
}
84-
}
85-
}
86-
_ => walk_expr(self, ex),
87-
}
88-
}
89-
}
90-
91-
impl<'a, 'tcx> Visitor<'_> for Validator<'a, 'tcx> {
92-
fn visit_expr(&mut self, ex: &Expr) {
93-
match ex.kind {
94-
ExprKind::Closure(closure) => {
95-
let hir = self.cx.tcx.hir();
96-
let node = hir.get(closure.body.hir_id);
97-
if let Node::Expr(expr) = node {
98-
let mut closure_visitor = ClosureVisitor {
99-
cx: self.cx,
100-
is_valid: true,
101-
};
102-
closure_visitor.visit_expr(expr);
103-
self.is_valid = self.is_valid && closure_visitor.is_valid;
104-
}
105-
}
106-
_ => walk_expr(self, ex),
107-
}
108-
}
109-
}
110-
11142
impl<'tcx> LateLintPass<'tcx> for ParIter {
11243
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
11344
if let ExprKind::MethodCall(path, _recv, _args, _span) = &expr.kind {
114-
let ident_name = &path.ident.name.to_string()[..];
115-
let src_map = cx.sess().source_map();
116-
let mut suggestion = span_to_snippet_macro(src_map, expr.span);
117-
match ident_name {
118-
"into_iter" => suggestion = suggestion.replace("into_iter", "into_par_iter"),
119-
120-
"iter" => suggestion = suggestion.replace("iter", "par_iter"),
121-
"iter_mut" => suggestion = suggestion.replace("iter_mut", "par_iter_mut"),
122-
_ => return,
45+
let method_name = path.ident.name.to_string();
46+
let replacement = get_replacement_method(&method_name);
47+
if replacement.is_empty() {
48+
return;
12349
}
12450

125-
let ty = cx.typeck_results().expr_ty(expr);
51+
let suggestion = generate_suggestion(cx, expr, &method_name, replacement);
12652

127-
let mut implements_par_iter = false;
53+
let ty = cx.typeck_results().expr_ty(expr);
12854

129-
let trait_defs = vec![
130-
get_trait_def_id(cx, &["rayon", "iter", "IntoParallelIterator"]),
131-
get_trait_def_id(cx, &["rayon", "iter", "ParallelIterator"]),
132-
// @todo get_trait_def_id(cx, &["rayon", "iter", "IndexedParallelIterator"]),
133-
// @todo get_trait_def_id(cx, &["rayon", "iter", "IntoParallelRefIterator"]),
134-
// @todo get_trait_def_id(cx, &["rayon", "iter", "IntoParallelRefMutIterator"]),
55+
let trait_paths = [
56+
["rayon", "iter", "IntoParallelIterator"],
57+
["rayon", "iter", "ParallelIterator"],
58+
// @todo ["rayon", "iter", "IndexedParallelIterator"],
59+
// @todo ["rayon", "iter", "IntoParallelRefIterator"],
60+
// @todo ["rayon", "iter", "IntoParallelRefMutIterator"],
61+
// Add more traits as needed
13562
];
13663

137-
for t in trait_defs {
138-
if let Some(trait_def_id) = t {
139-
implements_par_iter =
140-
implements_par_iter || implements_trait(cx, ty, trait_def_id, &[]);
141-
}
142-
}
64+
let implements_par_iter = trait_paths
65+
.iter()
66+
.filter_map(|path| get_trait_def_id(cx, path))
67+
.any(|trait_def_id| implements_trait(cx, ty, trait_def_id, &[]));
14368

14469
if !implements_par_iter {
14570
return;
14671
}
14772

14873
// check that all types inside the closures are Send and sync or Copy
149-
let mut validator = Validator { cx, is_valid: true };
150-
15174
let parent_node = cx.tcx.hir().get_parent(expr.hir_id);
152-
match parent_node {
153-
Node::Expr(expr) => {
154-
validator.visit_expr(expr);
75+
if let Node::Expr(parent_expr) = parent_node {
76+
let mut validator = Validator { cx, is_valid: true };
77+
validator.visit_expr(parent_expr);
78+
if !validator.is_valid {
79+
return;
15580
}
156-
// Handle other kinds of parent nodes as needed
157-
_ => {}
158-
}
159-
if !validator.is_valid {
160-
return;
16181
}
16282

16383
cx.struct_span_lint(
@@ -176,6 +96,86 @@ impl<'tcx> LateLintPass<'tcx> for ParIter {
17696
}
17797
}
17898

99+
struct ClosureVisitor<'a, 'tcx> {
100+
cx: &'a LateContext<'tcx>,
101+
is_valid: bool,
102+
}
103+
104+
struct Validator<'a, 'tcx> {
105+
cx: &'a LateContext<'tcx>,
106+
is_valid: bool,
107+
}
108+
109+
impl<'a, 'tcx> Visitor<'_> for ClosureVisitor<'a, 'tcx> {
110+
fn visit_expr(&mut self, ex: &Expr) {
111+
if let ExprKind::Path(ref path) = ex.kind {
112+
if let Res::Local(hir_id) = self.cx.typeck_results().qpath_res(&path, ex.hir_id) {
113+
if let Node::Local(local) = self.cx.tcx.hir().get_parent(hir_id) {
114+
if let Some(expr) = local.init {
115+
self.is_valid &= is_type_valid(
116+
self.cx,
117+
self.cx.tcx.typeck(expr.hir_id.owner).node_type(expr.hir_id),
118+
);
119+
};
120+
}
121+
}
122+
} else {
123+
walk_expr(self, ex)
124+
}
125+
}
126+
}
127+
128+
impl<'a, 'tcx> Visitor<'_> for Validator<'a, 'tcx> {
129+
fn visit_expr(&mut self, ex: &Expr) {
130+
if let ExprKind::Closure(closure) = ex.kind {
131+
if let Node::Expr(expr) = self.cx.tcx.hir().get(closure.body.hir_id) {
132+
let mut closure_visitor = ClosureVisitor {
133+
cx: self.cx,
134+
is_valid: true,
135+
};
136+
closure_visitor.visit_expr(expr);
137+
self.is_valid &= closure_visitor.is_valid;
138+
}
139+
} else {
140+
walk_expr(self, ex)
141+
}
142+
}
143+
}
144+
fn is_type_valid<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
145+
let implements_send = check_trait_impl(cx, ty, sym::Send);
146+
let implements_sync = check_trait_impl(cx, ty, sym::Sync);
147+
let is_copy = check_trait_impl(cx, ty, sym::Copy);
148+
is_copy || (implements_send && implements_sync)
149+
}
150+
151+
fn check_trait_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, trait_name: Symbol) -> bool {
152+
cx.tcx
153+
.get_diagnostic_item(trait_name)
154+
.map_or(false, |trait_id| implements_trait(cx, ty, trait_id, &[]))
155+
}
156+
157+
fn get_replacement_method(method_name: &str) -> &str {
158+
match method_name {
159+
"into_iter" => "into_par_iter",
160+
"iter" => "par_iter",
161+
"iter_mut" => "par_iter_mut",
162+
_ => "",
163+
}
164+
}
165+
166+
fn generate_suggestion(
167+
cx: &LateContext<'_>,
168+
expr: &Expr<'_>,
169+
method_name: &str,
170+
replacement: &str,
171+
) -> String {
172+
cx.sess()
173+
.source_map()
174+
.span_to_snippet(expr.span)
175+
.map(|s| s.replace(method_name, replacement))
176+
.unwrap_or_else(|_| String::from("/* error: unable to generate suggestion */"))
177+
}
178+
179179
#[test]
180180
fn ui() {
181181
dylint_testing::ui_test_examples(env!("CARGO_PKG_NAME"));

0 commit comments

Comments
 (0)