@@ -5,8 +5,10 @@ use ide_db::{
55 label:: Label ,
66 source_change:: SourceChangeBuilder ,
77} ;
8- use syntax:: ToSmolStr ;
9- use syntax:: ast:: edit:: AstNodeEdit ;
8+ use syntax:: {
9+ AstNode , ToSmolStr ,
10+ ast:: { HasName , edit:: AstNodeEdit } ,
11+ } ;
1012
1113use crate :: { Diagnostic , DiagnosticCode , DiagnosticsContext } ;
1214
@@ -82,16 +84,18 @@ fn quickfix_for_redundant_assoc_item(
8284 let db = ctx. sema . db ;
8385 let root = db. parse_or_expand ( d. file_id ) ;
8486 // don't modify trait def in outer crate
85- let current_crate = ctx. sema . scope ( & d. impl_ . syntax_node_ptr ( ) . to_node ( & root) ) ?. krate ( ) ;
87+ let impl_def = d. impl_ . to_node ( & root) ;
88+ let current_crate = ctx. sema . scope ( impl_def. syntax ( ) ) ?. krate ( ) ;
8689 let trait_def_crate = d. trait_ . module ( db) . krate ( db) ;
8790 if trait_def_crate != current_crate {
8891 return None ;
8992 }
9093
9194 let trait_def = d. trait_ . source ( db) ?. value ;
92- let l_curly = trait_def. assoc_item_list ( ) ?. l_curly_token ( ) ?. text_range ( ) ;
95+ let insert_after = find_insert_after ( range, & impl_def, & trait_def) ?;
96+
9397 let where_to_insert =
94- hir:: InFile :: new ( d. file_id , l_curly ) . original_node_file_range_rooted_opt ( db) ?;
98+ hir:: InFile :: new ( d. file_id , insert_after ) . original_node_file_range_rooted_opt ( db) ?;
9599 if where_to_insert. file_id != file_id {
96100 return None ;
97101 }
@@ -112,6 +116,41 @@ fn quickfix_for_redundant_assoc_item(
112116 } ] )
113117}
114118
119+ fn find_insert_after (
120+ redundant_range : TextRange ,
121+ impl_def : & syntax:: ast:: Impl ,
122+ trait_def : & syntax:: ast:: Trait ,
123+ ) -> Option < TextRange > {
124+ let impl_items_before_redundant = impl_def
125+ . assoc_item_list ( ) ?
126+ . assoc_items ( )
127+ . take_while ( |it| it. syntax ( ) . text_range ( ) . start ( ) < redundant_range. start ( ) )
128+ . filter_map ( |it| name_of ( & it) )
129+ . collect :: < Vec < _ > > ( ) ;
130+
131+ let after_item = trait_def
132+ . assoc_item_list ( ) ?
133+ . assoc_items ( )
134+ . filter ( |it| {
135+ name_of ( it) . is_some_and ( |name| {
136+ impl_items_before_redundant. iter ( ) . any ( |it| it. text ( ) == name. text ( ) )
137+ } )
138+ } )
139+ . last ( )
140+ . map ( |it| it. syntax ( ) . text_range ( ) ) ;
141+
142+ return after_item. or_else ( || Some ( trait_def. assoc_item_list ( ) ?. l_curly_token ( ) ?. text_range ( ) ) ) ;
143+
144+ fn name_of ( it : & syntax:: ast:: AssocItem ) -> Option < syntax:: ast:: Name > {
145+ match it {
146+ syntax:: ast:: AssocItem :: Const ( it) => it. name ( ) ,
147+ syntax:: ast:: AssocItem :: Fn ( it) => it. name ( ) ,
148+ syntax:: ast:: AssocItem :: TypeAlias ( it) => it. name ( ) ,
149+ syntax:: ast:: AssocItem :: MacroCall ( _) => None ,
150+ }
151+ }
152+ }
153+
115154#[ cfg( test) ]
116155mod tests {
117156 use crate :: tests:: { check_diagnostics, check_fix, check_no_fix} ;
@@ -274,6 +313,69 @@ mod indent {
274313 ) ;
275314 }
276315
316+ #[ test]
317+ fn quickfix_order ( ) {
318+ check_fix (
319+ r#"
320+ trait Marker {
321+ fn foo();
322+ fn baz();
323+ }
324+ struct Foo;
325+ impl Marker for Foo {
326+ fn foo() {}
327+ fn missing() {}$0
328+ fn baz() {}
329+ }
330+ "# ,
331+ r#"
332+ trait Marker {
333+ fn foo();
334+ fn missing();
335+ fn baz();
336+ }
337+ struct Foo;
338+ impl Marker for Foo {
339+ fn foo() {}
340+ fn missing() {}
341+ fn baz() {}
342+ }
343+ "# ,
344+ ) ;
345+
346+ check_fix (
347+ r#"
348+ trait Marker {
349+ type Item;
350+ fn bar();
351+ fn baz();
352+ }
353+ struct Foo;
354+ impl Marker for Foo {
355+ type Item = Foo;
356+ fn missing() {}$0
357+ fn bar() {}
358+ fn baz() {}
359+ }
360+ "# ,
361+ r#"
362+ trait Marker {
363+ type Item;
364+ fn missing();
365+ fn bar();
366+ fn baz();
367+ }
368+ struct Foo;
369+ impl Marker for Foo {
370+ type Item = Foo;
371+ fn missing() {}
372+ fn bar() {}
373+ fn baz() {}
374+ }
375+ "# ,
376+ ) ;
377+ }
378+
277379 #[ test]
278380 fn quickfix_dont_work ( ) {
279381 check_no_fix (
0 commit comments