1
1
use if_chain:: if_chain;
2
2
use rustc_errors:: Applicability ;
3
3
use rustc_hir as hir;
4
- use rustc_hir:: def:: { DefKind , Res } ;
5
- use rustc_hir:: intravisit:: { walk_item, walk_path, walk_ty, NestedVisitorMap , Visitor } ;
4
+ use rustc_hir:: intravisit:: { walk_expr, walk_impl_item, walk_ty, NestedVisitorMap , Visitor } ;
6
5
use rustc_hir:: {
7
- def, FnDecl , FnRetTy , FnSig , GenericArg , HirId , ImplItem , ImplItemKind , Item , ItemKind , Path , PathSegment , QPath ,
8
- TyKind ,
6
+ def, Expr , ExprKind , FnDecl , FnRetTy , FnSig , GenericArg , ImplItem , ImplItemKind , Item , ItemKind , Path , PathSegment ,
7
+ QPath , TyKind ,
9
8
} ;
10
9
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
11
10
use rustc_middle:: hir:: map:: Map ;
12
11
use rustc_middle:: lint:: in_external_macro;
13
12
use rustc_middle:: ty;
14
- use rustc_middle:: ty:: { DefIdTree , Ty } ;
13
+ use rustc_middle:: ty:: Ty ;
15
14
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
16
- use rustc_span:: symbol :: kw ;
15
+ use rustc_span:: Span ;
17
16
use rustc_typeck:: hir_ty_to_ty;
18
17
19
18
use crate :: utils:: { differing_macro_contexts, span_lint_and_sugg} ;
@@ -81,15 +80,87 @@ fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path<'_>, last_segment: O
81
80
) ;
82
81
}
83
82
84
- // FIXME: always use this (more correct) visitor, not just in method signatures.
85
- struct SemanticUseSelfVisitor < ' a , ' tcx > {
83
+ struct ImplVisitor < ' a , ' tcx > {
86
84
cx : & ' a LateContext < ' a , ' tcx > ,
85
+ item : & ' tcx Item < ' tcx > ,
87
86
self_ty : Ty < ' tcx > ,
88
87
}
89
88
90
- impl < ' a , ' tcx > Visitor < ' tcx > for SemanticUseSelfVisitor < ' a , ' tcx > {
89
+ impl < ' a , ' tcx > ImplVisitor < ' a , ' tcx > {
90
+ fn check_trait_method_impl_decl (
91
+ & mut self ,
92
+ impl_item : & ImplItem < ' tcx > ,
93
+ impl_decl : & ' tcx FnDecl < ' tcx > ,
94
+ impl_trait_ref : ty:: TraitRef < ' tcx > ,
95
+ ) {
96
+ let tcx = self . cx . tcx ;
97
+ let trait_method = tcx
98
+ . associated_items ( impl_trait_ref. def_id )
99
+ . find_by_name_and_kind ( tcx, impl_item. ident , ty:: AssocKind :: Fn , impl_trait_ref. def_id )
100
+ . expect ( "impl method matches a trait method" ) ;
101
+
102
+ let trait_method_sig = tcx. fn_sig ( trait_method. def_id ) ;
103
+ let trait_method_sig = tcx. erase_late_bound_regions ( & trait_method_sig) ;
104
+
105
+ let output_hir_ty = if let FnRetTy :: Return ( ty) = & impl_decl. output {
106
+ Some ( & * * ty)
107
+ } else {
108
+ None
109
+ } ;
110
+
111
+ // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
112
+ // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
113
+ // We use `impl_hir_ty` to see if the type was written as `Self`,
114
+ // `hir_ty_to_ty(...)` to check semantic types of paths, and
115
+ // `trait_ty` to determine which parts of the signature in the trait, mention
116
+ // the type being implemented verbatim (as opposed to `Self`).
117
+ for ( impl_hir_ty, trait_ty) in impl_decl
118
+ . inputs
119
+ . iter ( )
120
+ . chain ( output_hir_ty)
121
+ . zip ( trait_method_sig. inputs_and_output )
122
+ {
123
+ // Check if the input/output type in the trait method specifies the implemented
124
+ // type verbatim, and only suggest `Self` if that isn't the case.
125
+ // This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
126
+ // in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
127
+ // See also https://github.com/rust-lang/rust-clippy/issues/2894.
128
+ let self_ty = impl_trait_ref. self_ty ( ) ;
129
+ if !trait_ty. walk ( ) . any ( |inner| inner == self_ty. into ( ) ) {
130
+ self . visit_ty ( & impl_hir_ty) ;
131
+ }
132
+ }
133
+ }
134
+ }
135
+
136
+ impl < ' a , ' tcx > Visitor < ' tcx > for ImplVisitor < ' a , ' tcx > {
91
137
type Map = Map < ' tcx > ;
92
138
139
+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
140
+ if let ExprKind :: Struct ( QPath :: Resolved ( _, path) , ..) = expr. kind {
141
+ if self
142
+ . cx
143
+ . tcx
144
+ . typeck_tables_of ( expr. hir_id . owner . to_def_id ( ) )
145
+ . node_type ( expr. hir_id )
146
+ == self . self_ty
147
+ {
148
+ match path. res {
149
+ def:: Res :: SelfTy ( ..) => { } ,
150
+ _ => {
151
+ span_use_self_lint ( self . cx , path, None ) ;
152
+ } ,
153
+ }
154
+ span_use_self_lint ( self . cx , path, None ) ;
155
+ }
156
+ }
157
+ walk_expr ( self , expr)
158
+ }
159
+
160
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
161
+ NestedVisitorMap :: OnlyBodies ( self . cx . tcx . hir ( ) )
162
+ }
163
+
93
164
fn visit_ty ( & mut self , hir_ty : & ' tcx hir:: Ty < ' _ > ) {
94
165
if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = & hir_ty. kind {
95
166
match path. res {
@@ -105,54 +176,20 @@ impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> {
105
176
walk_ty ( self , hir_ty)
106
177
}
107
178
108
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
109
- NestedVisitorMap :: None
110
- }
111
- }
112
-
113
- fn check_trait_method_impl_decl < ' a , ' tcx > (
114
- cx : & ' a LateContext < ' a , ' tcx > ,
115
- impl_item : & ImplItem < ' _ > ,
116
- impl_decl : & ' tcx FnDecl < ' _ > ,
117
- impl_trait_ref : ty:: TraitRef < ' tcx > ,
118
- ) {
119
- let trait_method = cx
120
- . tcx
121
- . associated_items ( impl_trait_ref. def_id )
122
- . find_by_name_and_kind ( cx. tcx , impl_item. ident , ty:: AssocKind :: Fn , impl_trait_ref. def_id )
123
- . expect ( "impl method matches a trait method" ) ;
124
-
125
- let trait_method_sig = cx. tcx . fn_sig ( trait_method. def_id ) ;
126
- let trait_method_sig = cx. tcx . erase_late_bound_regions ( & trait_method_sig) ;
127
-
128
- let output_hir_ty = if let FnRetTy :: Return ( ty) = & impl_decl. output {
129
- Some ( & * * ty)
130
- } else {
131
- None
132
- } ;
133
-
134
- // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
135
- // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
136
- // We use `impl_hir_ty` to see if the type was written as `Self`,
137
- // `hir_ty_to_ty(...)` to check semantic types of paths, and
138
- // `trait_ty` to determine which parts of the signature in the trait, mention
139
- // the type being implemented verbatim (as opposed to `Self`).
140
- for ( impl_hir_ty, trait_ty) in impl_decl
141
- . inputs
142
- . iter ( )
143
- . chain ( output_hir_ty)
144
- . zip ( trait_method_sig. inputs_and_output )
145
- {
146
- // Check if the input/output type in the trait method specifies the implemented
147
- // type verbatim, and only suggest `Self` if that isn't the case.
148
- // This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
149
- // in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
150
- // See also https://github.com/rust-lang/rust-clippy/issues/2894.
151
- let self_ty = impl_trait_ref. self_ty ( ) ;
152
- if !trait_ty. walk ( ) . any ( |inner| inner == self_ty. into ( ) ) {
153
- let mut visitor = SemanticUseSelfVisitor { cx, self_ty } ;
154
-
155
- visitor. visit_ty ( & impl_hir_ty) ;
179
+ fn visit_impl_item ( & mut self , ii : & ' tcx ImplItem < ' tcx > ) {
180
+ let tcx = self . cx . tcx ;
181
+ let impl_def_id = tcx. hir ( ) . local_def_id ( self . item . hir_id ) ;
182
+ let impl_trait_ref = tcx. impl_trait_ref ( impl_def_id) ;
183
+ if_chain ! {
184
+ if let Some ( impl_trait_ref) = impl_trait_ref;
185
+ if let ImplItemKind :: Fn ( FnSig { decl: impl_decl, .. } , impl_body_id) = & ii. kind;
186
+ then {
187
+ self . check_trait_method_impl_decl( ii, impl_decl, impl_trait_ref) ;
188
+ let body = tcx. hir( ) . body( * impl_body_id) ;
189
+ self . visit_body( body) ;
190
+ } else {
191
+ walk_impl_item( self , ii)
192
+ }
156
193
}
157
194
}
158
195
}
@@ -177,96 +214,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
177
214
} ;
178
215
179
216
if should_check {
180
- let visitor = & mut UseSelfVisitor {
181
- item_path ,
217
+ let self_ty= hir_ty_to_ty ( cx . tcx , item_type ) ;
218
+ let visitor = & mut ImplVisitor {
182
219
cx,
220
+ item,
221
+ self_ty,
183
222
} ;
184
- let impl_def_id = cx. tcx. hir( ) . local_def_id( item. hir_id) ;
185
- let impl_trait_ref = cx. tcx. impl_trait_ref( impl_def_id) ;
186
-
187
- if let Some ( impl_trait_ref) = impl_trait_ref {
188
- for impl_item_ref in refs {
189
- let impl_item = cx. tcx. hir( ) . impl_item( impl_item_ref. id) ;
190
- if let ImplItemKind :: Fn ( FnSig { decl: impl_decl, .. } , impl_body_id)
191
- = & impl_item. kind {
192
- check_trait_method_impl_decl( cx, impl_item, impl_decl, impl_trait_ref) ;
193
223
194
- let body = cx. tcx. hir( ) . body( * impl_body_id) ;
195
- visitor. visit_body( body) ;
196
- } else {
197
- visitor. visit_impl_item( impl_item) ;
198
- }
199
- }
200
- } else {
201
- for impl_item_ref in refs {
202
- let impl_item = cx. tcx. hir( ) . impl_item( impl_item_ref. id) ;
203
- visitor. visit_impl_item( impl_item) ;
204
- }
224
+ for impl_item_ref in refs {
225
+ let impl_item = cx. tcx. hir( ) . impl_item( impl_item_ref. id) ;
226
+ visitor. visit_impl_item( impl_item) ;
205
227
}
206
228
}
207
229
}
208
230
}
209
231
}
210
232
}
211
-
212
- struct UseSelfVisitor < ' a , ' tcx > {
213
- item_path : & ' a Path < ' a > ,
214
- cx : & ' a LateContext < ' a , ' tcx > ,
215
- }
216
-
217
- impl < ' a , ' tcx > Visitor < ' tcx > for UseSelfVisitor < ' a , ' tcx > {
218
- type Map = Map < ' tcx > ;
219
-
220
- fn visit_path ( & mut self , path : & ' tcx Path < ' _ > , _id : HirId ) {
221
- if !path. segments . iter ( ) . any ( |p| p. ident . span . is_dummy ( ) ) {
222
- if path. segments . len ( ) >= 2 {
223
- let last_but_one = & path. segments [ path. segments . len ( ) - 2 ] ;
224
- if last_but_one. ident . name != kw:: SelfUpper {
225
- let enum_def_id = match path. res {
226
- Res :: Def ( DefKind :: Variant , variant_def_id) => self . cx . tcx . parent ( variant_def_id) ,
227
- Res :: Def ( DefKind :: Ctor ( def:: CtorOf :: Variant , _) , ctor_def_id) => {
228
- let variant_def_id = self . cx . tcx . parent ( ctor_def_id) ;
229
- variant_def_id. and_then ( |def_id| self . cx . tcx . parent ( def_id) )
230
- } ,
231
- _ => None ,
232
- } ;
233
-
234
- if self . item_path . res . opt_def_id ( ) == enum_def_id {
235
- span_use_self_lint ( self . cx , path, Some ( last_but_one) ) ;
236
- }
237
- }
238
- }
239
-
240
- if path. segments . last ( ) . expect ( SEGMENTS_MSG ) . ident . name != kw:: SelfUpper {
241
- if self . item_path . res == path. res {
242
- span_use_self_lint ( self . cx , path, None ) ;
243
- } else if let Res :: Def ( DefKind :: Ctor ( def:: CtorOf :: Struct , _) , ctor_def_id) = path. res {
244
- if self . item_path . res . opt_def_id ( ) == self . cx . tcx . parent ( ctor_def_id) {
245
- span_use_self_lint ( self . cx , path, None ) ;
246
- }
247
- }
248
- }
249
- }
250
-
251
- walk_path ( self , path) ;
252
- }
253
-
254
- fn visit_item ( & mut self , item : & ' tcx Item < ' _ > ) {
255
- match item. kind {
256
- ItemKind :: Use ( ..)
257
- | ItemKind :: Static ( ..)
258
- | ItemKind :: Enum ( ..)
259
- | ItemKind :: Struct ( ..)
260
- | ItemKind :: Union ( ..)
261
- | ItemKind :: Impl { .. }
262
- | ItemKind :: Fn ( ..) => {
263
- // Don't check statements that shadow `Self` or where `Self` can't be used
264
- } ,
265
- _ => walk_item ( self , item) ,
266
- }
267
- }
268
-
269
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
270
- NestedVisitorMap :: All ( self . cx . tcx . hir ( ) )
271
- }
272
- }
0 commit comments