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 ;
3
10
4
11
use crate :: { Diagnostic , DiagnosticCode , DiagnosticsContext } ;
5
12
@@ -10,42 +17,100 @@ pub(crate) fn trait_impl_redundant_assoc_item(
10
17
ctx : & DiagnosticsContext < ' _ > ,
11
18
d : & hir:: TraitImplRedundantAssocItems ,
12
19
) -> Diagnostic {
20
+ let db = ctx. sema . db ;
13
21
let name = d. assoc_item . 0 . clone ( ) ;
22
+ let redundant_assoc_item_name = name. display ( db) ;
14
23
let assoc_item = d. assoc_item . 1 ;
15
- let db = ctx. sema . db ;
16
24
17
25
let default_range = d. impl_ . syntax_node_ptr ( ) . text_range ( ) ;
18
26
let trait_name = d. trait_ . name ( db) . to_smol_str ( ) ;
19
27
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
+ }
42
62
} ;
43
63
44
64
Diagnostic :: new (
45
65
DiagnosticCode :: RustcHardError ( "E0407" ) ,
46
66
format ! ( "{redundant_item_name} is not a member of trait `{trait_name}`" ) ,
47
67
FileRange { file_id : d. file_id . file_id ( ) . unwrap ( ) , range : diagnostic_range } ,
48
68
)
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
+ } ] )
49
114
}
50
115
51
116
#[ cfg( test) ]
0 commit comments