1
+ use crate :: utils;
2
+ use crate :: utils:: snippet_opt;
3
+ use crate :: utils:: span_lint_and_sugg;
1
4
use if_chain:: if_chain;
2
5
use rustc_errors:: Applicability ;
3
6
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 } ;
7
+ use rustc_hir:: def:: DefKind ;
8
+ use rustc_hir:: intravisit:: { walk_expr , walk_impl_item , walk_ty, NestedVisitorMap , Visitor } ;
6
9
use rustc_hir:: {
7
- def, FnDecl , FnRetTy , FnSig , GenericArg , HirId , ImplItem , ImplItemKind , Item , ItemKind , Path , PathSegment , QPath ,
8
- TyKind ,
10
+ def, Expr , ExprKind , FnDecl , FnRetTy , FnSig , GenericArg , ImplItem , ImplItemKind , ItemKind , Node , Path , PathSegment ,
11
+ QPath , TyKind ,
9
12
} ;
10
13
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
11
14
use rustc_middle:: hir:: map:: Map ;
12
15
use rustc_middle:: lint:: in_external_macro;
13
16
use rustc_middle:: ty;
14
- use rustc_middle:: ty:: { DefIdTree , Ty } ;
15
- use rustc_semver:: RustcVersion ;
16
- use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
17
- use rustc_span:: symbol:: kw;
17
+ use rustc_middle:: ty:: Ty ;
18
+ use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
19
+ use rustc_span:: { BytePos , Span } ;
18
20
use rustc_typeck:: hir_ty_to_ty;
19
21
20
- use crate :: utils:: { differing_macro_contexts, meets_msrv, span_lint_and_sugg} ;
21
-
22
22
declare_clippy_lint ! {
23
23
/// **What it does:** Checks for unnecessary repetition of structure name when a
24
24
/// replacement with `Self` is applicable.
@@ -28,8 +28,7 @@ declare_clippy_lint! {
28
28
/// feels inconsistent.
29
29
///
30
30
/// **Known problems:**
31
- /// - False positive when using associated types ([#2843](https://github.com/rust-lang/rust-clippy/issues/2843))
32
- /// - False positives in some situations when using generics ([#3410](https://github.com/rust-lang/rust-clippy/issues/3410))
31
+ /// Unaddressed false negatives related to unresolved internal compiler errors.
33
32
///
34
33
/// **Example:**
35
34
/// ```rust
@@ -54,23 +53,11 @@ declare_clippy_lint! {
54
53
"unnecessary structure name repetition whereas `Self` is applicable"
55
54
}
56
55
57
- impl_lint_pass ! ( UseSelf => [ USE_SELF ] ) ;
56
+ declare_lint_pass ! ( UseSelf => [ USE_SELF ] ) ;
58
57
59
58
const SEGMENTS_MSG : & str = "segments should be composed of at least 1 element" ;
60
59
61
- fn span_use_self_lint ( cx : & LateContext < ' _ > , path : & Path < ' _ > , last_segment : Option < & PathSegment < ' _ > > ) {
62
- let last_segment = last_segment. unwrap_or_else ( || path. segments . last ( ) . expect ( SEGMENTS_MSG ) ) ;
63
-
64
- // Path segments only include actual path, no methods or fields.
65
- let last_path_span = last_segment. ident . span ;
66
-
67
- if differing_macro_contexts ( path. span , last_path_span) {
68
- return ;
69
- }
70
-
71
- // Only take path up to the end of last_path_span.
72
- let span = path. span . with_hi ( last_path_span. hi ( ) ) ;
73
-
60
+ fn span_lint < ' tcx > ( cx : & LateContext < ' tcx > , span : Span ) {
74
61
span_lint_and_sugg (
75
62
cx,
76
63
USE_SELF ,
@@ -82,107 +69,196 @@ fn span_use_self_lint(cx: &LateContext<'_>, path: &Path<'_>, last_segment: Optio
82
69
) ;
83
70
}
84
71
85
- // FIXME: always use this (more correct) visitor, not just in method signatures.
86
- struct SemanticUseSelfVisitor < ' a , ' tcx > {
72
+ #[ allow( clippy:: cast_possible_truncation) ]
73
+ fn span_lint_until_last_segment < ' tcx > ( cx : & LateContext < ' tcx > , span : Span , segment : & ' tcx PathSegment < ' tcx > ) {
74
+ let sp = span. with_hi ( segment. ident . span . lo ( ) ) ;
75
+ // remove the trailing ::
76
+ let span_without_last_segment = match snippet_opt ( cx, sp) {
77
+ Some ( snippet) => match snippet. rfind ( "::" ) {
78
+ Some ( bidx) => sp. with_hi ( sp. lo ( ) + BytePos ( bidx as u32 ) ) ,
79
+ None => sp,
80
+ } ,
81
+ None => sp,
82
+ } ;
83
+ span_lint ( cx, span_without_last_segment) ;
84
+ }
85
+
86
+ fn span_lint_on_path_until_last_segment < ' tcx > ( cx : & LateContext < ' tcx > , path : & ' tcx Path < ' tcx > ) {
87
+ if path. segments . len ( ) > 1 {
88
+ span_lint_until_last_segment ( cx, path. span , path. segments . last ( ) . unwrap ( ) ) ;
89
+ }
90
+ }
91
+
92
+ fn span_lint_on_qpath_resolved < ' tcx > ( cx : & LateContext < ' tcx > , qpath : & ' tcx QPath < ' tcx > , until_last_segment : bool ) {
93
+ if let QPath :: Resolved ( _, path) = qpath {
94
+ if until_last_segment {
95
+ span_lint_on_path_until_last_segment ( cx, path) ;
96
+ } else {
97
+ span_lint ( cx, path. span ) ;
98
+ }
99
+ }
100
+ }
101
+
102
+ struct ImplVisitor < ' a , ' tcx > {
87
103
cx : & ' a LateContext < ' tcx > ,
88
104
self_ty : Ty < ' tcx > ,
89
105
}
90
106
91
- impl < ' a , ' tcx > Visitor < ' tcx > for SemanticUseSelfVisitor < ' a , ' tcx > {
107
+ impl < ' a , ' tcx > ImplVisitor < ' a , ' tcx > {
108
+ fn check_trait_method_impl_decl (
109
+ & mut self ,
110
+ impl_item : & ImplItem < ' tcx > ,
111
+ impl_decl : & ' tcx FnDecl < ' tcx > ,
112
+ impl_trait_ref : ty:: TraitRef < ' tcx > ,
113
+ ) {
114
+ let tcx = self . cx . tcx ;
115
+ let trait_method = tcx
116
+ . associated_items ( impl_trait_ref. def_id )
117
+ . find_by_name_and_kind ( tcx, impl_item. ident , ty:: AssocKind :: Fn , impl_trait_ref. def_id )
118
+ . expect ( "impl method matches a trait method" ) ;
119
+
120
+ let trait_method_sig = tcx. fn_sig ( trait_method. def_id ) ;
121
+ let trait_method_sig = tcx. erase_late_bound_regions ( & trait_method_sig) ;
122
+
123
+ let output_hir_ty = if let FnRetTy :: Return ( ty) = & impl_decl. output {
124
+ Some ( & * * ty)
125
+ } else {
126
+ None
127
+ } ;
128
+
129
+ // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
130
+ // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
131
+ // We use `impl_hir_ty` to see if the type was written as `Self`,
132
+ // `hir_ty_to_ty(...)` to check semantic types of paths, and
133
+ // `trait_ty` to determine which parts of the signature in the trait, mention
134
+ // the type being implemented verbatim (as opposed to `Self`).
135
+ for ( impl_hir_ty, trait_ty) in impl_decl
136
+ . inputs
137
+ . iter ( )
138
+ . chain ( output_hir_ty)
139
+ . zip ( trait_method_sig. inputs_and_output )
140
+ {
141
+ // Check if the input/output type in the trait method specifies the implemented
142
+ // type verbatim, and only suggest `Self` if that isn't the case.
143
+ // This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
144
+ // in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
145
+ // See also https://github.com/rust-lang/rust-clippy/issues/2894.
146
+ let self_ty = impl_trait_ref. self_ty ( ) ;
147
+ if !trait_ty. walk ( ) . any ( |inner| inner == self_ty. into ( ) ) {
148
+ self . visit_ty ( & impl_hir_ty) ;
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ impl < ' a , ' tcx > Visitor < ' tcx > for ImplVisitor < ' a , ' tcx > {
92
155
type Map = Map < ' tcx > ;
93
156
94
- fn visit_ty ( & mut self , hir_ty : & ' tcx hir:: Ty < ' _ > ) {
95
- if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = & hir_ty. kind {
157
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
158
+ NestedVisitorMap :: OnlyBodies ( self . cx . tcx . hir ( ) )
159
+ }
160
+
161
+ fn visit_ty ( & mut self , hir_ty : & ' tcx hir:: Ty < ' tcx > ) {
162
+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = hir_ty. kind {
96
163
match path. res {
97
164
def:: Res :: SelfTy ( ..) => { } ,
98
165
_ => {
99
- if hir_ty_to_ty ( self . cx . tcx , hir_ty) == self . self_ty {
100
- span_use_self_lint ( self . cx , path, None ) ;
166
+ match self . cx . tcx . hir ( ) . find ( self . cx . tcx . hir ( ) . get_parent_node ( hir_ty. hir_id ) ) {
167
+ Some ( Node :: Expr ( Expr {
168
+ kind : ExprKind :: Path ( QPath :: TypeRelative ( _, _segment) ) ,
169
+ ..
170
+ } ) ) => {
171
+ // The following block correctly identifies applicable lint locations
172
+ // but `hir_ty_to_ty` calls cause odd ICEs.
173
+ //
174
+ // if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty {
175
+ // // FIXME: this span manipulation should not be necessary
176
+ // // @flip1995 found an ast lowering issue in
177
+ // // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#L142-L162
178
+ // span_lint_until_last_segment(self.cx, hir_ty.span, segment);
179
+ // }
180
+ } ,
181
+ _ => {
182
+ if hir_ty_to_ty ( self . cx . tcx , hir_ty) == self . self_ty {
183
+ span_lint ( self . cx , hir_ty. span )
184
+ }
185
+ } ,
101
186
}
102
187
} ,
103
188
}
104
189
}
105
190
106
- walk_ty ( self , hir_ty)
191
+ walk_ty ( self , hir_ty) ;
107
192
}
108
193
109
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
110
- NestedVisitorMap :: None
111
- }
112
- }
113
-
114
- fn check_trait_method_impl_decl < ' tcx > (
115
- cx : & LateContext < ' tcx > ,
116
- impl_item : & ImplItem < ' _ > ,
117
- impl_decl : & ' tcx FnDecl < ' _ > ,
118
- impl_trait_ref : ty:: TraitRef < ' tcx > ,
119
- ) {
120
- let trait_method = cx
121
- . tcx
122
- . associated_items ( impl_trait_ref. def_id )
123
- . find_by_name_and_kind ( cx. tcx , impl_item. ident , ty:: AssocKind :: Fn , impl_trait_ref. def_id )
124
- . expect ( "impl method matches a trait method" ) ;
125
-
126
- let trait_method_sig = cx. tcx . fn_sig ( trait_method. def_id ) ;
127
- let trait_method_sig = cx. tcx . erase_late_bound_regions ( trait_method_sig) ;
128
-
129
- let output_hir_ty = if let FnRetTy :: Return ( ty) = & impl_decl. output {
130
- Some ( & * * ty)
131
- } else {
132
- None
133
- } ;
134
-
135
- // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
136
- // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
137
- // We use `impl_hir_ty` to see if the type was written as `Self`,
138
- // `hir_ty_to_ty(...)` to check semantic types of paths, and
139
- // `trait_ty` to determine which parts of the signature in the trait, mention
140
- // the type being implemented verbatim (as opposed to `Self`).
141
- for ( impl_hir_ty, trait_ty) in impl_decl
142
- . inputs
143
- . iter ( )
144
- . chain ( output_hir_ty)
145
- . zip ( trait_method_sig. inputs_and_output )
146
- {
147
- // Check if the input/output type in the trait method specifies the implemented
148
- // type verbatim, and only suggest `Self` if that isn't the case.
149
- // This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
150
- // in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
151
- // See also https://github.com/rust-lang/rust-clippy/issues/2894.
152
- let self_ty = impl_trait_ref. self_ty ( ) ;
153
- if !trait_ty. walk ( ) . any ( |inner| inner == self_ty. into ( ) ) {
154
- let mut visitor = SemanticUseSelfVisitor { cx, self_ty } ;
155
-
156
- visitor. visit_ty ( & impl_hir_ty) ;
194
+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
195
+ fn expr_ty_matches < ' tcx > ( expr : & ' tcx Expr < ' tcx > , self_ty : Ty < ' tcx > , cx : & LateContext < ' tcx > ) -> bool {
196
+ let def_id = expr. hir_id . owner ;
197
+ if cx. tcx . has_typeck_results ( def_id) {
198
+ cx. tcx . typeck ( def_id) . expr_ty_opt ( expr) == Some ( self_ty)
199
+ } else {
200
+ false
201
+ }
157
202
}
158
- }
159
- }
160
-
161
- const USE_SELF_MSRV : RustcVersion = RustcVersion :: new ( 1 , 37 , 0 ) ;
162
-
163
- pub struct UseSelf {
164
- msrv : Option < RustcVersion > ,
165
- }
166
-
167
- impl UseSelf {
168
- #[ must_use]
169
- pub fn new ( msrv : Option < RustcVersion > ) -> Self {
170
- Self { msrv }
203
+ match expr. kind {
204
+ ExprKind :: Struct ( QPath :: Resolved ( _, path) , ..) => {
205
+ if expr_ty_matches ( expr, self . self_ty , self . cx ) {
206
+ match path. res {
207
+ def:: Res :: SelfTy ( ..) => ( ) ,
208
+ def:: Res :: Def ( DefKind :: Variant , _) => span_lint_on_path_until_last_segment ( self . cx , path) ,
209
+ _ => {
210
+ span_lint ( self . cx , path. span ) ;
211
+ } ,
212
+ }
213
+ }
214
+ } ,
215
+ // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
216
+ ExprKind :: Call ( fun, _) => {
217
+ if let Expr {
218
+ kind : ExprKind :: Path ( ref qpath) ,
219
+ ..
220
+ } = fun
221
+ {
222
+ if expr_ty_matches ( expr, self . self_ty , self . cx ) {
223
+ let res = utils:: qpath_res ( self . cx , qpath, fun. hir_id ) ;
224
+
225
+ if let def:: Res :: Def ( DefKind :: Ctor ( ctor_of, _) , ..) = res {
226
+ match ctor_of {
227
+ def:: CtorOf :: Variant => {
228
+ span_lint_on_qpath_resolved ( self . cx , qpath, true ) ;
229
+ } ,
230
+ def:: CtorOf :: Struct => {
231
+ span_lint_on_qpath_resolved ( self . cx , qpath, false ) ;
232
+ } ,
233
+ }
234
+ }
235
+ }
236
+ }
237
+ } ,
238
+ // unit enum variants (`Enum::A`)
239
+ ExprKind :: Path ( ref qpath) => {
240
+ if expr_ty_matches ( expr, self . self_ty , self . cx ) {
241
+ span_lint_on_qpath_resolved ( self . cx , qpath, true ) ;
242
+ }
243
+ } ,
244
+ _ => ( ) ,
245
+ }
246
+ walk_expr ( self , expr) ;
171
247
}
172
248
}
173
249
174
250
impl < ' tcx > LateLintPass < ' tcx > for UseSelf {
175
- fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
176
- if ! meets_msrv ( self . msrv . as_ref ( ) , & USE_SELF_MSRV ) {
251
+ fn check_impl_item ( & mut self , cx : & LateContext < ' tcx > , impl_item : & ' tcx ImplItem < ' _ > ) {
252
+ if in_external_macro ( cx . sess ( ) , impl_item . span ) {
177
253
return ;
178
254
}
179
255
180
- if in_external_macro ( cx. sess ( ) , item . span ) {
181
- return ;
182
- }
256
+ let parent_id = cx. tcx . hir ( ) . get_parent_item ( impl_item . hir_id ) ;
257
+ let imp = cx . tcx . hir ( ) . expect_item ( parent_id ) ;
258
+
183
259
if_chain ! {
184
- if let ItemKind :: Impl ( impl_ ) = & item . kind;
185
- if let TyKind :: Path ( QPath :: Resolved ( _, ref item_path) ) = impl_ . self_ty . kind;
260
+ if let ItemKind :: Impl { self_ty : hir_self_ty , .. } = imp . kind;
261
+ if let TyKind :: Path ( QPath :: Resolved ( _, ref item_path) ) = hir_self_ty . kind;
186
262
then {
187
263
let parameters = & item_path. segments. last( ) . expect( SEGMENTS_MSG ) . args;
188
264
let should_check = parameters. as_ref( ) . map_or(
@@ -191,31 +267,23 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
191
267
&&!params. args. iter( ) . any( |arg| matches!( arg, GenericArg :: Lifetime ( _) ) )
192
268
) ;
193
269
270
+ // TODO: don't short-circuit upon lifetime parameters
194
271
if should_check {
195
- let visitor = & mut UseSelfVisitor {
196
- item_path,
197
- cx,
198
- } ;
199
- let impl_def_id = cx. tcx. hir( ) . local_def_id( item. hir_id) ;
200
- let impl_trait_ref = cx. tcx. impl_trait_ref( impl_def_id) ;
201
-
202
- if let Some ( impl_trait_ref) = impl_trait_ref {
203
- for impl_item_ref in impl_. items {
204
- let impl_item = cx. tcx. hir( ) . impl_item( impl_item_ref. id) ;
205
- if let ImplItemKind :: Fn ( FnSig { decl: impl_decl, .. } , impl_body_id)
206
- = & impl_item. kind {
207
- check_trait_method_impl_decl( cx, impl_item, impl_decl, impl_trait_ref) ;
208
-
209
- let body = cx. tcx. hir( ) . body( * impl_body_id) ;
210
- visitor. visit_body( body) ;
211
- } else {
212
- visitor. visit_impl_item( impl_item) ;
213
- }
214
- }
215
- } else {
216
- for impl_item_ref in impl_. items {
217
- let impl_item = cx. tcx. hir( ) . impl_item( impl_item_ref. id) ;
218
- visitor. visit_impl_item( impl_item) ;
272
+ let self_ty = hir_ty_to_ty( cx. tcx, hir_self_ty) ;
273
+ let visitor = & mut ImplVisitor { cx, self_ty } ;
274
+
275
+ let tcx = cx. tcx;
276
+ let impl_def_id = tcx. hir( ) . local_def_id( imp. hir_id) ;
277
+ let impl_trait_ref = tcx. impl_trait_ref( impl_def_id) ;
278
+ if_chain! {
279
+ if let Some ( impl_trait_ref) = impl_trait_ref;
280
+ if let ImplItemKind :: Fn ( FnSig { decl: impl_decl, .. } , impl_body_id) = & impl_item. kind;
281
+ then {
282
+ visitor. check_trait_method_impl_decl( impl_item, impl_decl, impl_trait_ref) ;
283
+ let body = tcx. hir( ) . body( * impl_body_id) ;
284
+ visitor. visit_body( body) ;
285
+ } else {
286
+ walk_impl_item( visitor, impl_item)
219
287
}
220
288
}
221
289
}
0 commit comments