1- use hir:: { Const , Function , HasSource , TypeAlias } ;
2- use ide_db:: base_db:: FileRange ;
1+ use hir:: { db:: ExpandDatabase , Const , Function , HasSource , HirDisplay , TypeAlias } ;
2+ use ide_db:: {
3+ assists:: { Assist , AssistId , AssistKind } ,
4+ base_db:: FileRange ,
5+ label:: Label ,
6+ source_change:: SourceChangeBuilder ,
7+ } ;
8+ use syntax:: { AstNode , SyntaxKind } ;
9+ use text_edit:: TextRange ;
310
411use crate :: { Diagnostic , DiagnosticCode , DiagnosticsContext } ;
512
@@ -10,42 +17,100 @@ pub(crate) fn trait_impl_redundant_assoc_item(
1017 ctx : & DiagnosticsContext < ' _ > ,
1118 d : & hir:: TraitImplRedundantAssocItems ,
1219) -> Diagnostic {
20+ let db = ctx. sema . db ;
1321 let name = d. assoc_item . 0 . clone ( ) ;
22+ let redundant_assoc_item_name = name. display ( db) ;
1423 let assoc_item = d. assoc_item . 1 ;
15- let db = ctx. sema . db ;
1624
1725 let default_range = d. impl_ . syntax_node_ptr ( ) . text_range ( ) ;
1826 let trait_name = d. trait_ . name ( db) . to_smol_str ( ) ;
1927
20- let ( redundant_item_name, diagnostic_range) = match assoc_item {
21- hir:: AssocItem :: Function ( id) => (
22- format ! ( "`fn {}`" , name. display( db) ) ,
23- Function :: from ( id)
24- . source ( db)
25- . map ( |it| it. syntax ( ) . value . text_range ( ) )
26- . unwrap_or ( default_range) ,
27- ) ,
28- hir:: AssocItem :: Const ( id) => (
29- format ! ( "`const {}`" , name. display( db) ) ,
30- Const :: from ( id)
31- . source ( db)
32- . map ( |it| it. syntax ( ) . value . text_range ( ) )
33- . unwrap_or ( default_range) ,
34- ) ,
35- hir:: AssocItem :: TypeAlias ( id) => (
36- format ! ( "`type {}`" , name. display( db) ) ,
37- TypeAlias :: from ( id)
38- . source ( db)
39- . map ( |it| it. syntax ( ) . value . text_range ( ) )
40- . unwrap_or ( default_range) ,
41- ) ,
28+ let ( redundant_item_name, diagnostic_range, redundant_item_def) = match assoc_item {
29+ hir:: AssocItem :: Function ( id) => {
30+ let function = Function :: from ( id) ;
31+ (
32+ format ! ( "`fn {}`" , redundant_assoc_item_name) ,
33+ function
34+ . source ( db)
35+ . map ( |it| it. syntax ( ) . value . text_range ( ) )
36+ . unwrap_or ( default_range) ,
37+ format ! ( "\n {};" , function. display( db) . to_string( ) ) ,
38+ )
39+ }
40+ hir:: AssocItem :: Const ( id) => {
41+ let constant = Const :: from ( id) ;
42+ (
43+ format ! ( "`const {}`" , redundant_assoc_item_name) ,
44+ constant
45+ . source ( db)
46+ . map ( |it| it. syntax ( ) . value . text_range ( ) )
47+ . unwrap_or ( default_range) ,
48+ format ! ( "\n {};" , constant. display( db) . to_string( ) ) ,
49+ )
50+ }
51+ hir:: AssocItem :: TypeAlias ( id) => {
52+ let type_alias = TypeAlias :: from ( id) ;
53+ (
54+ format ! ( "`type {}`" , redundant_assoc_item_name) ,
55+ type_alias
56+ . source ( db)
57+ . map ( |it| it. syntax ( ) . value . text_range ( ) )
58+ . unwrap_or ( default_range) ,
59+ format ! ( "\n type {};" , type_alias. name( ctx. sema. db) . to_smol_str( ) ) ,
60+ )
61+ }
4262 } ;
4363
4464 Diagnostic :: new (
4565 DiagnosticCode :: RustcHardError ( "E0407" ) ,
4666 format ! ( "{redundant_item_name} is not a member of trait `{trait_name}`" ) ,
4767 FileRange { file_id : d. file_id . file_id ( ) . unwrap ( ) , range : diagnostic_range } ,
4868 )
69+ . with_fixes ( quickfix_for_redundant_assoc_item (
70+ ctx,
71+ d,
72+ redundant_item_def,
73+ diagnostic_range,
74+ ) )
75+ }
76+
77+ /// add assoc item into the trait def body
78+ fn quickfix_for_redundant_assoc_item (
79+ ctx : & DiagnosticsContext < ' _ > ,
80+ d : & hir:: TraitImplRedundantAssocItems ,
81+ redundant_item_def : String ,
82+ range : TextRange ,
83+ ) -> Option < Vec < Assist > > {
84+ let add_assoc_item_def = |builder : & mut SourceChangeBuilder | -> Option < ( ) > {
85+ let db = ctx. sema . db ;
86+ let root = db. parse_or_expand ( d. file_id ) ;
87+ // don't modify trait def in outer crate
88+ let current_crate = ctx. sema . scope ( & d. impl_ . syntax_node_ptr ( ) . to_node ( & root) ) ?. krate ( ) ;
89+ let trait_def_crate = d. trait_ . module ( db) . krate ( ) ;
90+ if trait_def_crate != current_crate {
91+ return None ;
92+ }
93+ let trait_def = d. trait_ . source ( db) ?. value ;
94+ let where_to_insert = trait_def
95+ . syntax ( )
96+ . descendants_with_tokens ( )
97+ . find ( |it| it. kind ( ) == SyntaxKind :: L_CURLY )
98+ . map ( |it| it. text_range ( ) ) ?;
99+
100+ Some ( builder. insert ( where_to_insert. end ( ) , redundant_item_def) )
101+ } ;
102+ let file_id = d. file_id . file_id ( ) ?;
103+ let mut source_change_builder = SourceChangeBuilder :: new ( file_id) ;
104+ add_assoc_item_def ( & mut source_change_builder) ?;
105+
106+ Some ( vec ! [ Assist {
107+ id: AssistId ( "add assoc item def into trait def" , AssistKind :: QuickFix ) ,
108+ label: Label :: new( "Add assoc item def into trait def" . to_string( ) ) ,
109+ group: None ,
110+ target: range,
111+ source_change: Some ( source_change_builder. finish( ) ) ,
112+ trigger_signature_help: false ,
113+ } ] )
49114}
50115
51116#[ cfg( test) ]
0 commit comments