@@ -8,10 +8,11 @@ use ra_syntax::{
88 SyntaxNode , TextRange , TextSize , T ,
99} ;
1010
11- use hir:: { db:: HirDatabase , HasSource , PathResolution } ;
11+ use hir:: { db:: HirDatabase , HasSource , HasVisibility , PathResolution } ;
1212use test_utils:: tested_by;
1313
1414use crate :: { AssistContext , AssistId , Assists } ;
15+ use ra_db:: FileId ;
1516
1617// Assist: change_visibility
1718//
@@ -29,6 +30,8 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio
2930 return change_vis ( acc, vis) ;
3031 }
3132 add_vis ( acc, ctx)
33+ . or_else ( || add_vis_to_referenced_module_def ( acc, ctx) )
34+ . or_else ( || add_vis_to_referenced_record_field ( acc, ctx) )
3235}
3336
3437fn add_vis ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
@@ -74,86 +77,141 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
7477 } )
7578}
7679
77- fn add_missing_vis ( ctx : AssistCtx ) -> Option < Assist > {
80+ fn add_vis_to_referenced_module_def ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
7881 let path: ast:: Path = ctx. find_node_at_offset ( ) ?;
79- let path_res = dbg ! ( ctx. sema. resolve_path( & path) ) ?;
82+ let path_res = ctx. sema . resolve_path ( & path) ?;
8083 let def = match path_res {
8184 PathResolution :: Def ( def) => def,
8285 _ => return None ,
8386 } ;
84- dbg ! ( & def) ;
8587
86- let current_module = dbg ! ( ctx. sema. scope( & path. syntax( ) ) . module( ) ) ?;
87- let target_module = dbg ! ( def. module( ctx. db) ) ?;
88+ let current_module = ctx. sema . scope ( & path. syntax ( ) ) . module ( ) ?;
89+ let target_module = def. module ( ctx. db ) ?;
8890
89- let vis = dbg ! ( target_module. visibility_of( ctx. db, & def) ) ?;
91+ let vis = target_module. visibility_of ( ctx. db , & def) ?;
9092 if vis. is_visible_from ( ctx. db , current_module. into ( ) ) {
9193 return None ;
9294 } ;
93- let target_name;
9495
95- let ( offset, target) = match def {
96+ let ( offset, target, target_file, target_name) = target_data_for_def ( ctx. db , def) ?;
97+
98+ let missing_visibility =
99+ if current_module. krate ( ) == target_module. krate ( ) { "pub(crate)" } else { "pub" } ;
100+
101+ let assist_label = match target_name {
102+ None => format ! ( "Change visibility to {}" , missing_visibility) ,
103+ Some ( name) => format ! ( "Change visibility of {} to {}" , name, missing_visibility) ,
104+ } ;
105+
106+ acc. add ( AssistId ( "change_visibility" ) , assist_label, target, |edit| {
107+ edit. set_file ( target_file) ;
108+ edit. insert ( offset, format ! ( "{} " , missing_visibility) ) ;
109+ edit. set_cursor ( offset) ;
110+ } )
111+ }
112+
113+ fn add_vis_to_referenced_record_field ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
114+ let record_field: ast:: RecordField = ctx. find_node_at_offset ( ) ?;
115+ let ( record_field_def, _) = ctx. sema . resolve_record_field ( & record_field) ?;
116+
117+ let current_module = ctx. sema . scope ( record_field. syntax ( ) ) . module ( ) ?;
118+ let visibility = record_field_def. visibility ( ctx. db ) ;
119+ if visibility. is_visible_from ( ctx. db , current_module. into ( ) ) {
120+ return None ;
121+ }
122+
123+ let parent = record_field_def. parent_def ( ctx. db ) ;
124+ let parent_name = parent. name ( ctx. db ) ;
125+ let target_module = parent. module ( ctx. db ) ;
126+
127+ let in_file_source = record_field_def. source ( ctx. db ) ;
128+ let ( offset, target) = match in_file_source. value {
129+ hir:: FieldSource :: Named ( it) => {
130+ let s = it. syntax ( ) ;
131+ ( vis_offset ( s) , s. text_range ( ) )
132+ }
133+ hir:: FieldSource :: Pos ( it) => {
134+ let s = it. syntax ( ) ;
135+ ( vis_offset ( s) , s. text_range ( ) )
136+ }
137+ } ;
138+
139+ let missing_visibility =
140+ if current_module. krate ( ) == target_module. krate ( ) { "pub(crate)" } else { "pub" } ;
141+ let target_file = in_file_source. file_id . original_file ( ctx. db ) ;
142+
143+ let target_name = record_field_def. name ( ctx. db ) ;
144+ let assist_label =
145+ format ! ( "Change visibility of {}.{} to {}" , parent_name, target_name, missing_visibility) ;
146+
147+ acc. add ( AssistId ( "change_visibility" ) , assist_label, target, |edit| {
148+ edit. set_file ( target_file) ;
149+ edit. insert ( offset, format ! ( "{} " , missing_visibility) ) ;
150+ edit. set_cursor ( offset)
151+ } )
152+ }
153+
154+ fn target_data_for_def (
155+ db : & dyn HirDatabase ,
156+ def : hir:: ModuleDef ,
157+ ) -> Option < ( TextSize , TextRange , FileId , Option < hir:: Name > ) > {
158+ fn offset_target_and_file_id < S , Ast > (
159+ db : & dyn HirDatabase ,
160+ x : S ,
161+ ) -> ( TextSize , TextRange , FileId )
162+ where
163+ S : HasSource < Ast = Ast > ,
164+ Ast : AstNode ,
165+ {
166+ let source = x. source ( db) ;
167+ let in_file_syntax = source. syntax ( ) ;
168+ let file_id = in_file_syntax. file_id ;
169+ let syntax = in_file_syntax. value ;
170+ ( vis_offset ( syntax) , syntax. text_range ( ) , file_id. original_file ( db. upcast ( ) ) )
171+ }
172+
173+ let target_name;
174+ let ( offset, target, target_file) = match def {
96175 hir:: ModuleDef :: Function ( f) => {
97- target_name = Some ( f. name ( ctx . db ) ) ;
98- offset_and_target ( ctx . db , f)
176+ target_name = Some ( f. name ( db) ) ;
177+ offset_target_and_file_id ( db, f)
99178 }
100179 hir:: ModuleDef :: Adt ( adt) => {
101- target_name = Some ( adt. name ( ctx . db ) ) ;
180+ target_name = Some ( adt. name ( db) ) ;
102181 match adt {
103- hir:: Adt :: Struct ( s) => offset_and_target ( ctx . db , s) ,
104- hir:: Adt :: Union ( u) => offset_and_target ( ctx . db , u) ,
105- hir:: Adt :: Enum ( e) => offset_and_target ( ctx . db , e) ,
182+ hir:: Adt :: Struct ( s) => offset_target_and_file_id ( db, s) ,
183+ hir:: Adt :: Union ( u) => offset_target_and_file_id ( db, u) ,
184+ hir:: Adt :: Enum ( e) => offset_target_and_file_id ( db, e) ,
106185 }
107186 }
108187 hir:: ModuleDef :: Const ( c) => {
109- target_name = c. name ( ctx . db ) ;
110- offset_and_target ( ctx . db , c)
188+ target_name = c. name ( db) ;
189+ offset_target_and_file_id ( db, c)
111190 }
112191 hir:: ModuleDef :: Static ( s) => {
113- target_name = s. name ( ctx . db ) ;
114- offset_and_target ( ctx . db , s)
192+ target_name = s. name ( db) ;
193+ offset_target_and_file_id ( db, s)
115194 }
116195 hir:: ModuleDef :: Trait ( t) => {
117- target_name = Some ( t. name ( ctx . db ) ) ;
118- offset_and_target ( ctx . db , t)
196+ target_name = Some ( t. name ( db) ) ;
197+ offset_target_and_file_id ( db, t)
119198 }
120199 hir:: ModuleDef :: TypeAlias ( t) => {
121- target_name = Some ( t. name ( ctx . db ) ) ;
122- offset_and_target ( ctx . db , t)
200+ target_name = Some ( t. name ( db) ) ;
201+ offset_target_and_file_id ( db, t)
123202 }
124203 hir:: ModuleDef :: Module ( m) => {
125- target_name = m. name ( ctx. db ) ;
126- let source = dbg ! ( m. declaration_source( ctx. db) ) ?. value ;
127- let syntax = source. syntax ( ) ;
128- ( vis_offset ( syntax) , syntax. text_range ( ) )
204+ target_name = m. name ( db) ;
205+ let in_file_source = m. declaration_source ( db) ?;
206+ let file_id = in_file_source. file_id . original_file ( db. upcast ( ) ) ;
207+ let syntax = in_file_source. value . syntax ( ) ;
208+ ( vis_offset ( syntax) , syntax. text_range ( ) , file_id)
129209 }
130210 // Enum variants can't be private, we can't modify builtin types
131211 hir:: ModuleDef :: EnumVariant ( _) | hir:: ModuleDef :: BuiltinType ( _) => return None ,
132212 } ;
133213
134- // FIXME if target is in another crate, add `pub` instead of `pub(crate)`
135-
136- let assist_label = match target_name {
137- None => "Change visibility to pub(crate)" . to_string ( ) ,
138- Some ( name) => format ! ( "Change visibility of {} to pub(crate)" , name) ,
139- } ;
140- let target_file = target_module. definition_source ( ctx. db ) . file_id . original_file ( ctx. db ) ;
141-
142- ctx. add_assist ( AssistId ( "change_visibility" ) , assist_label, target, |edit| {
143- edit. set_file ( target_file) ;
144- edit. insert ( offset, "pub(crate) " ) ;
145- edit. set_cursor ( offset) ;
146- } )
147- }
148-
149- fn offset_and_target < S , Ast > ( db : & dyn HirDatabase , x : S ) -> ( TextSize , TextRange )
150- where
151- S : HasSource < Ast = Ast > ,
152- Ast : AstNode ,
153- {
154- let source = x. source ( db) ;
155- let syntax = source. syntax ( ) . value ;
156- ( vis_offset ( syntax) , syntax. text_range ( ) )
214+ Some ( ( offset, target, target_file, target_name) )
157215}
158216
159217fn vis_offset ( node : & SyntaxNode ) -> TextSize {
@@ -350,7 +408,6 @@ mod tests {
350408 }
351409
352410 #[ test]
353- // FIXME this requires a separate implementation, struct fields are not a ast::Path
354411 fn change_visibility_of_struct_field_via_path ( ) {
355412 check_assist (
356413 change_visibility,
@@ -359,11 +416,108 @@ mod tests {
359416 r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } }
360417 fn main() { foo::Foo { bar: () }; } " ,
361418 ) ;
419+ check_assist (
420+ change_visibility,
421+ r"//- /lib.rs
422+ mod foo;
423+ fn main() { foo::Foo { <|>bar: () }; }
424+ //- /foo.rs
425+ pub struct Foo { bar: () }
426+ " ,
427+ r"pub struct Foo { <|>pub(crate) bar: () }
428+
429+ " ,
430+ ) ;
431+ check_assist_not_applicable (
432+ change_visibility,
433+ r"mod foo { pub struct Foo { pub bar: (), } }
434+ fn main() { foo::Foo { <|>bar: () }; } " ,
435+ ) ;
436+ check_assist_not_applicable (
437+ change_visibility,
438+ r"//- /lib.rs
439+ mod foo;
440+ fn main() { foo::Foo { <|>bar: () }; }
441+ //- /foo.rs
442+ pub struct Foo { pub bar: () }
443+ " ,
444+ ) ;
445+ }
446+
447+ #[ test]
448+ fn change_visibility_of_enum_variant_field_via_path ( ) {
449+ check_assist (
450+ change_visibility,
451+ r"mod foo { pub enum Foo { Bar { bar: () } } }
452+ fn main() { foo::Foo::Bar { <|>bar: () }; } " ,
453+ r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } }
454+ fn main() { foo::Foo::Bar { bar: () }; } " ,
455+ ) ;
456+ check_assist (
457+ change_visibility,
458+ r"//- /lib.rs
459+ mod foo;
460+ fn main() { foo::Foo::Bar { <|>bar: () }; }
461+ //- /foo.rs
462+ pub enum Foo { Bar { bar: () } }
463+ " ,
464+ r"pub enum Foo { Bar { <|>pub(crate) bar: () } }
465+
466+ " ,
467+ ) ;
362468 check_assist_not_applicable (
363469 change_visibility,
364470 r"mod foo { pub struct Foo { pub bar: (), } }
365471 fn main() { foo::Foo { <|>bar: () }; } " ,
366472 ) ;
473+ check_assist_not_applicable (
474+ change_visibility,
475+ r"//- /lib.rs
476+ mod foo;
477+ fn main() { foo::Foo { <|>bar: () }; }
478+ //- /foo.rs
479+ pub struct Foo { pub bar: () }
480+ " ,
481+ ) ;
482+ }
483+
484+ #[ test]
485+ #[ ignore]
486+ // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields
487+ fn change_visibility_of_union_field_via_path ( ) {
488+ check_assist (
489+ change_visibility,
490+ r"mod foo { pub union Foo { bar: (), } }
491+ fn main() { foo::Foo { <|>bar: () }; } " ,
492+ r"mod foo { pub union Foo { <|>pub(crate) bar: (), } }
493+ fn main() { foo::Foo { bar: () }; } " ,
494+ ) ;
495+ check_assist (
496+ change_visibility,
497+ r"//- /lib.rs
498+ mod foo;
499+ fn main() { foo::Foo { <|>bar: () }; }
500+ //- /foo.rs
501+ pub union Foo { bar: () }
502+ " ,
503+ r"pub union Foo { <|>pub(crate) bar: () }
504+
505+ " ,
506+ ) ;
507+ check_assist_not_applicable (
508+ change_visibility,
509+ r"mod foo { pub union Foo { pub bar: (), } }
510+ fn main() { foo::Foo { <|>bar: () }; } " ,
511+ ) ;
512+ check_assist_not_applicable (
513+ change_visibility,
514+ r"//- /lib.rs
515+ mod foo;
516+ fn main() { foo::Foo { <|>bar: () }; }
517+ //- /foo.rs
518+ pub union Foo { pub bar: () }
519+ " ,
520+ ) ;
367521 }
368522
369523 #[ test]
@@ -500,24 +654,57 @@ mod tests {
500654 fn change_visibility_of_module_declaration_in_other_file_via_path ( ) {
501655 check_assist (
502656 change_visibility,
503- r"
504- //- /main.rs
505- mod foo;
506- fn main() { foo::bar<|>>::baz(); }
657+ r"//- /main.rs
658+ mod foo;
659+ fn main() { foo::bar<|>>::baz(); }
507660
508- //- /foo.rs
509- mod bar {
510- pub fn baz() {}
511- }
512- " ,
661+ //- /foo.rs
662+ mod bar {
663+ pub fn baz() {}
664+ }" ,
513665 r"<|>pub(crate) mod bar {
514666 pub fn baz() {}
515667}
516-
517668" ,
518669 ) ;
519670 }
520671
672+ #[ test]
673+ #[ ignore]
674+ // FIXME handle reexports properly
675+ fn change_visibility_of_reexport ( ) {
676+ check_assist (
677+ change_visibility,
678+ r"
679+ mod foo {
680+ use bar::Baz;
681+ mod bar { pub(super) struct Baz; }
682+ }
683+ foo::Baz<|>
684+ " ,
685+ r"
686+ mod foo {
687+ <|>pub(crate) use bar::Baz;
688+ mod bar { pub(super) struct Baz; }
689+ }
690+ foo::Baz
691+ " ,
692+ )
693+ }
694+
695+ #[ test]
696+ fn adds_pub_when_target_is_in_another_crate ( ) {
697+ check_assist (
698+ change_visibility,
699+ r"//- /main.rs crate:a deps:foo
700+ foo::Bar<|>
701+ //- /lib.rs crate:foo
702+ struct Bar;" ,
703+ r"<|>pub struct Bar;
704+ " ,
705+ )
706+ }
707+
521708 #[ test]
522709 fn change_visibility_target ( ) {
523710 check_assist_target ( change_visibility, "<|>fn foo() {}" , "fn" ) ;
0 commit comments