@@ -4,7 +4,7 @@ use ide_db::text_edit::TextEdit;
4
4
use ide_db:: { EditionedFileId , RootDatabase , source_change:: SourceChange } ;
5
5
use syntax:: {
6
6
AstNode ,
7
- ast:: { self , edit:: IndentLevel , make} ,
7
+ ast:: { self , HasName , RecordField , RecordFieldList , edit:: IndentLevel , make} ,
8
8
} ;
9
9
10
10
use crate :: { Assist , Diagnostic , DiagnosticCode , DiagnosticsContext , fix} ;
@@ -23,6 +23,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField)
23
23
node,
24
24
)
25
25
. stable ( )
26
+ . with_fixes ( field_is_private_fixes ( ctx, d) )
26
27
} else {
27
28
Diagnostic :: new_with_syntax_node_ptr (
28
29
ctx,
@@ -34,11 +35,78 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField)
34
35
node,
35
36
)
36
37
. stable ( )
37
- . with_fixes ( fixes ( ctx, d) )
38
+ . with_fixes ( no_such_field_fixes ( ctx, d) )
38
39
}
39
40
}
40
41
41
- fn fixes ( ctx : & DiagnosticsContext < ' _ > , d : & hir:: NoSuchField ) -> Option < Vec < Assist > > {
42
+ fn field_is_private_fixes (
43
+ ctx : & DiagnosticsContext < ' _ > ,
44
+ d : & hir:: NoSuchField ,
45
+ ) -> Option < Vec < Assist > > {
46
+ let root = ctx. sema . db . parse_or_expand ( d. field . file_id ) ;
47
+ match & d. field . value . to_node ( & root) {
48
+ Either :: Left ( node) => private_record_expr_field_fixes ( & ctx. sema , node) ,
49
+ _ => None ,
50
+ }
51
+ }
52
+ fn private_record_expr_field_fixes (
53
+ sema : & Semantics < ' _ , RootDatabase > ,
54
+ record_expr_field : & ast:: RecordExprField ,
55
+ ) -> Option < Vec < Assist > > {
56
+ let record_lit = ast:: RecordExpr :: cast ( record_expr_field. syntax ( ) . parent ( ) ?. parent ( ) ?) ?;
57
+ let def_id = sema. resolve_variant ( record_lit) ?;
58
+ let def_file_id;
59
+ let record_fields = match def_id {
60
+ hir:: VariantDef :: Struct ( s) => {
61
+ let source = s. source ( sema. db ) ?;
62
+ def_file_id = source. file_id ;
63
+ let fields = source. value . field_list ( ) ?;
64
+ record_field_list ( fields) ?
65
+ }
66
+ hir:: VariantDef :: Union ( u) => {
67
+ let source = u. source ( sema. db ) ?;
68
+ def_file_id = source. file_id ;
69
+ source. value . record_field_list ( ) ?
70
+ }
71
+ hir:: VariantDef :: Variant ( e) => {
72
+ let source = e. source ( sema. db ) ?;
73
+ def_file_id = source. file_id ;
74
+ let fields = source. value . field_list ( ) ?;
75
+ record_field_list ( fields) ?
76
+ }
77
+ } ;
78
+ let def_file_id = def_file_id. original_file ( sema. db ) ;
79
+
80
+ let field_definition = find_field ( & record_fields, record_expr_field) ?;
81
+
82
+ let source_change = SourceChange :: from_text_edit (
83
+ def_file_id. file_id ( sema. db ) ,
84
+ TextEdit :: insert ( field_definition. syntax ( ) . text_range ( ) . start ( ) , "pub " . into ( ) ) ,
85
+ ) ;
86
+
87
+ Some ( vec ! [ fix(
88
+ "make_field_public" ,
89
+ "Make field public" ,
90
+ source_change,
91
+ record_expr_field. syntax( ) . text_range( ) ,
92
+ ) ] )
93
+ }
94
+
95
+ fn find_field (
96
+ field_list : & RecordFieldList ,
97
+ record_expr_field : & ast:: RecordExprField ,
98
+ ) -> Option < RecordField > {
99
+ for field in field_list. fields ( ) {
100
+ let name = field. name ( ) ?;
101
+ let token = name. ident_token ( ) ?;
102
+ if token. text ( ) == record_expr_field. field_name ( ) ?. ident_token ( ) ?. text ( ) {
103
+ return Some ( field) ;
104
+ }
105
+ }
106
+ None
107
+ }
108
+
109
+ fn no_such_field_fixes ( ctx : & DiagnosticsContext < ' _ > , d : & hir:: NoSuchField ) -> Option < Vec < Assist > > {
42
110
// FIXME: quickfix for pattern
43
111
let root = ctx. sema . db . parse_or_expand ( d. field . file_id ) ;
44
112
match & d. field . value . to_node ( & root) {
@@ -120,12 +188,11 @@ fn missing_record_expr_field_fixes(
120
188
source_change,
121
189
record_expr_field. syntax( ) . text_range( ) ,
122
190
) ] ) ;
123
-
124
- fn record_field_list ( field_def_list : ast:: FieldList ) -> Option < ast:: RecordFieldList > {
125
- match field_def_list {
126
- ast:: FieldList :: RecordFieldList ( it) => Some ( it) ,
127
- ast:: FieldList :: TupleFieldList ( _) => None ,
128
- }
191
+ }
192
+ fn record_field_list ( field_def_list : ast:: FieldList ) -> Option < ast:: RecordFieldList > {
193
+ match field_def_list {
194
+ ast:: FieldList :: RecordFieldList ( it) => Some ( it) ,
195
+ ast:: FieldList :: TupleFieldList ( _) => None ,
129
196
}
130
197
}
131
198
@@ -368,6 +435,52 @@ fn main() {
368
435
)
369
436
}
370
437
438
+ #[ test]
439
+ fn test_struct_field_private_fix ( ) {
440
+ check_diagnostics (
441
+ r#"
442
+ mod m {
443
+ pub struct Struct {
444
+ field: u32,
445
+ }
446
+ }
447
+ fn f() {
448
+ let _ = m::Struct {
449
+ field: 0,
450
+ //^^^^^^^^ 💡 error: field is private
451
+ };
452
+ }
453
+ "# ,
454
+ ) ;
455
+
456
+ check_fix (
457
+ r#"
458
+ mod m {
459
+ pub struct Struct {
460
+ field: u32,
461
+ }
462
+ }
463
+ fn f() {
464
+ let _ = m::Struct {
465
+ field$0: 0,
466
+ };
467
+ }
468
+ "# ,
469
+ r#"
470
+ mod m {
471
+ pub struct Struct {
472
+ pub field: u32,
473
+ }
474
+ }
475
+ fn f() {
476
+ let _ = m::Struct {
477
+ field: 0,
478
+ };
479
+ }
480
+ "# ,
481
+ )
482
+ }
483
+
371
484
#[ test]
372
485
fn test_struct_field_private ( ) {
373
486
check_diagnostics (
@@ -387,15 +500,15 @@ fn f(s@m::Struct {
387
500
// assignee expression
388
501
m::Struct {
389
502
field: 0,
390
- //^^^^^^^^ error: field is private
503
+ //^^^^^^^^ 💡 error: field is private
391
504
field2
392
- //^^^^^^ error: field is private
505
+ //^^^^^^ 💡 error: field is private
393
506
} = s;
394
507
m::Struct {
395
508
field: 0,
396
- //^^^^^^^^ error: field is private
509
+ //^^^^^^^^ 💡 error: field is private
397
510
field2
398
- //^^^^^^ error: field is private
511
+ //^^^^^^ 💡 error: field is private
399
512
};
400
513
}
401
514
"# ,
0 commit comments