@@ -10,10 +10,10 @@ use rustc::ty::{
1010 self , AdtKind , GenericParamDefKind , ToPredicate , Ty , TyCtxt , TypeFoldable , WithConstness ,
1111} ;
1212use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
13- use rustc_errors:: { struct_span_err, DiagnosticBuilder } ;
13+ use rustc_errors:: { struct_span_err, Applicability , DiagnosticBuilder } ;
1414use rustc_hir:: def_id:: DefId ;
1515use rustc_hir:: ItemKind ;
16- use rustc_span:: symbol:: sym;
16+ use rustc_span:: symbol:: { sym, Ident } ;
1717use rustc_span:: Span ;
1818use syntax:: ast;
1919
@@ -176,9 +176,74 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: DefId) {
176176 hir:: TraitItemKind :: Method ( ref sig, _) => Some ( sig) ,
177177 _ => None ,
178178 } ;
179+ check_bare_self_trait_by_name ( tcx, & trait_item) ;
179180 check_associated_item ( tcx, trait_item. hir_id , trait_item. span , method_sig) ;
180181}
181182
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+
182247pub fn check_impl_item ( tcx : TyCtxt < ' _ > , def_id : DefId ) {
183248 let hir_id = tcx. hir ( ) . as_local_hir_id ( def_id) . unwrap ( ) ;
184249 let impl_item = tcx. hir ( ) . expect_impl_item ( hir_id) ;
0 commit comments