@@ -8,15 +8,15 @@ use std::cell::RefCell;
88
99use hir:: {
1010 diagnostics:: { AstDiagnostic , Diagnostic as _, DiagnosticSink } ,
11- Semantics ,
11+ HasSource , HirDisplay , Semantics , VariantDef ,
1212} ;
1313use itertools:: Itertools ;
1414use ra_db:: { RelativePath , SourceDatabase , SourceDatabaseExt } ;
1515use ra_ide_db:: RootDatabase ;
1616use ra_prof:: profile;
1717use ra_syntax:: {
1818 algo,
19- ast:: { self , make, AstNode } ,
19+ ast:: { self , edit :: IndentLevel , make, AstNode } ,
2020 SyntaxNode , TextRange , T ,
2121} ;
2222use ra_text_edit:: { TextEdit , TextEditBuilder } ;
@@ -123,14 +123,85 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
123123 severity : Severity :: Error ,
124124 fix : Some ( fix) ,
125125 } )
126+ } )
127+ . on :: < hir:: diagnostics:: NoSuchField , _ > ( |d| {
128+ res. borrow_mut ( ) . push ( Diagnostic {
129+ range : sema. diagnostics_range ( d) . range ,
130+ message : d. message ( ) ,
131+ severity : Severity :: Error ,
132+ fix : missing_struct_field_fix ( & sema, file_id, d) ,
133+ } )
126134 } ) ;
135+
127136 if let Some ( m) = sema. to_module_def ( file_id) {
128137 m. diagnostics ( db, & mut sink) ;
129138 } ;
130139 drop ( sink) ;
131140 res. into_inner ( )
132141}
133142
143+ fn missing_struct_field_fix (
144+ sema : & Semantics < RootDatabase > ,
145+ file_id : FileId ,
146+ d : & hir:: diagnostics:: NoSuchField ,
147+ ) -> Option < Fix > {
148+ let record_expr = sema. ast ( d) ;
149+
150+ let record_lit = ast:: RecordLit :: cast ( record_expr. syntax ( ) . parent ( ) ?. parent ( ) ?) ?;
151+ let def_id = sema. resolve_variant ( record_lit) ?;
152+ let module;
153+ let record_fields = match VariantDef :: from ( def_id) {
154+ VariantDef :: Struct ( s) => {
155+ module = s. module ( sema. db ) ;
156+ let source = s. source ( sema. db ) ;
157+ let fields = source. value . field_def_list ( ) ?;
158+ record_field_def_list ( fields) ?
159+ }
160+ VariantDef :: Union ( u) => {
161+ module = u. module ( sema. db ) ;
162+ let source = u. source ( sema. db ) ;
163+ source. value . record_field_def_list ( ) ?
164+ }
165+ VariantDef :: EnumVariant ( e) => {
166+ module = e. module ( sema. db ) ;
167+ let source = e. source ( sema. db ) ;
168+ let fields = source. value . field_def_list ( ) ?;
169+ record_field_def_list ( fields) ?
170+ }
171+ } ;
172+
173+ let new_field_type = sema. type_of_expr ( & record_expr. expr ( ) ?) ?;
174+ let new_field = make:: record_field_def (
175+ record_expr. field_name ( ) ?,
176+ make:: type_ref ( & new_field_type. display_source_code ( sema. db , module. into ( ) ) . ok ( ) ?) ,
177+ ) ;
178+
179+ let last_field = record_fields. fields ( ) . last ( ) ?;
180+ let last_field_syntax = last_field. syntax ( ) ;
181+ let indent = IndentLevel :: from_node ( last_field_syntax) ;
182+
183+ let mut new_field = format ! ( "\n {}{}" , indent, new_field) ;
184+
185+ let needs_comma = !last_field_syntax. to_string ( ) . ends_with ( "," ) ;
186+ if needs_comma {
187+ new_field = format ! ( ",{}" , new_field) ;
188+ }
189+
190+ let source_change = SourceFileEdit {
191+ file_id,
192+ edit : TextEdit :: insert ( last_field_syntax. text_range ( ) . end ( ) , new_field) ,
193+ } ;
194+ let fix = Fix :: new ( "Create field" , source_change. into ( ) ) ;
195+ return Some ( fix) ;
196+
197+ fn record_field_def_list ( field_def_list : ast:: FieldDefList ) -> Option < ast:: RecordFieldDefList > {
198+ match field_def_list {
199+ ast:: FieldDefList :: RecordFieldDefList ( it) => Some ( it) ,
200+ ast:: FieldDefList :: TupleFieldDefList ( _) => None ,
201+ }
202+ }
203+ }
204+
134205fn check_unnecessary_braces_in_use_statement (
135206 acc : & mut Vec < Diagnostic > ,
136207 file_id : FileId ,
@@ -795,4 +866,27 @@ fn main() {
795866 check_struct_shorthand_initialization,
796867 ) ;
797868 }
869+
870+ #[ test]
871+ fn test_add_field_from_usage ( ) {
872+ check_apply_diagnostic_fix (
873+ r"
874+ fn main() {
875+ Foo { bar: 3, baz: false};
876+ }
877+ struct Foo {
878+ bar: i32
879+ }
880+ " ,
881+ r"
882+ fn main() {
883+ Foo { bar: 3, baz: false};
884+ }
885+ struct Foo {
886+ bar: i32,
887+ baz: bool
888+ }
889+ " ,
890+ )
891+ }
798892}
0 commit comments