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
- use crate :: utils:: { differing_macro_contexts , span_lint_and_sugg} ;
19
+ use crate :: utils:: span_lint_and_sugg;
20
20
21
21
declare_clippy_lint ! {
22
22
/// **What it does:** Checks for unnecessary repetition of structure name when a
@@ -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,182 @@ 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
+ fn span_lint_on_qpath_resolved < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , qpath : & ' tcx QPath < ' tcx > , enum_variant : bool ) {
83
+ match qpath {
84
+ QPath :: Resolved ( _, path) => {
85
+ if enum_variant {
86
+ span_lint_ignore_last_segment ( cx, path) ;
87
+ } else {
88
+ span_lint ( cx, path. span ) ;
89
+ }
90
+ } ,
91
+ _ => ( ) ,
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
93
- fn visit_ty ( & mut self , hir_ty : & ' tcx hir:: Ty < ' _ > ) {
94
- if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = & hir_ty. kind {
151
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
152
+ NestedVisitorMap :: OnlyBodies ( self . cx . tcx . hir ( ) )
153
+ }
154
+
155
+ fn visit_ty ( & mut self , hir_ty : & ' tcx hir:: Ty < ' tcx > ) {
156
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = hir_ty. kind {
95
157
match path. res {
96
158
def:: Res :: SelfTy ( ..) => { } ,
97
159
_ => {
98
160
if hir_ty_to_ty ( self . cx . tcx , hir_ty) == self . self_ty {
99
- span_use_self_lint ( self . cx , path, None ) ;
161
+ match self . cx . tcx . hir ( ) . find ( self . cx . tcx . hir ( ) . get_parent_node ( hir_ty. hir_id ) ) {
162
+ Some ( Node :: Expr ( Expr {
163
+ kind : ExprKind :: Path ( QPath :: TypeRelative ( _, last_segment) ) ,
164
+ ..
165
+ } ) ) =>
166
+ // FIXME: this span manipulation should not be necessary
167
+ // @flip1995 found an ast lowering issue in
168
+ // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#L142-L162
169
+ {
170
+ span_lint ( self . cx , hir_ty. span . with_hi ( last_segment. ident . span . lo ( ) - BytePos ( 2 ) ) )
171
+ } ,
172
+ _ => span_lint ( self . cx , hir_ty. span ) ,
173
+ }
100
174
}
101
175
} ,
102
176
}
103
177
}
104
178
105
- walk_ty ( self , hir_ty)
106
- }
107
-
108
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
109
- NestedVisitorMap :: None
179
+ walk_ty ( self , hir_ty) ;
110
180
}
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
181
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
- } ;
182
+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
183
+ fn expr_ty_matches < ' a , ' tcx > ( expr : & ' tcx Expr < ' tcx > , self_ty : Ty < ' tcx > , cx : & LateContext < ' a , ' tcx > ) -> bool {
184
+ cx. tcx
185
+ . typeck_tables_of ( expr. hir_id . owner . to_def_id ( ) )
186
+ . node_type ( expr. hir_id )
187
+ == self_ty
188
+ }
189
+ match expr. kind {
190
+ ExprKind :: Struct ( QPath :: Resolved ( _, path) , ..) => {
191
+ if expr_ty_matches ( expr, self . self_ty , self . cx ) {
192
+ match path. res {
193
+ def:: Res :: SelfTy ( ..) => ( ) ,
194
+ def:: Res :: Def ( DefKind :: Variant , _) => span_lint_ignore_last_segment ( self . cx , path) ,
195
+ _ => {
196
+ span_lint ( self . cx , path. span ) ;
197
+ } ,
198
+ }
199
+ }
200
+ } ,
201
+ // type-relative fn calls (`Foo::new()`) and tuple-like instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
202
+ ExprKind :: Call (
203
+ fun
204
+ @ Expr {
205
+ kind : ExprKind :: Path ( ref qpath) ,
206
+ ..
207
+ } ,
208
+ _,
209
+ ) => {
210
+ if expr_ty_matches ( expr, self . self_ty , self . cx ) {
211
+ let res = self . cx . tables . qpath_res ( qpath, fun. hir_id ) ;
133
212
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 } ;
213
+ if let def :: Res :: Def ( DefKind :: Ctor ( ctor_of , _ ) , .. ) = res {
214
+ match ctor_of {
215
+ def :: CtorOf :: Variant => span_lint_on_qpath_resolved ( self . cx , qpath , true ) ,
216
+ def :: CtorOf :: Struct => span_lint_on_qpath_resolved ( self . cx , qpath , false ) ,
217
+ }
218
+ } else if let def :: Res :: Def ( DefKind :: Variant , .. ) = res {
219
+ span_lint_on_qpath_resolved ( self . cx , qpath , true ) ;
220
+ }
221
+ }
222
+ } ,
223
+ // unit enum variants (`Enum::A` )
224
+ ExprKind :: Path ( ref qpath ) => {
225
+ if expr_ty_matches ( expr , self . self_ty , self . cx ) {
226
+ span_lint_on_qpath_resolved ( self . cx , qpath , true ) ;
227
+ }
228
+ } ,
229
+ _ => ( ) ,
230
+ }
231
+ walk_expr ( self , expr ) ;
232
+ }
154
233
155
- visitor. visit_ty ( & impl_hir_ty) ;
234
+ fn visit_impl_item ( & mut self , ii : & ' tcx ImplItem < ' tcx > ) {
235
+ let tcx = self . cx . tcx ;
236
+ let impl_def_id = tcx. hir ( ) . local_def_id ( self . item . hir_id ) ;
237
+ let impl_trait_ref = tcx. impl_trait_ref ( impl_def_id) ;
238
+ if_chain ! {
239
+ if let Some ( impl_trait_ref) = impl_trait_ref;
240
+ if let ImplItemKind :: Fn ( FnSig { decl: impl_decl, .. } , impl_body_id) = & ii. kind;
241
+ then {
242
+ self . check_trait_method_impl_decl( ii, impl_decl, impl_trait_ref) ;
243
+ let body = tcx. hir( ) . body( * impl_body_id) ;
244
+ self . visit_body( body) ;
245
+ } else {
246
+ walk_impl_item( self , ii)
247
+ }
156
248
}
157
249
}
158
250
}
@@ -176,97 +268,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
176
268
true
177
269
} ;
178
270
271
+ // TODO: don't short-circuit upon lifetime parameters
179
272
if should_check {
180
- let visitor = & mut UseSelfVisitor {
181
- item_path ,
273
+ let self_ty= hir_ty_to_ty ( cx . tcx , item_type ) ;
274
+ let visitor = & mut ImplVisitor {
182
275
cx,
276
+ item,
277
+ self_ty,
183
278
} ;
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
279
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
- }
280
+ for impl_item_ref in refs {
281
+ let impl_item = cx. tcx. hir( ) . impl_item( impl_item_ref. id) ;
282
+ visitor. visit_impl_item( impl_item) ;
205
283
}
206
284
}
207
285
}
208
286
}
209
287
}
210
288
}
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