@@ -149,7 +149,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
149149 && let predicates = cx. tcx . super_predicates_of ( trait_def_id) . predicates
150150 && !predicates. is_empty ( ) // If the trait has no supertrait, there is nothing to add.
151151 {
152- Some ( ( bound. span ( ) , path. args . map_or ( [ ] . as_slice ( ) , |a| a . args ) , predicates, trait_def_id) )
152+ Some ( ( bound. span ( ) , path, predicates, trait_def_id) )
153153 } else {
154154 None
155155 }
@@ -162,10 +162,14 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
162162 if let GenericBound :: Trait ( poly_trait, TraitBoundModifier :: None ) = bound
163163 && let [ .., path] = poly_trait. trait_ref . path . segments
164164 && let implied_args = path. args . map_or ( [ ] . as_slice ( ) , |a| a. args )
165+ && let implied_bindings = path. args . map_or ( [ ] . as_slice ( ) , |a| a. bindings )
165166 && let Some ( def_id) = poly_trait. trait_ref . path . res . opt_def_id ( )
166- && let Some ( implied_by_span) = implied_bounds
167+ && let Some ( ( implied_by_span, implied_by_args , implied_by_bindings ) ) = implied_bounds
167168 . iter ( )
168- . find_map ( |& ( span, implied_by_args, preds, implied_by_def_id) | {
169+ . find_map ( |& ( span, implied_by_path, preds, implied_by_def_id) | {
170+ let implied_by_args = implied_by_path. args . map_or ( [ ] . as_slice ( ) , |a| a. args ) ;
171+ let implied_by_bindings = implied_by_path. args . map_or ( [ ] . as_slice ( ) , |a| a. bindings ) ;
172+
169173 preds. iter ( ) . find_map ( |( clause, _) | {
170174 if let ClauseKind :: Trait ( tr) = clause. kind ( ) . skip_binder ( )
171175 && tr. def_id ( ) == def_id
@@ -178,7 +182,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
178182 def_id,
179183 )
180184 {
181- Some ( span)
185+ Some ( ( span, implied_by_args , implied_by_bindings ) )
182186 } else {
183187 None
184188 }
@@ -192,21 +196,72 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
192196 & format ! ( "this bound is already specified as the supertrait of `{implied_by}`" ) ,
193197 |diag| {
194198 // If we suggest removing a bound, we may also need extend the span
195- // to include the `+` token, so we don't end up with something like `impl + B`
199+ // to include the `+` token that is ahead or behind,
200+ // so we don't end up with something like `impl + B` or `impl A + `
196201
197202 let implied_span_extended = if let Some ( next_bound) = opaque_ty. bounds . get ( index + 1 ) {
198203 poly_trait. span . to ( next_bound. span ( ) . shrink_to_lo ( ) )
204+ } else if index > 0
205+ && let Some ( prev_bound) = opaque_ty. bounds . get ( index - 1 )
206+ {
207+ prev_bound. span ( ) . shrink_to_hi ( ) . to ( poly_trait. span . shrink_to_hi ( ) )
199208 } else {
200209 poly_trait. span
201210 } ;
202211
203- diag. span_suggestion_with_style (
204- implied_span_extended,
205- "try removing this bound" ,
206- "" ,
207- Applicability :: MachineApplicable ,
208- SuggestionStyle :: ShowAlways
209- ) ;
212+ let mut sugg = vec ! [
213+ ( implied_span_extended, String :: new( ) ) ,
214+ ] ;
215+
216+ // We also might need to include associated type binding that were specified in the implied bound,
217+ // but omitted in the implied-by bound:
218+ // `fn f() -> impl Deref<Target = u8> + DerefMut`
219+ // If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
220+ let omitted_assoc_tys: Vec < _ > = implied_bindings
221+ . iter ( )
222+ . filter ( |binding| {
223+ implied_by_bindings
224+ . iter ( )
225+ // TODO: is checking idents enough for stuff like `<Target: Sized> == <Target = u8>`
226+ . find ( |b| b. ident == binding. ident )
227+ . is_none ( )
228+ } )
229+ . collect ( ) ;
230+
231+ if !omitted_assoc_tys. is_empty ( ) {
232+ // `<>` needs to be added if there aren't yet any generic arguments or bindings
233+ let needs_angle_brackets = implied_by_args. is_empty ( ) && implied_by_bindings. is_empty ( ) ;
234+ let insert_span = match ( implied_by_args, implied_by_bindings) {
235+ ( [ .., arg] , [ .., binding] ) => arg. span ( ) . max ( binding. span ) . shrink_to_hi ( ) ,
236+ ( [ .., arg] , [ ] ) => arg. span ( ) . shrink_to_hi ( ) ,
237+ ( [ ] , [ .., binding] ) => binding. span . shrink_to_hi ( ) ,
238+ ( [ ] , [ ] ) => implied_by_span. shrink_to_hi ( ) ,
239+ } ;
240+
241+ let mut associated_tys_sugg = if needs_angle_brackets {
242+ "<" . to_owned ( )
243+ } else {
244+ // If angle brackets aren't needed (i.e., there are already generic arguments or bindings),
245+ // we need to add a comma:
246+ // `impl A<B, C >`
247+ // ^ if we insert `Assoc=i32` without a comma here, that'd be invalid syntax:
248+ // `impl A<B, C Assoc=i32>`
249+ ", " . to_owned ( )
250+ } ;
251+
252+ for ( index, binding) in omitted_assoc_tys. into_iter ( ) . enumerate ( ) {
253+ if index > 0 {
254+ associated_tys_sugg += ", " ;
255+ }
256+ associated_tys_sugg += & snippet ( cx, binding. span , ".." ) ;
257+ }
258+ if needs_angle_brackets {
259+ associated_tys_sugg += ">" ;
260+ }
261+ sugg. push ( ( insert_span, associated_tys_sugg) ) ;
262+ }
263+
264+ diag. multipart_suggestion_with_style ( "try removing this bound" , sugg, Applicability :: MachineApplicable , SuggestionStyle :: ShowAlways ) ;
210265 }
211266 ) ;
212267 }
0 commit comments