4
4
5
5
extern crate rustc_errors;
6
6
extern crate rustc_hir;
7
+ extern crate rustc_middle;
7
8
extern crate rustc_span;
8
9
9
10
use clippy_utils:: { get_trait_def_id, ty:: implements_trait} ;
@@ -14,8 +15,8 @@ use rustc_hir::{
14
15
Expr , ExprKind , Node ,
15
16
} ;
16
17
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 } ;
19
20
20
21
dylint_linting:: declare_late_lint! {
21
22
/// ### What it does
@@ -38,126 +39,45 @@ dylint_linting::declare_late_lint! {
38
39
"suggest using par iter"
39
40
}
40
41
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
-
111
42
impl < ' tcx > LateLintPass < ' tcx > for ParIter {
112
43
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
113
44
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 ;
123
49
}
124
50
125
- let ty = cx . typeck_results ( ) . expr_ty ( expr) ;
51
+ let suggestion = generate_suggestion ( cx , expr, & method_name , replacement ) ;
126
52
127
- let mut implements_par_iter = false ;
53
+ let ty = cx . typeck_results ( ) . expr_ty ( expr ) ;
128
54
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
135
62
] ;
136
63
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, & [ ] ) ) ;
143
68
144
69
if !implements_par_iter {
145
70
return ;
146
71
}
147
72
148
73
// check that all types inside the closures are Send and sync or Copy
149
- let mut validator = Validator { cx, is_valid : true } ;
150
-
151
74
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 ;
155
80
}
156
- // Handle other kinds of parent nodes as needed
157
- _ => { }
158
- }
159
- if !validator. is_valid {
160
- return ;
161
81
}
162
82
163
83
cx. struct_span_lint (
@@ -176,6 +96,86 @@ impl<'tcx> LateLintPass<'tcx> for ParIter {
176
96
}
177
97
}
178
98
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
+
179
179
#[ test]
180
180
fn ui ( ) {
181
181
dylint_testing:: ui_test_examples ( env ! ( "CARGO_PKG_NAME" ) ) ;
0 commit comments