@@ -13,6 +13,7 @@ use super::elaborate_predicates;
1313use crate :: traits:: { self , Obligation , ObligationCause } ;
1414use crate :: ty:: subst:: { InternalSubsts , Subst } ;
1515use crate :: ty:: { self , Predicate , ToPredicate , Ty , TyCtxt , TypeFoldable , WithConstness } ;
16+ use rustc_errors:: Applicability ;
1617use rustc_hir as hir;
1718use rustc_hir:: def_id:: DefId ;
1819use rustc_session:: lint:: builtin:: WHERE_CLAUSES_OBJECT_SAFETY ;
@@ -48,14 +49,20 @@ impl ObjectSafetyViolation {
4849 "it cannot use `Self` as a type parameter in the supertraits or `where`-clauses"
4950 . into ( )
5051 }
51- ObjectSafetyViolation :: Method ( name, MethodViolationCode :: StaticMethod , _) => {
52+ ObjectSafetyViolation :: Method ( name, MethodViolationCode :: StaticMethod ( _ ) , _) => {
5253 format ! ( "associated function `{}` has no `self` parameter" , name) . into ( )
5354 }
54- ObjectSafetyViolation :: Method ( name, MethodViolationCode :: ReferencesSelf , _) => format ! (
55- "method `{}` references the `Self` type in its parameters or return type" ,
55+ ObjectSafetyViolation :: Method (
5656 name,
57- )
58- . into ( ) ,
57+ MethodViolationCode :: ReferencesSelfInput ( _) ,
58+ DUMMY_SP ,
59+ ) => format ! ( "method `{}` references the `Self` type in its parameters" , name) . into ( ) ,
60+ ObjectSafetyViolation :: Method ( name, MethodViolationCode :: ReferencesSelfInput ( _) , _) => {
61+ format ! ( "method `{}` references the `Self` type in this parameter" , name) . into ( )
62+ }
63+ ObjectSafetyViolation :: Method ( name, MethodViolationCode :: ReferencesSelfOutput , _) => {
64+ format ! ( "method `{}` references the `Self` type in its return type" , name) . into ( )
65+ }
5966 ObjectSafetyViolation :: Method (
6067 name,
6168 MethodViolationCode :: WhereClauseReferencesSelf ,
@@ -78,23 +85,31 @@ impl ObjectSafetyViolation {
7885 }
7986 }
8087
81- pub fn solution ( & self ) -> Option < String > {
88+ pub fn solution ( & self ) -> Option < ( String , Option < ( String , Span ) > ) > {
8289 Some ( match * self {
8390 ObjectSafetyViolation :: SizedSelf ( _) | ObjectSafetyViolation :: SupertraitSelf => {
8491 return None ;
8592 }
86- ObjectSafetyViolation :: Method ( name, MethodViolationCode :: StaticMethod , _) => format ! (
87- "consider turning `{}` into a method by giving it a `&self` argument or \
88- constraining it with `where Self: Sized`",
89- name
93+ ObjectSafetyViolation :: Method ( name, MethodViolationCode :: StaticMethod ( sugg) , _) => (
94+ format ! (
95+ "consider turning `{}` into a method by giving it a `&self` argument or \
96+ constraining it so it does not apply to trait objects",
97+ name
98+ ) ,
99+ sugg. map ( |( sugg, sp) | ( sugg. to_string ( ) , sp) ) ,
90100 ) ,
91- ObjectSafetyViolation :: Method ( name, MethodViolationCode :: UndispatchableReceiver , _) => {
101+ ObjectSafetyViolation :: Method (
102+ name,
103+ MethodViolationCode :: UndispatchableReceiver ,
104+ span,
105+ ) => (
92106 format ! ( "consider changing method `{}`'s `self` parameter to be `&self`" , name)
93- . into ( )
94- }
107+ . into ( ) ,
108+ Some ( ( "&Self" . to_string ( ) , span) ) ,
109+ ) ,
95110 ObjectSafetyViolation :: AssocConst ( name, _)
96111 | ObjectSafetyViolation :: Method ( name, ..) => {
97- format ! ( "consider moving `{}` to another trait" , name)
112+ ( format ! ( "consider moving `{}` to another trait" , name) , None )
98113 }
99114 } )
100115 }
@@ -119,10 +134,13 @@ impl ObjectSafetyViolation {
119134#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
120135pub enum MethodViolationCode {
121136 /// e.g., `fn foo()`
122- StaticMethod ,
137+ StaticMethod ( Option < ( & ' static str , Span ) > ) ,
138+
139+ /// e.g., `fn foo(&self, x: Self)`
140+ ReferencesSelfInput ( usize ) ,
123141
124- /// e.g., `fn foo(&self, x: Self)` or `fn foo(&self ) -> Self`
125- ReferencesSelf ,
142+ /// e.g., `fn foo(&self) -> Self`
143+ ReferencesSelfOutput ,
126144
127145 /// e.g., `fn foo(&self) where Self: Clone`
128146 WhereClauseReferencesSelf ,
@@ -193,7 +211,7 @@ fn object_safety_violations_for_trait(
193211 . filter ( |item| item. kind == ty:: AssocKind :: Method )
194212 . filter_map ( |item| {
195213 object_safety_violation_for_method ( tcx, trait_def_id, & item)
196- . map ( |code| ObjectSafetyViolation :: Method ( item. ident . name , code, item . ident . span ) )
214+ . map ( |( code, span ) | ObjectSafetyViolation :: Method ( item. ident . name , code, span) )
197215 } )
198216 . filter ( |violation| {
199217 if let ObjectSafetyViolation :: Method (
@@ -224,9 +242,15 @@ fn object_safety_violations_for_trait(
224242 )
225243 } ;
226244 err. span_label ( * span, & msg) ;
227- if let ( Some ( _) , Some ( note) ) = ( node, violation. solution ( ) ) {
245+ match ( node, violation. solution ( ) ) {
246+ ( Some ( _) , Some ( ( note, None ) ) ) => {
247+ err. help ( & note) ;
248+ }
249+ ( Some ( _) , Some ( ( note, Some ( ( sugg, span) ) ) ) ) => {
250+ err. span_suggestion ( span, & note, sugg, Applicability :: MachineApplicable ) ;
251+ }
228252 // Only provide the help if its a local trait, otherwise it's not actionable.
229- err . help ( & note ) ;
253+ _ => { }
230254 }
231255 err. emit ( ) ;
232256 false
@@ -398,15 +422,34 @@ fn object_safety_violation_for_method(
398422 tcx : TyCtxt < ' _ > ,
399423 trait_def_id : DefId ,
400424 method : & ty:: AssocItem ,
401- ) -> Option < MethodViolationCode > {
425+ ) -> Option < ( MethodViolationCode , Span ) > {
402426 debug ! ( "object_safety_violation_for_method({:?}, {:?})" , trait_def_id, method) ;
403427 // Any method that has a `Self : Sized` requisite is otherwise
404428 // exempt from the regulations.
405429 if generics_require_sized_self ( tcx, method. def_id ) {
406430 return None ;
407431 }
408432
409- virtual_call_violation_for_method ( tcx, trait_def_id, method)
433+ let violation = virtual_call_violation_for_method ( tcx, trait_def_id, method) ;
434+ // Get an accurate span depending on the violation.
435+ violation. map ( |v| {
436+ let node = tcx. hir ( ) . get_if_local ( method. def_id ) ;
437+ let span = match ( v, node) {
438+ ( MethodViolationCode :: ReferencesSelfInput ( arg) , Some ( node) ) => node
439+ . fn_decl ( )
440+ . and_then ( |decl| decl. inputs . get ( arg + 1 ) )
441+ . map_or ( method. ident . span , |arg| arg. span ) ,
442+ ( MethodViolationCode :: UndispatchableReceiver , Some ( node) ) => node
443+ . fn_decl ( )
444+ . and_then ( |decl| decl. inputs . get ( 0 ) )
445+ . map_or ( method. ident . span , |arg| arg. span ) ,
446+ ( MethodViolationCode :: ReferencesSelfOutput , Some ( node) ) => {
447+ node. fn_decl ( ) . map_or ( method. ident . span , |decl| decl. output . span ( ) )
448+ }
449+ _ => method. ident . span ,
450+ } ;
451+ ( v, span)
452+ } )
410453}
411454
412455/// Returns `Some(_)` if this method cannot be called on a trait
@@ -420,18 +463,26 @@ fn virtual_call_violation_for_method<'tcx>(
420463) -> Option < MethodViolationCode > {
421464 // The method's first parameter must be named `self`
422465 if !method. method_has_self_argument {
423- return Some ( MethodViolationCode :: StaticMethod ) ;
466+ // We'll attempt to provide a structured suggestion for `Self: Sized`.
467+ let sugg =
468+ tcx. hir ( ) . get_if_local ( method. def_id ) . as_ref ( ) . and_then ( |node| node. generics ( ) ) . map (
469+ |generics| match generics. where_clause . predicates {
470+ [ ] => ( " where Self: Sized" , generics. where_clause . span ) ,
471+ [ .., pred] => ( ", Self: Sized" , pred. span ( ) . shrink_to_hi ( ) ) ,
472+ } ,
473+ ) ;
474+ return Some ( MethodViolationCode :: StaticMethod ( sugg) ) ;
424475 }
425476
426477 let sig = tcx. fn_sig ( method. def_id ) ;
427478
428- for input_ty in & sig. skip_binder ( ) . inputs ( ) [ 1 ..] {
479+ for ( i , input_ty) in sig. skip_binder ( ) . inputs ( ) [ 1 ..] . iter ( ) . enumerate ( ) {
429480 if contains_illegal_self_type_reference ( tcx, trait_def_id, input_ty) {
430- return Some ( MethodViolationCode :: ReferencesSelf ) ;
481+ return Some ( MethodViolationCode :: ReferencesSelfInput ( i ) ) ;
431482 }
432483 }
433484 if contains_illegal_self_type_reference ( tcx, trait_def_id, sig. output ( ) . skip_binder ( ) ) {
434- return Some ( MethodViolationCode :: ReferencesSelf ) ;
485+ return Some ( MethodViolationCode :: ReferencesSelfOutput ) ;
435486 }
436487
437488 // We can't monomorphize things like `fn foo<A>(...)`.
0 commit comments