aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/fix_visibility.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/fix_visibility.rs')
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs120
1 files changed, 90 insertions, 30 deletions
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
index 19d4dac5e..1aefa79cc 100644
--- a/crates/ra_assists/src/handlers/fix_visibility.rs
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -2,7 +2,8 @@ use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
2use ra_db::FileId; 2use ra_db::FileId;
3use ra_syntax::{ast, AstNode, TextRange, TextSize}; 3use ra_syntax::{ast, AstNode, TextRange, TextSize};
4 4
5use crate::{utils::vis_offset, AssistContext, AssistId, Assists}; 5use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
6use ast::VisibilityOwner;
6 7
7// FIXME: this really should be a fix for diagnostic, rather than an assist. 8// FIXME: this really should be a fix for diagnostic, rather than an assist.
8 9
@@ -41,14 +42,15 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
41 }; 42 };
42 43
43 let current_module = ctx.sema.scope(&path.syntax()).module()?; 44 let current_module = ctx.sema.scope(&path.syntax()).module()?;
44 let target_module = def.module(ctx.db)?; 45 let target_module = def.module(ctx.db())?;
45 46
46 let vis = target_module.visibility_of(ctx.db, &def)?; 47 let vis = target_module.visibility_of(ctx.db(), &def)?;
47 if vis.is_visible_from(ctx.db, current_module.into()) { 48 if vis.is_visible_from(ctx.db(), current_module.into()) {
48 return None; 49 return None;
49 }; 50 };
50 51
51 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; 52 let (offset, current_visibility, target, target_file, target_name) =
53 target_data_for_def(ctx.db(), def)?;
52 54
53 let missing_visibility = 55 let missing_visibility =
54 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; 56 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
@@ -58,54 +60,78 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
58 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), 60 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
59 }; 61 };
60 62
61 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 63 acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
62 builder.edit_file(target_file); 64 builder.edit_file(target_file);
63 match ctx.config.snippet_cap { 65 match ctx.config.snippet_cap {
64 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 66 Some(cap) => match current_visibility {
65 None => builder.insert(offset, format!("{} ", missing_visibility)), 67 Some(current_visibility) => builder.replace_snippet(
68 cap,
69 current_visibility.syntax().text_range(),
70 format!("$0{}", missing_visibility),
71 ),
72 None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
73 },
74 None => match current_visibility {
75 Some(current_visibility) => {
76 builder.replace(current_visibility.syntax().text_range(), missing_visibility)
77 }
78 None => builder.insert(offset, format!("{} ", missing_visibility)),
79 },
66 } 80 }
67 }) 81 })
68} 82}
69 83
70fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 84fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
71 let record_field: ast::RecordField = ctx.find_node_at_offset()?; 85 let record_field: ast::RecordExprField = ctx.find_node_at_offset()?;
72 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; 86 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
73 87
74 let current_module = ctx.sema.scope(record_field.syntax()).module()?; 88 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
75 let visibility = record_field_def.visibility(ctx.db); 89 let visibility = record_field_def.visibility(ctx.db());
76 if visibility.is_visible_from(ctx.db, current_module.into()) { 90 if visibility.is_visible_from(ctx.db(), current_module.into()) {
77 return None; 91 return None;
78 } 92 }
79 93
80 let parent = record_field_def.parent_def(ctx.db); 94 let parent = record_field_def.parent_def(ctx.db());
81 let parent_name = parent.name(ctx.db); 95 let parent_name = parent.name(ctx.db());
82 let target_module = parent.module(ctx.db); 96 let target_module = parent.module(ctx.db());
83 97
84 let in_file_source = record_field_def.source(ctx.db); 98 let in_file_source = record_field_def.source(ctx.db());
85 let (offset, target) = match in_file_source.value { 99 let (offset, current_visibility, target) = match in_file_source.value {
86 hir::FieldSource::Named(it) => { 100 hir::FieldSource::Named(it) => {
87 let s = it.syntax(); 101 let s = it.syntax();
88 (vis_offset(s), s.text_range()) 102 (vis_offset(s), it.visibility(), s.text_range())
89 } 103 }
90 hir::FieldSource::Pos(it) => { 104 hir::FieldSource::Pos(it) => {
91 let s = it.syntax(); 105 let s = it.syntax();
92 (vis_offset(s), s.text_range()) 106 (vis_offset(s), it.visibility(), s.text_range())
93 } 107 }
94 }; 108 };
95 109
96 let missing_visibility = 110 let missing_visibility =
97 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; 111 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
98 let target_file = in_file_source.file_id.original_file(ctx.db); 112 let target_file = in_file_source.file_id.original_file(ctx.db());
99 113
100 let target_name = record_field_def.name(ctx.db); 114 let target_name = record_field_def.name(ctx.db());
101 let assist_label = 115 let assist_label =
102 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); 116 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
103 117
104 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 118 acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
105 builder.edit_file(target_file); 119 builder.edit_file(target_file);
106 match ctx.config.snippet_cap { 120 match ctx.config.snippet_cap {
107 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 121 Some(cap) => match current_visibility {
108 None => builder.insert(offset, format!("{} ", missing_visibility)), 122 Some(current_visibility) => builder.replace_snippet(
123 cap,
124 dbg!(current_visibility.syntax()).text_range(),
125 format!("$0{}", missing_visibility),
126 ),
127 None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
128 },
129 None => match current_visibility {
130 Some(current_visibility) => {
131 builder.replace(current_visibility.syntax().text_range(), missing_visibility)
132 }
133 None => builder.insert(offset, format!("{} ", missing_visibility)),
134 },
109 } 135 }
110 }) 136 })
111} 137}
@@ -113,24 +139,30 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
113fn target_data_for_def( 139fn target_data_for_def(
114 db: &dyn HirDatabase, 140 db: &dyn HirDatabase,
115 def: hir::ModuleDef, 141 def: hir::ModuleDef,
116) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> { 142) -> Option<(TextSize, Option<ast::Visibility>, TextRange, FileId, Option<hir::Name>)> {
117 fn offset_target_and_file_id<S, Ast>( 143 fn offset_target_and_file_id<S, Ast>(
118 db: &dyn HirDatabase, 144 db: &dyn HirDatabase,
119 x: S, 145 x: S,
120 ) -> (TextSize, TextRange, FileId) 146 ) -> (TextSize, Option<ast::Visibility>, TextRange, FileId)
121 where 147 where
122 S: HasSource<Ast = Ast>, 148 S: HasSource<Ast = Ast>,
123 Ast: AstNode, 149 Ast: AstNode + ast::VisibilityOwner,
124 { 150 {
125 let source = x.source(db); 151 let source = x.source(db);
126 let in_file_syntax = source.syntax(); 152 let in_file_syntax = source.syntax();
127 let file_id = in_file_syntax.file_id; 153 let file_id = in_file_syntax.file_id;
128 let syntax = in_file_syntax.value; 154 let syntax = in_file_syntax.value;
129 (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast())) 155 let current_visibility = source.value.visibility();
156 (
157 vis_offset(syntax),
158 current_visibility,
159 syntax.text_range(),
160 file_id.original_file(db.upcast()),
161 )
130 } 162 }
131 163
132 let target_name; 164 let target_name;
133 let (offset, target, target_file) = match def { 165 let (offset, current_visibility, target, target_file) = match def {
134 hir::ModuleDef::Function(f) => { 166 hir::ModuleDef::Function(f) => {
135 target_name = Some(f.name(db)); 167 target_name = Some(f.name(db));
136 offset_target_and_file_id(db, f) 168 offset_target_and_file_id(db, f)
@@ -164,13 +196,13 @@ fn target_data_for_def(
164 let in_file_source = m.declaration_source(db)?; 196 let in_file_source = m.declaration_source(db)?;
165 let file_id = in_file_source.file_id.original_file(db.upcast()); 197 let file_id = in_file_source.file_id.original_file(db.upcast());
166 let syntax = in_file_source.value.syntax(); 198 let syntax = in_file_source.value.syntax();
167 (vis_offset(syntax), syntax.text_range(), file_id) 199 (vis_offset(syntax), in_file_source.value.visibility(), syntax.text_range(), file_id)
168 } 200 }
169 // Enum variants can't be private, we can't modify builtin types 201 // Enum variants can't be private, we can't modify builtin types
170 hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None, 202 hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
171 }; 203 };
172 204
173 Some((offset, target, target_file, target_name)) 205 Some((offset, current_visibility, target, target_file, target_name))
174} 206}
175 207
176#[cfg(test)] 208#[cfg(test)]
@@ -523,6 +555,34 @@ struct Bar;
523 } 555 }
524 556
525 #[test] 557 #[test]
558 fn replaces_pub_crate_with_pub() {
559 check_assist(
560 fix_visibility,
561 r"
562//- /main.rs crate:a deps:foo
563foo::Bar<|>
564//- /lib.rs crate:foo
565pub(crate) struct Bar;
566",
567 r"$0pub struct Bar;
568",
569 );
570 check_assist(
571 fix_visibility,
572 r"
573//- /main.rs crate:a deps:foo
574fn main() {
575 foo::Foo { <|>bar: () };
576}
577//- /lib.rs crate:foo
578pub struct Foo { pub(crate) bar: () }
579",
580 r"pub struct Foo { $0pub bar: () }
581",
582 );
583 }
584
585 #[test]
526 #[ignore] 586 #[ignore]
527 // FIXME handle reexports properly 587 // FIXME handle reexports properly
528 fn fix_visibility_of_reexport() { 588 fn fix_visibility_of_reexport() {