1- use crate :: { Diagnostic , DiagnosticCode , DiagnosticsContext } ;
1+ use hir:: { EditionedFileId , FileRange , HasCrate , HasSource , Semantics } ;
2+ use ide_db:: { RootDatabase , assists:: Assist , source_change:: SourceChange , text_edit:: TextEdit } ;
3+ use syntax:: { AstNode , TextRange , TextSize , ast:: HasVisibility } ;
4+
5+ use crate :: { Diagnostic , DiagnosticCode , DiagnosticsContext , fix} ;
26
37// Diagnostic: private-field
48//
@@ -16,11 +20,59 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField)
1620 d. expr . map ( |it| it. into ( ) ) ,
1721 )
1822 . stable ( )
23+ . with_fixes ( field_is_private_fixes (
24+ & ctx. sema ,
25+ d. expr . file_id . original_file ( ctx. sema . db ) ,
26+ d. field ,
27+ ctx. sema . original_range ( d. expr . to_node ( ctx. sema . db ) . syntax ( ) ) . range ,
28+ ) )
29+ }
30+
31+ pub ( crate ) fn field_is_private_fixes (
32+ sema : & Semantics < ' _ , RootDatabase > ,
33+ usage_file_id : EditionedFileId ,
34+ private_field : hir:: Field ,
35+ fix_range : TextRange ,
36+ ) -> Option < Vec < Assist > > {
37+ let def_crate = private_field. krate ( sema. db ) ;
38+ let usage_crate = sema. file_to_module_def ( usage_file_id. file_id ( sema. db ) ) ?. krate ( ) ;
39+ let mut visibility_text = if usage_crate == def_crate { "pub(crate) " } else { "pub " } ;
40+
41+ let source = private_field. source ( sema. db ) ?;
42+ let existing_visibility = match & source. value {
43+ hir:: FieldSource :: Named ( it) => it. visibility ( ) ,
44+ hir:: FieldSource :: Pos ( it) => it. visibility ( ) ,
45+ } ;
46+ let range = match existing_visibility {
47+ Some ( visibility) => {
48+ // If there is an existing visibility, don't insert whitespace after.
49+ visibility_text = visibility_text. trim_end ( ) ;
50+ source. with_value ( visibility. syntax ( ) ) . original_file_range_opt ( sema. db ) ?. 0
51+ }
52+ None => {
53+ let ( range, _) = source. syntax ( ) . original_file_range_opt ( sema. db ) ?;
54+ FileRange {
55+ file_id : range. file_id ,
56+ range : TextRange :: at ( range. range . start ( ) , TextSize :: new ( 0 ) ) ,
57+ }
58+ }
59+ } ;
60+ let source_change = SourceChange :: from_text_edit (
61+ range. file_id . file_id ( sema. db ) ,
62+ TextEdit :: replace ( range. range , visibility_text. into ( ) ) ,
63+ ) ;
64+
65+ Some ( vec ! [ fix(
66+ "increase_field_visibility" ,
67+ "Increase field visibility" ,
68+ source_change,
69+ fix_range,
70+ ) ] )
1971}
2072
2173#[ cfg( test) ]
2274mod tests {
23- use crate :: tests:: check_diagnostics;
75+ use crate :: tests:: { check_diagnostics, check_fix } ;
2476
2577 #[ test]
2678 fn private_field ( ) {
@@ -29,7 +81,7 @@ mod tests {
2981mod module { pub struct Struct { field: u32 } }
3082fn main(s: module::Struct) {
3183 s.field;
32- //^^^^^^^ error: field `field` of `Struct` is private
84+ //^^^^^^^ 💡 error: field `field` of `Struct` is private
3385}
3486"# ,
3587 ) ;
@@ -42,7 +94,7 @@ fn main(s: module::Struct) {
4294mod module { pub struct Struct(u32); }
4395fn main(s: module::Struct) {
4496 s.0;
45- //^^^ error: field `0` of `Struct` is private
97+ //^^^ 💡 error: field `0` of `Struct` is private
4698}
4799"# ,
48100 ) ;
@@ -113,4 +165,68 @@ fn main() {
113165"# ,
114166 ) ;
115167 }
168+
169+ #[ test]
170+ fn change_visibility_fix ( ) {
171+ check_fix (
172+ r#"
173+ pub mod foo {
174+ pub mod bar {
175+ pub struct Struct {
176+ field: i32,
177+ }
178+ }
179+ }
180+
181+ fn foo(v: foo::bar::Struct) {
182+ v.field$0;
183+ }
184+ "# ,
185+ r#"
186+ pub mod foo {
187+ pub mod bar {
188+ pub struct Struct {
189+ pub(crate) field: i32,
190+ }
191+ }
192+ }
193+
194+ fn foo(v: foo::bar::Struct) {
195+ v.field;
196+ }
197+ "# ,
198+ ) ;
199+ }
200+
201+ #[ test]
202+ fn change_visibility_with_existing_visibility ( ) {
203+ check_fix (
204+ r#"
205+ pub mod foo {
206+ pub mod bar {
207+ pub struct Struct {
208+ pub(super) field: i32,
209+ }
210+ }
211+ }
212+
213+ fn foo(v: foo::bar::Struct) {
214+ v.field$0;
215+ }
216+ "# ,
217+ r#"
218+ pub mod foo {
219+ pub mod bar {
220+ pub struct Struct {
221+ pub(crate) field: i32,
222+ }
223+ }
224+ }
225+
226+ fn foo(v: foo::bar::Struct) {
227+ v.field;
228+ }
229+ "# ,
230+ ) ;
231+ }
116232}
0 commit comments