@@ -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,87 @@ 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 (
49
+ & ctx. sema ,
50
+ d. field . file_id . original_file ( ctx. sema . db ) ,
51
+ node,
52
+ ) ,
53
+ _ => None ,
54
+ }
55
+ }
56
+ fn private_record_expr_field_fixes (
57
+ sema : & Semantics < ' _ , RootDatabase > ,
58
+ usage_file_id : EditionedFileId ,
59
+ record_expr_field : & ast:: RecordExprField ,
60
+ ) -> Option < Vec < Assist > > {
61
+ let record_lit = ast:: RecordExpr :: cast ( record_expr_field. syntax ( ) . parent ( ) ?. parent ( ) ?) ?;
62
+ let def_id = sema. resolve_variant ( record_lit) ?;
63
+ let module;
64
+ let def_file_id;
65
+ let record_fields = match def_id {
66
+ hir:: VariantDef :: Struct ( s) => {
67
+ module = s. module ( sema. db ) ;
68
+ let source = s. source ( sema. db ) ?;
69
+ def_file_id = source. file_id ;
70
+ let fields = source. value . field_list ( ) ?;
71
+ record_field_list ( fields) ?
72
+ }
73
+ hir:: VariantDef :: Union ( u) => {
74
+ module = u. module ( sema. db ) ;
75
+ let source = u. source ( sema. db ) ?;
76
+ def_file_id = source. file_id ;
77
+ source. value . record_field_list ( ) ?
78
+ }
79
+ hir:: VariantDef :: Variant ( e) => {
80
+ module = e. module ( sema. db ) ;
81
+ let source = e. source ( sema. db ) ?;
82
+ def_file_id = source. file_id ;
83
+ let fields = source. value . field_list ( ) ?;
84
+ record_field_list ( fields) ?
85
+ }
86
+ } ;
87
+ let def_file_id = def_file_id. original_file ( sema. db ) ;
88
+
89
+ let field_definition = find_field ( & record_fields, record_expr_field) ?;
90
+
91
+ let source_change = SourceChange :: from_text_edit (
92
+ def_file_id. file_id ( sema. db ) ,
93
+ TextEdit :: insert ( field_definition. syntax ( ) . text_range ( ) . start ( ) , "pub " . into ( ) ) ,
94
+ ) ;
95
+
96
+ return Some ( vec ! [ fix(
97
+ "make_field_public" ,
98
+ "Make field public" ,
99
+ source_change,
100
+ record_expr_field. syntax( ) . text_range( ) ,
101
+ ) ] ) ;
102
+ }
103
+
104
+ fn find_field (
105
+ field_list : & RecordFieldList ,
106
+ record_expr_field : & ast:: RecordExprField ,
107
+ ) -> Option < RecordField > {
108
+ for field in field_list. fields ( ) {
109
+ let name = field. name ( ) ?;
110
+ let token = name. ident_token ( ) ?;
111
+ if token. text ( ) == record_expr_field. field_name ( ) ?. ident_token ( ) ?. text ( ) {
112
+ return Some ( field) ;
113
+ }
114
+ }
115
+ None
116
+ }
117
+
118
+ fn no_such_field_fixes ( ctx : & DiagnosticsContext < ' _ > , d : & hir:: NoSuchField ) -> Option < Vec < Assist > > {
42
119
// FIXME: quickfix for pattern
43
120
let root = ctx. sema . db . parse_or_expand ( d. field . file_id ) ;
44
121
match & d. field . value . to_node ( & root) {
@@ -120,12 +197,11 @@ fn missing_record_expr_field_fixes(
120
197
source_change,
121
198
record_expr_field. syntax( ) . text_range( ) ,
122
199
) ] ) ;
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
- }
200
+ }
201
+ fn record_field_list ( field_def_list : ast:: FieldList ) -> Option < ast:: RecordFieldList > {
202
+ match field_def_list {
203
+ ast:: FieldList :: RecordFieldList ( it) => Some ( it) ,
204
+ ast:: FieldList :: TupleFieldList ( _) => None ,
129
205
}
130
206
}
131
207
@@ -368,6 +444,52 @@ fn main() {
368
444
)
369
445
}
370
446
447
+ #[ test]
448
+ fn test_struct_field_private_fix ( ) {
449
+ check_diagnostics (
450
+ r#"
451
+ mod m {
452
+ pub struct Struct {
453
+ field: u32,
454
+ }
455
+ }
456
+ fn f() {
457
+ let _ = m::Struct {
458
+ field: 0,
459
+ //^^^^^^^^ 💡 error: field is private
460
+ };
461
+ }
462
+ "# ,
463
+ ) ;
464
+
465
+ check_fix (
466
+ r#"
467
+ mod m {
468
+ pub struct Struct {
469
+ field: u32,
470
+ }
471
+ }
472
+ fn f() {
473
+ let _ = m::Struct {
474
+ field$0: 0,
475
+ };
476
+ }
477
+ "# ,
478
+ r#"
479
+ mod m {
480
+ pub struct Struct {
481
+ pub field: u32,
482
+ }
483
+ }
484
+ fn f() {
485
+ let _ = m::Struct {
486
+ field: 0,
487
+ };
488
+ }
489
+ "# ,
490
+ )
491
+ }
492
+
371
493
#[ test]
372
494
fn test_struct_field_private ( ) {
373
495
check_diagnostics (
0 commit comments