@@ -10,10 +10,10 @@ use rustc::ty::{
10
10
self , AdtKind , GenericParamDefKind , ToPredicate , Ty , TyCtxt , TypeFoldable , WithConstness ,
11
11
} ;
12
12
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
13
- use rustc_errors:: { struct_span_err, DiagnosticBuilder } ;
13
+ use rustc_errors:: { struct_span_err, Applicability , DiagnosticBuilder } ;
14
14
use rustc_hir:: def_id:: DefId ;
15
15
use rustc_hir:: ItemKind ;
16
- use rustc_span:: symbol:: sym;
16
+ use rustc_span:: symbol:: { sym, Ident } ;
17
17
use rustc_span:: Span ;
18
18
use syntax:: ast;
19
19
@@ -176,9 +176,74 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: DefId) {
176
176
hir:: TraitItemKind :: Method ( ref sig, _) => Some ( sig) ,
177
177
_ => None ,
178
178
} ;
179
+ check_bare_self_trait_by_name ( tcx, & trait_item) ;
179
180
check_associated_item ( tcx, trait_item. hir_id , trait_item. span , method_sig) ;
180
181
}
181
182
183
+ fn could_be_self ( trait_name : Ident , ty : & hir:: Ty < ' _ > ) -> bool {
184
+ match ty. kind {
185
+ hir:: TyKind :: TraitObject ( [ trait_ref] , ..) => {
186
+ let mut p = trait_ref. trait_ref . path . segments . iter ( ) . map ( |s| s. ident ) ;
187
+ match ( p. next ( ) , p. next ( ) ) {
188
+ ( Some ( ident) , None ) => ident == trait_name,
189
+ _ => false ,
190
+ }
191
+ }
192
+ _ => false ,
193
+ }
194
+ }
195
+
196
+ /// Detect when an object unsafe trait is referring to itself in one of its associated items.
197
+ /// When this is done, suggest using `Self` instead.
198
+ fn check_bare_self_trait_by_name ( tcx : TyCtxt < ' _ > , item : & hir:: TraitItem < ' _ > ) {
199
+ let ( trait_name, trait_def_id) = match tcx. hir ( ) . get ( tcx. hir ( ) . get_parent_item ( item. hir_id ) ) {
200
+ hir:: Node :: Item ( item) => match item. kind {
201
+ hir:: ItemKind :: Trait ( ..) => ( item. ident , tcx. hir ( ) . local_def_id ( item. hir_id ) ) ,
202
+ _ => return ,
203
+ } ,
204
+ _ => return ,
205
+ } ;
206
+ let mut trait_should_be_self = vec ! [ ] ;
207
+ match & item. kind {
208
+ hir:: TraitItemKind :: Const ( ty, _) | hir:: TraitItemKind :: Type ( _, Some ( ty) )
209
+ if could_be_self ( trait_name, ty) =>
210
+ {
211
+ trait_should_be_self. push ( ty. span )
212
+ }
213
+ hir:: TraitItemKind :: Method ( sig, _) => {
214
+ for ty in sig. decl . inputs {
215
+ if could_be_self ( trait_name, ty) {
216
+ trait_should_be_self. push ( ty. span ) ;
217
+ }
218
+ }
219
+ match sig. decl . output {
220
+ hir:: FunctionRetTy :: Return ( ty) if could_be_self ( trait_name, ty) => {
221
+ trait_should_be_self. push ( ty. span ) ;
222
+ }
223
+ _ => { }
224
+ }
225
+ }
226
+ _ => { }
227
+ }
228
+ if !trait_should_be_self. is_empty ( ) {
229
+ if rustc:: traits:: object_safety_violations ( tcx, trait_def_id) . is_empty ( ) {
230
+ return ;
231
+ }
232
+ let sugg = trait_should_be_self. iter ( ) . map ( |span| ( * span, "Self" . to_string ( ) ) ) . collect ( ) ;
233
+ let mut err = tcx. sess . struct_span_err (
234
+ trait_should_be_self,
235
+ "associated item referring to unboxed trait object for its own trait" ,
236
+ ) ;
237
+ err. span_label ( trait_name. span , "in this trait" ) ;
238
+ err. multipart_suggestion (
239
+ "you might have meant to use `Self` to refer to the materialized type" ,
240
+ sugg,
241
+ Applicability :: MachineApplicable ,
242
+ ) ;
243
+ err. emit ( ) ;
244
+ }
245
+ }
246
+
182
247
pub fn check_impl_item ( tcx : TyCtxt < ' _ > , def_id : DefId ) {
183
248
let hir_id = tcx. hir ( ) . as_local_hir_id ( def_id) . unwrap ( ) ;
184
249
let impl_item = tcx. hir ( ) . expect_impl_item ( hir_id) ;
0 commit comments