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 ,
7
+ PathSegment , 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;
17
15
use rustc_typeck:: hir_ty_to_ty;
16
+ use rustc_span:: { Span } ;
18
17
19
18
use crate :: utils:: { differing_macro_contexts, span_lint_and_sugg} ;
20
19
@@ -81,22 +80,96 @@ 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
+ fn simple_span ( cx : & LateContext < ' _ , ' _ > , span : Span ) {
84
+ span_lint_and_sugg (
85
+ cx,
86
+ USE_SELF ,
87
+ span,
88
+ "unnecessary structure name repetition" ,
89
+ "use the applicable keyword" ,
90
+ "Self" . to_owned ( ) ,
91
+ Applicability :: MachineApplicable ,
92
+ ) ;
93
+ }
94
+
95
+ struct ImplVisitor < ' a , ' tcx > {
86
96
cx : & ' a LateContext < ' a , ' tcx > ,
97
+ item : & ' tcx Item < ' tcx > ,
87
98
self_ty : Ty < ' tcx > ,
88
99
}
89
100
90
- impl < ' a , ' tcx > Visitor < ' tcx > for SemanticUseSelfVisitor < ' a , ' tcx > {
101
+ impl < ' a , ' tcx > ImplVisitor < ' a , ' tcx > {
102
+ fn check_trait_method_impl_decl (
103
+ & mut self ,
104
+ impl_item : & ImplItem < ' tcx > ,
105
+ impl_decl : & ' tcx FnDecl < ' tcx > ,
106
+ impl_trait_ref : ty:: TraitRef < ' tcx > ,
107
+ ) {
108
+ let tcx = self . cx . tcx ;
109
+ let trait_method = tcx
110
+ . associated_items ( impl_trait_ref. def_id )
111
+ . find_by_name_and_kind ( tcx, impl_item. ident , ty:: AssocKind :: Fn , impl_trait_ref. def_id )
112
+ . expect ( "impl method matches a trait method" ) ;
113
+
114
+ let trait_method_sig = tcx. fn_sig ( trait_method. def_id ) ;
115
+ let trait_method_sig = tcx. erase_late_bound_regions ( & trait_method_sig) ;
116
+
117
+ let output_hir_ty = if let FnRetTy :: Return ( ty) = & impl_decl. output {
118
+ Some ( & * * ty)
119
+ } else {
120
+ None
121
+ } ;
122
+
123
+ // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
124
+ // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
125
+ // We use `impl_hir_ty` to see if the type was written as `Self`,
126
+ // `hir_ty_to_ty(...)` to check semantic types of paths, and
127
+ // `trait_ty` to determine which parts of the signature in the trait, mention
128
+ // the type being implemented verbatim (as opposed to `Self`).
129
+ for ( impl_hir_ty, trait_ty) in impl_decl
130
+ . inputs
131
+ . iter ( )
132
+ . chain ( output_hir_ty)
133
+ . zip ( trait_method_sig. inputs_and_output )
134
+ {
135
+ // Check if the input/output type in the trait method specifies the implemented
136
+ // type verbatim, and only suggest `Self` if that isn't the case.
137
+ // This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
138
+ // in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
139
+ // See also https://github.com/rust-lang/rust-clippy/issues/2894.
140
+ let self_ty = impl_trait_ref. self_ty ( ) ;
141
+ if !trait_ty. walk ( ) . any ( |inner| inner == self_ty. into ( ) ) {
142
+ self . visit_ty ( & impl_hir_ty) ;
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ impl < ' a , ' tcx > Visitor < ' tcx > for ImplVisitor < ' a , ' tcx > {
91
149
type Map = Map < ' tcx > ;
92
150
151
+ fn visit_expr ( & mut self , ex : & ' tcx Expr < ' tcx > ) {
152
+ if let ExprKind :: Struct ( QPath :: Resolved ( _, _path) , ..) = ex. kind {
153
+ // todo: this doesn't work, `expr_ty_opt` returns None
154
+
155
+ // if self.cx.tcx.body.expr_ty_opt(ex) == self.self_ty {
156
+ // span_use_self_lint(self.cx, path, None);
157
+ // }
158
+ }
159
+ walk_expr ( self , ex)
160
+ }
161
+
162
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
163
+ NestedVisitorMap :: OnlyBodies ( self . cx . tcx . hir ( ) )
164
+ }
165
+
93
166
fn visit_ty ( & mut self , hir_ty : & ' tcx hir:: Ty < ' _ > ) {
94
167
if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = & hir_ty. kind {
95
168
match path. res {
96
169
def:: Res :: SelfTy ( ..) => { } ,
97
170
_ => {
98
171
if hir_ty_to_ty ( self . cx . tcx , hir_ty) == self . self_ty {
99
- span_use_self_lint ( self . cx , path , None ) ;
172
+ simple_span ( self . cx , hir_ty . span ) ;
100
173
}
101
174
} ,
102
175
}
@@ -105,54 +178,20 @@ impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> {
105
178
walk_ty ( self , hir_ty)
106
179
}
107
180
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) ;
181
+ fn visit_impl_item ( & mut self , ii : & ' tcx ImplItem < ' tcx > ) {
182
+ let tcx = self . cx . tcx ;
183
+ let impl_def_id = tcx. hir ( ) . local_def_id ( self . item . hir_id ) ;
184
+ let impl_trait_ref = tcx. impl_trait_ref ( impl_def_id) ;
185
+ if_chain ! {
186
+ if let Some ( impl_trait_ref) = impl_trait_ref;
187
+ if let ImplItemKind :: Fn ( FnSig { decl: impl_decl, .. } , impl_body_id) = & ii. kind;
188
+ then {
189
+ self . check_trait_method_impl_decl( ii, impl_decl, impl_trait_ref) ;
190
+ let body = tcx. hir( ) . body( * impl_body_id) ;
191
+ self . visit_body( body) ;
192
+ } else {
193
+ walk_impl_item( self , ii)
194
+ }
156
195
}
157
196
}
158
197
}
@@ -177,96 +216,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
177
216
} ;
178
217
179
218
if should_check {
180
- let visitor = & mut UseSelfVisitor {
181
- item_path ,
219
+ let self_ty= hir_ty_to_ty ( cx . tcx , item_type ) ;
220
+ let visitor = & mut ImplVisitor {
182
221
cx,
222
+ item,
223
+ self_ty,
183
224
} ;
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
-
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
- }
205
- }
206
- }
207
- }
208
- }
209
- }
210
- }
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
225
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 ) ;
226
+ for impl_item_ref in refs {
227
+ let impl_item = cx. tcx. hir( ) . impl_item( impl_item_ref. id) ;
228
+ visitor. visit_impl_item( impl_item) ;
246
229
}
247
230
}
248
231
}
249
232
}
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
233
}
272
234
}
0 commit comments