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:: def:: DefKind ;
5
+ use rustc_hir:: intravisit:: { walk_expr , walk_impl_item , walk_ty, NestedVisitorMap , Visitor } ;
6
6
use rustc_hir:: {
7
- def, FnDecl , FnRetTy , FnSig , GenericArg , HirId , ImplItem , ImplItemKind , Item , ItemKind , Path , PathSegment , QPath ,
7
+ def, Expr , ExprKind , FnDecl , FnRetTy , FnSig , GenericArg , ImplItem , ImplItemKind , Item , ItemKind , Node , Path , QPath ,
8
8
TyKind ,
9
9
} ;
10
10
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
11
11
use rustc_middle:: hir:: map:: Map ;
12
12
use rustc_middle:: lint:: in_external_macro;
13
13
use rustc_middle:: ty;
14
- use rustc_middle:: ty:: { DefIdTree , Ty } ;
14
+ use rustc_middle:: ty:: Ty ;
15
15
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
16
- use rustc_span:: symbol :: kw ;
16
+ use rustc_span:: { BytePos , Span } ;
17
17
use rustc_typeck:: hir_ty_to_ty;
18
18
19
19
use crate :: utils:: { differing_macro_contexts, span_lint_and_sugg} ;
@@ -57,19 +57,7 @@ declare_lint_pass!(UseSelf => [USE_SELF]);
57
57
58
58
const SEGMENTS_MSG : & str = "segments should be composed of at least 1 element" ;
59
59
60
- fn span_use_self_lint ( cx : & LateContext < ' _ , ' _ > , path : & Path < ' _ > , last_segment : Option < & PathSegment < ' _ > > ) {
61
- let last_segment = last_segment. unwrap_or_else ( || path. segments . last ( ) . expect ( SEGMENTS_MSG ) ) ;
62
-
63
- // Path segments only include actual path, no methods or fields.
64
- let last_path_span = last_segment. ident . span ;
65
-
66
- if differing_macro_contexts ( path. span , last_path_span) {
67
- return ;
68
- }
69
-
70
- // Only take path up to the end of last_path_span.
71
- let span = path. span . with_hi ( last_path_span. hi ( ) ) ;
72
-
60
+ fn span_lint < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , span : Span ) {
73
61
span_lint_and_sugg (
74
62
cx,
75
63
USE_SELF ,
@@ -81,78 +69,170 @@ fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path<'_>, last_segment: O
81
69
) ;
82
70
}
83
71
84
- // FIXME: always use this (more correct) visitor, not just in method signatures.
85
- struct SemanticUseSelfVisitor < ' a , ' tcx > {
72
+ fn span_lint_ignore_last_segment < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , path : & ' tcx Path < ' tcx > ) {
73
+ if path. segments . len ( ) > 1 {
74
+ span_lint (
75
+ cx,
76
+ path. span
77
+ . with_hi ( path. segments [ path. segments . len ( ) - 1 ] . ident . span . lo ( ) - BytePos ( 2 ) ) ,
78
+ )
79
+ }
80
+ }
81
+
82
+ struct ImplVisitor < ' a , ' tcx > {
86
83
cx : & ' a LateContext < ' a , ' tcx > ,
84
+ item : & ' tcx Item < ' tcx > ,
87
85
self_ty : Ty < ' tcx > ,
88
86
}
89
87
90
- impl < ' a , ' tcx > Visitor < ' tcx > for SemanticUseSelfVisitor < ' a , ' tcx > {
88
+ impl < ' a , ' tcx > ImplVisitor < ' a , ' tcx > {
89
+ fn check_trait_method_impl_decl (
90
+ & mut self ,
91
+ impl_item : & ImplItem < ' tcx > ,
92
+ impl_decl : & ' tcx FnDecl < ' tcx > ,
93
+ impl_trait_ref : ty:: TraitRef < ' tcx > ,
94
+ ) {
95
+ let tcx = self . cx . tcx ;
96
+ let trait_method = tcx
97
+ . associated_items ( impl_trait_ref. def_id )
98
+ . find_by_name_and_kind ( tcx, impl_item. ident , ty:: AssocKind :: Fn , impl_trait_ref. def_id )
99
+ . expect ( "impl method matches a trait method" ) ;
100
+
101
+ let trait_method_sig = tcx. fn_sig ( trait_method. def_id ) ;
102
+ let trait_method_sig = tcx. erase_late_bound_regions ( & trait_method_sig) ;
103
+
104
+ let output_hir_ty = if let FnRetTy :: Return ( ty) = & impl_decl. output {
105
+ Some ( & * * ty)
106
+ } else {
107
+ None
108
+ } ;
109
+
110
+ // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
111
+ // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
112
+ // We use `impl_hir_ty` to see if the type was written as `Self`,
113
+ // `hir_ty_to_ty(...)` to check semantic types of paths, and
114
+ // `trait_ty` to determine which parts of the signature in the trait, mention
115
+ // the type being implemented verbatim (as opposed to `Self`).
116
+ for ( impl_hir_ty, trait_ty) in impl_decl
117
+ . inputs
118
+ . iter ( )
119
+ . chain ( output_hir_ty)
120
+ . zip ( trait_method_sig. inputs_and_output )
121
+ {
122
+ // Check if the input/output type in the trait method specifies the implemented
123
+ // type verbatim, and only suggest `Self` if that isn't the case.
124
+ // This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
125
+ // in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
126
+ // See also https://github.com/rust-lang/rust-clippy/issues/2894.
127
+ let self_ty = impl_trait_ref. self_ty ( ) ;
128
+ if !trait_ty. walk ( ) . any ( |inner| inner == self_ty. into ( ) ) {
129
+ self . visit_ty ( & impl_hir_ty) ;
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ impl < ' a , ' tcx > Visitor < ' tcx > for ImplVisitor < ' a , ' tcx > {
91
136
type Map = Map < ' tcx > ;
92
137
93
- fn visit_ty ( & mut self , hir_ty : & ' tcx hir:: Ty < ' _ > ) {
94
- if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = & hir_ty. kind {
138
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
139
+ NestedVisitorMap :: OnlyBodies ( self . cx . tcx . hir ( ) )
140
+ }
141
+
142
+ fn visit_ty ( & mut self , hir_ty : & ' tcx hir:: Ty < ' tcx > ) {
143
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = hir_ty. kind {
95
144
match path. res {
96
145
def:: Res :: SelfTy ( ..) => { } ,
97
146
_ => {
98
147
if hir_ty_to_ty ( self . cx . tcx , hir_ty) == self . self_ty {
99
- span_use_self_lint ( self . cx , path, None ) ;
148
+ match self . cx . tcx . hir ( ) . find ( self . cx . tcx . hir ( ) . get_parent_node ( hir_ty. hir_id ) ) {
149
+ Some ( Node :: Expr ( Expr {
150
+ kind : ExprKind :: Path ( QPath :: TypeRelative ( _, last_segment) ) ,
151
+ ..
152
+ } ) ) =>
153
+ // FIXME: this span manipulation should not be necessary
154
+ // @flip1995 found an ast lowering issue in
155
+ // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#L142-L162
156
+ {
157
+ span_lint ( self . cx , hir_ty. span . with_hi ( last_segment. ident . span . lo ( ) - BytePos ( 2 ) ) )
158
+ } ,
159
+ _ => span_lint ( self . cx , hir_ty. span ) ,
160
+ }
100
161
}
101
162
} ,
102
163
}
103
164
}
104
165
105
- walk_ty ( self , hir_ty)
166
+ walk_ty ( self , hir_ty) ;
106
167
}
107
168
108
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
109
- NestedVisitorMap :: None
169
+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
170
+ match expr. kind {
171
+ ExprKind :: Struct ( QPath :: Resolved ( _, path) , ..) => {
172
+ if self
173
+ . cx
174
+ . tcx
175
+ . typeck_tables_of ( expr. hir_id . owner . to_def_id ( ) )
176
+ . node_type ( expr. hir_id )
177
+ == self . self_ty
178
+ {
179
+ match path. res {
180
+ def:: Res :: SelfTy ( ..) => ( ) ,
181
+ def:: Res :: Def ( DefKind :: Variant , _) => span_lint_ignore_last_segment ( self . cx , path) ,
182
+ _ => {
183
+ span_lint ( self . cx , path. span ) ;
184
+ } ,
185
+ }
186
+ }
187
+ } ,
188
+ ExprKind :: Call (
189
+ Expr {
190
+ kind :
191
+ ExprKind :: Path ( QPath :: Resolved (
192
+ _,
193
+ path
194
+ @
195
+ Path {
196
+ res : def:: Res :: Def ( def:: DefKind :: Ctor ( ctor_of, _) , _) ,
197
+ ..
198
+ } ,
199
+ ) ) ,
200
+ ..
201
+ } ,
202
+ _,
203
+ ) => {
204
+ if self
205
+ . cx
206
+ . tcx
207
+ . typeck_tables_of ( expr. hir_id . owner . to_def_id ( ) )
208
+ . node_type ( expr. hir_id )
209
+ == self . self_ty
210
+ {
211
+ match ctor_of {
212
+ def:: CtorOf :: Struct => span_lint ( self . cx , path. span ) ,
213
+ def:: CtorOf :: Variant => span_lint_ignore_last_segment ( self . cx , path) ,
214
+ }
215
+ }
216
+ } ,
217
+ _ => ( ) ,
218
+ }
219
+ walk_expr ( self , expr) ;
110
220
}
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
221
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) ;
222
+ fn visit_impl_item ( & mut self , ii : & ' tcx ImplItem < ' tcx > ) {
223
+ let tcx = self . cx . tcx ;
224
+ let impl_def_id = tcx. hir ( ) . local_def_id ( self . item . hir_id ) ;
225
+ let impl_trait_ref = tcx. impl_trait_ref ( impl_def_id) ;
226
+ if_chain ! {
227
+ if let Some ( impl_trait_ref) = impl_trait_ref;
228
+ if let ImplItemKind :: Fn ( FnSig { decl: impl_decl, .. } , impl_body_id) = & ii. kind;
229
+ then {
230
+ self . check_trait_method_impl_decl( ii, impl_decl, impl_trait_ref) ;
231
+ let body = tcx. hir( ) . body( * impl_body_id) ;
232
+ self . visit_body( body) ;
233
+ } else {
234
+ walk_impl_item( self , ii)
235
+ }
156
236
}
157
237
}
158
238
}
@@ -176,97 +256,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
176
256
true
177
257
} ;
178
258
259
+ // TODO: don't short-circuit upon lifetime parameters
179
260
if should_check {
180
- let visitor = & mut UseSelfVisitor {
181
- item_path ,
261
+ let self_ty= hir_ty_to_ty ( cx . tcx , item_type ) ;
262
+ let visitor = & mut ImplVisitor {
182
263
cx,
264
+ item,
265
+ self_ty,
183
266
} ;
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
267
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 ) ;
268
+ for impl_item_ref in refs {
269
+ let impl_item = cx. tcx. hir( ) . impl_item( impl_item_ref. id) ;
270
+ visitor. visit_impl_item( impl_item) ;
246
271
}
247
272
}
248
273
}
249
274
}
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
275
}
272
276
}
0 commit comments