aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs507
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs555
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_assists/src/tests/generated.rs23
4 files changed, 581 insertions, 506 deletions
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index 71d55e0c3..1d9b8e645 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -5,14 +5,11 @@ use ra_syntax::{
5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY, 5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
6 WHITESPACE, 6 WHITESPACE,
7 }, 7 },
8 SyntaxNode, TextRange, TextSize, T, 8 SyntaxNode, TextSize, T,
9}; 9};
10
11use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
12use test_utils::mark; 10use test_utils::mark;
13 11
14use crate::{AssistContext, AssistId, Assists}; 12use crate::{AssistContext, AssistId, Assists};
15use ra_db::FileId;
16 13
17// Assist: change_visibility 14// Assist: change_visibility
18// 15//
@@ -30,8 +27,6 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio
30 return change_vis(acc, vis); 27 return change_vis(acc, vis);
31 } 28 }
32 add_vis(acc, ctx) 29 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))
35} 30}
36 31
37fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 32fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -77,143 +72,6 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
77 }) 72 })
78} 73}
79 74
80fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
81 let path: ast::Path = ctx.find_node_at_offset()?;
82 let path_res = ctx.sema.resolve_path(&path)?;
83 let def = match path_res {
84 PathResolution::Def(def) => def,
85 _ => return None,
86 };
87
88 let current_module = ctx.sema.scope(&path.syntax()).module()?;
89 let target_module = def.module(ctx.db)?;
90
91 let vis = target_module.visibility_of(ctx.db, &def)?;
92 if vis.is_visible_from(ctx.db, current_module.into()) {
93 return None;
94 };
95
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
113fn 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
154fn 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 {
175 hir::ModuleDef::Function(f) => {
176 target_name = Some(f.name(db));
177 offset_target_and_file_id(db, f)
178 }
179 hir::ModuleDef::Adt(adt) => {
180 target_name = Some(adt.name(db));
181 match adt {
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),
185 }
186 }
187 hir::ModuleDef::Const(c) => {
188 target_name = c.name(db);
189 offset_target_and_file_id(db, c)
190 }
191 hir::ModuleDef::Static(s) => {
192 target_name = s.name(db);
193 offset_target_and_file_id(db, s)
194 }
195 hir::ModuleDef::Trait(t) => {
196 target_name = Some(t.name(db));
197 offset_target_and_file_id(db, t)
198 }
199 hir::ModuleDef::TypeAlias(t) => {
200 target_name = Some(t.name(db));
201 offset_target_and_file_id(db, t)
202 }
203 hir::ModuleDef::Module(m) => {
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)
209 }
210 // Enum variants can't be private, we can't modify builtin types
211 hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
212 };
213
214 Some((offset, target, target_file, target_name))
215}
216
217fn vis_offset(node: &SyntaxNode) -> TextSize { 75fn vis_offset(node: &SyntaxNode) -> TextSize {
218 node.children_with_tokens() 76 node.children_with_tokens()
219 .skip_while(|it| match it.kind() { 77 .skip_while(|it| match it.kind() {
@@ -334,193 +192,6 @@ mod tests {
334 } 192 }
335 193
336 #[test] 194 #[test]
337 fn change_visibility_of_fn_via_path() {
338 check_assist(
339 change_visibility,
340 r"mod foo { fn foo() {} }
341 fn main() { foo::foo<|>() } ",
342 r"mod foo { <|>pub(crate) fn foo() {} }
343 fn main() { foo::foo() } ",
344 );
345 check_assist_not_applicable(
346 change_visibility,
347 r"mod foo { pub fn foo() {} }
348 fn main() { foo::foo<|>() } ",
349 )
350 }
351
352 #[test]
353 fn change_visibility_of_adt_in_submodule_via_path() {
354 check_assist(
355 change_visibility,
356 r"mod foo { struct Foo; }
357 fn main() { foo::Foo<|> } ",
358 r"mod foo { <|>pub(crate) struct Foo; }
359 fn main() { foo::Foo } ",
360 );
361 check_assist_not_applicable(
362 change_visibility,
363 r"mod foo { pub struct Foo; }
364 fn main() { foo::Foo<|> } ",
365 );
366 check_assist(
367 change_visibility,
368 r"mod foo { enum Foo; }
369 fn main() { foo::Foo<|> } ",
370 r"mod foo { <|>pub(crate) enum Foo; }
371 fn main() { foo::Foo } ",
372 );
373 check_assist_not_applicable(
374 change_visibility,
375 r"mod foo { pub enum Foo; }
376 fn main() { foo::Foo<|> } ",
377 );
378 check_assist(
379 change_visibility,
380 r"mod foo { union Foo; }
381 fn main() { foo::Foo<|> } ",
382 r"mod foo { <|>pub(crate) union Foo; }
383 fn main() { foo::Foo } ",
384 );
385 check_assist_not_applicable(
386 change_visibility,
387 r"mod foo { pub union Foo; }
388 fn main() { foo::Foo<|> } ",
389 );
390 }
391
392 #[test]
393 fn change_visibility_of_adt_in_other_file_via_path() {
394 check_assist(
395 change_visibility,
396 r"
397 //- /main.rs
398 mod foo;
399 fn main() { foo::Foo<|> }
400
401 //- /foo.rs
402 struct Foo;
403 ",
404 r"<|>pub(crate) struct Foo;
405
406",
407 );
408 }
409
410 #[test]
411 fn change_visibility_of_struct_field_via_path() {
412 check_assist(
413 change_visibility,
414 r"mod foo { pub struct Foo { bar: (), } }
415 fn main() { foo::Foo { <|>bar: () }; } ",
416 r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } }
417 fn main() { foo::Foo { bar: () }; } ",
418 );
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 );
468 check_assist_not_applicable(
469 change_visibility,
470 r"mod foo { pub struct Foo { pub bar: (), } }
471 fn main() { foo::Foo { <|>bar: () }; } ",
472 );
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 );
521 }
522
523 #[test]
524 fn not_applicable_for_enum_variants() { 195 fn not_applicable_for_enum_variants() {
525 check_assist_not_applicable( 196 check_assist_not_applicable(
526 change_visibility, 197 change_visibility,
@@ -530,182 +201,6 @@ mod tests {
530 } 201 }
531 202
532 #[test] 203 #[test]
533 fn change_visibility_of_const_via_path() {
534 check_assist(
535 change_visibility,
536 r"mod foo { const FOO: () = (); }
537 fn main() { foo::FOO<|> } ",
538 r"mod foo { <|>pub(crate) const FOO: () = (); }
539 fn main() { foo::FOO } ",
540 );
541 check_assist_not_applicable(
542 change_visibility,
543 r"mod foo { pub const FOO: () = (); }
544 fn main() { foo::FOO<|> } ",
545 );
546 }
547
548 #[test]
549 fn change_visibility_of_static_via_path() {
550 check_assist(
551 change_visibility,
552 r"mod foo { static FOO: () = (); }
553 fn main() { foo::FOO<|> } ",
554 r"mod foo { <|>pub(crate) static FOO: () = (); }
555 fn main() { foo::FOO } ",
556 );
557 check_assist_not_applicable(
558 change_visibility,
559 r"mod foo { pub static FOO: () = (); }
560 fn main() { foo::FOO<|> } ",
561 );
562 }
563
564 #[test]
565 fn change_visibility_of_trait_via_path() {
566 check_assist(
567 change_visibility,
568 r"mod foo { trait Foo { fn foo(&self) {} } }
569 fn main() { let x: &dyn foo::<|>Foo; } ",
570 r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } }
571 fn main() { let x: &dyn foo::Foo; } ",
572 );
573 check_assist_not_applicable(
574 change_visibility,
575 r"mod foo { pub trait Foo { fn foo(&self) {} } }
576 fn main() { let x: &dyn foo::Foo<|>; } ",
577 );
578 }
579
580 #[test]
581 fn change_visibility_of_type_alias_via_path() {
582 check_assist(
583 change_visibility,
584 r"mod foo { type Foo = (); }
585 fn main() { let x: foo::Foo<|>; } ",
586 r"mod foo { <|>pub(crate) type Foo = (); }
587 fn main() { let x: foo::Foo; } ",
588 );
589 check_assist_not_applicable(
590 change_visibility,
591 r"mod foo { pub type Foo = (); }
592 fn main() { let x: foo::Foo<|>; } ",
593 );
594 }
595
596 #[test]
597 fn change_visibility_of_module_via_path() {
598 check_assist(
599 change_visibility,
600 r"mod foo { mod bar { fn bar() {} } }
601 fn main() { foo::bar<|>::bar(); } ",
602 r"mod foo { <|>pub(crate) mod bar { fn bar() {} } }
603 fn main() { foo::bar::bar(); } ",
604 );
605
606 check_assist(
607 change_visibility,
608 r"
609 //- /main.rs
610 mod foo;
611 fn main() { foo::bar<|>::baz(); }
612
613 //- /foo.rs
614 mod bar {
615 pub fn baz() {}
616 }
617 ",
618 r"<|>pub(crate) mod bar {
619 pub fn baz() {}
620}
621
622",
623 );
624
625 check_assist_not_applicable(
626 change_visibility,
627 r"mod foo { pub mod bar { pub fn bar() {} } }
628 fn main() { foo::bar<|>::bar(); } ",
629 );
630 }
631
632 #[test]
633 fn change_visibility_of_inline_module_in_other_file_via_path() {
634 check_assist(
635 change_visibility,
636 r"
637 //- /main.rs
638 mod foo;
639 fn main() { foo::bar<|>::baz(); }
640
641 //- /foo.rs
642 mod bar;
643
644 //- /foo/bar.rs
645 pub fn baz() {}
646 }
647 ",
648 r"<|>pub(crate) mod bar;
649",
650 );
651 }
652
653 #[test]
654 fn change_visibility_of_module_declaration_in_other_file_via_path() {
655 check_assist(
656 change_visibility,
657 r"//- /main.rs
658 mod foo;
659 fn main() { foo::bar<|>>::baz(); }
660
661 //- /foo.rs
662 mod bar {
663 pub fn baz() {}
664 }",
665 r"<|>pub(crate) mod bar {
666 pub fn baz() {}
667}
668",
669 );
670 }
671
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
708 #[test]
709 fn change_visibility_target() { 204 fn change_visibility_target() {
710 check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); 205 check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
711 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)"); 206 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)");
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
new file mode 100644
index 000000000..48ce07ca5
--- /dev/null
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -0,0 +1,555 @@
1use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
2use ra_db::FileId;
3use ra_syntax::{
4 ast, AstNode,
5 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
6 SyntaxNode, TextRange, TextSize,
7};
8
9use crate::{AssistContext, AssistId, Assists};
10
11// FIXME: this really should be a fix for diagnostic, rather than an assist.
12
13// Assist: fix_visibility
14//
15// Makes inaccessible item public.
16//
17// ```
18// mod m {
19// fn frobnicate() {}
20// }
21// fn main() {
22// m::frobnicate<|>() {}
23// }
24// ```
25// ->
26// ```
27// mod m {
28// pub(crate) fn frobnicate() {}
29// }
30// fn main() {
31// m::frobnicate() {}
32// }
33// ```
34pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
35 add_vis_to_referenced_module_def(acc, ctx)
36 .or_else(|| add_vis_to_referenced_record_field(acc, ctx))
37}
38
39fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let path: ast::Path = ctx.find_node_at_offset()?;
41 let path_res = ctx.sema.resolve_path(&path)?;
42 let def = match path_res {
43 PathResolution::Def(def) => def,
44 _ => return None,
45 };
46
47 let current_module = ctx.sema.scope(&path.syntax()).module()?;
48 let target_module = def.module(ctx.db)?;
49
50 let vis = target_module.visibility_of(ctx.db, &def)?;
51 if vis.is_visible_from(ctx.db, current_module.into()) {
52 return None;
53 };
54
55 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?;
56
57 let missing_visibility =
58 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
59
60 let assist_label = match target_name {
61 None => format!("Change visibility to {}", missing_visibility),
62 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
63 };
64
65 acc.add(AssistId("fix_visibility"), assist_label, target, |edit| {
66 edit.set_file(target_file);
67 edit.insert(offset, format!("{} ", missing_visibility));
68 edit.set_cursor(offset);
69 })
70}
71
72fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
73 let record_field: ast::RecordField = ctx.find_node_at_offset()?;
74 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
75
76 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
77 let visibility = record_field_def.visibility(ctx.db);
78 if visibility.is_visible_from(ctx.db, current_module.into()) {
79 return None;
80 }
81
82 let parent = record_field_def.parent_def(ctx.db);
83 let parent_name = parent.name(ctx.db);
84 let target_module = parent.module(ctx.db);
85
86 let in_file_source = record_field_def.source(ctx.db);
87 let (offset, target) = match in_file_source.value {
88 hir::FieldSource::Named(it) => {
89 let s = it.syntax();
90 (vis_offset(s), s.text_range())
91 }
92 hir::FieldSource::Pos(it) => {
93 let s = it.syntax();
94 (vis_offset(s), s.text_range())
95 }
96 };
97
98 let missing_visibility =
99 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
100 let target_file = in_file_source.file_id.original_file(ctx.db);
101
102 let target_name = record_field_def.name(ctx.db);
103 let assist_label =
104 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
105
106 acc.add(AssistId("fix_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
113fn target_data_for_def(
114 db: &dyn HirDatabase,
115 def: hir::ModuleDef,
116) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> {
117 fn offset_target_and_file_id<S, Ast>(
118 db: &dyn HirDatabase,
119 x: S,
120 ) -> (TextSize, TextRange, FileId)
121 where
122 S: HasSource<Ast = Ast>,
123 Ast: AstNode,
124 {
125 let source = x.source(db);
126 let in_file_syntax = source.syntax();
127 let file_id = in_file_syntax.file_id;
128 let syntax = in_file_syntax.value;
129 (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast()))
130 }
131
132 let target_name;
133 let (offset, target, target_file) = match def {
134 hir::ModuleDef::Function(f) => {
135 target_name = Some(f.name(db));
136 offset_target_and_file_id(db, f)
137 }
138 hir::ModuleDef::Adt(adt) => {
139 target_name = Some(adt.name(db));
140 match adt {
141 hir::Adt::Struct(s) => offset_target_and_file_id(db, s),
142 hir::Adt::Union(u) => offset_target_and_file_id(db, u),
143 hir::Adt::Enum(e) => offset_target_and_file_id(db, e),
144 }
145 }
146 hir::ModuleDef::Const(c) => {
147 target_name = c.name(db);
148 offset_target_and_file_id(db, c)
149 }
150 hir::ModuleDef::Static(s) => {
151 target_name = s.name(db);
152 offset_target_and_file_id(db, s)
153 }
154 hir::ModuleDef::Trait(t) => {
155 target_name = Some(t.name(db));
156 offset_target_and_file_id(db, t)
157 }
158 hir::ModuleDef::TypeAlias(t) => {
159 target_name = Some(t.name(db));
160 offset_target_and_file_id(db, t)
161 }
162 hir::ModuleDef::Module(m) => {
163 target_name = m.name(db);
164 let in_file_source = m.declaration_source(db)?;
165 let file_id = in_file_source.file_id.original_file(db.upcast());
166 let syntax = in_file_source.value.syntax();
167 (vis_offset(syntax), syntax.text_range(), file_id)
168 }
169 // Enum variants can't be private, we can't modify builtin types
170 hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
171 };
172
173 Some((offset, target, target_file, target_name))
174}
175
176fn vis_offset(node: &SyntaxNode) -> TextSize {
177 node.children_with_tokens()
178 .skip_while(|it| match it.kind() {
179 WHITESPACE | COMMENT | ATTR => true,
180 _ => false,
181 })
182 .next()
183 .map(|it| it.text_range().start())
184 .unwrap_or_else(|| node.text_range().start())
185}
186
187#[cfg(test)]
188mod tests {
189 use crate::tests::{check_assist, check_assist_not_applicable};
190
191 use super::*;
192
193 #[test]
194 fn fix_visibility_of_fn() {
195 check_assist(
196 fix_visibility,
197 r"mod foo { fn foo() {} }
198 fn main() { foo::foo<|>() } ",
199 r"mod foo { <|>pub(crate) fn foo() {} }
200 fn main() { foo::foo() } ",
201 );
202 check_assist_not_applicable(
203 fix_visibility,
204 r"mod foo { pub fn foo() {} }
205 fn main() { foo::foo<|>() } ",
206 )
207 }
208
209 #[test]
210 fn fix_visibility_of_adt_in_submodule() {
211 check_assist(
212 fix_visibility,
213 r"mod foo { struct Foo; }
214 fn main() { foo::Foo<|> } ",
215 r"mod foo { <|>pub(crate) struct Foo; }
216 fn main() { foo::Foo } ",
217 );
218 check_assist_not_applicable(
219 fix_visibility,
220 r"mod foo { pub struct Foo; }
221 fn main() { foo::Foo<|> } ",
222 );
223 check_assist(
224 fix_visibility,
225 r"mod foo { enum Foo; }
226 fn main() { foo::Foo<|> } ",
227 r"mod foo { <|>pub(crate) enum Foo; }
228 fn main() { foo::Foo } ",
229 );
230 check_assist_not_applicable(
231 fix_visibility,
232 r"mod foo { pub enum Foo; }
233 fn main() { foo::Foo<|> } ",
234 );
235 check_assist(
236 fix_visibility,
237 r"mod foo { union Foo; }
238 fn main() { foo::Foo<|> } ",
239 r"mod foo { <|>pub(crate) union Foo; }
240 fn main() { foo::Foo } ",
241 );
242 check_assist_not_applicable(
243 fix_visibility,
244 r"mod foo { pub union Foo; }
245 fn main() { foo::Foo<|> } ",
246 );
247 }
248
249 #[test]
250 fn fix_visibility_of_adt_in_other_file() {
251 check_assist(
252 fix_visibility,
253 r"
254 //- /main.rs
255 mod foo;
256 fn main() { foo::Foo<|> }
257
258 //- /foo.rs
259 struct Foo;
260 ",
261 r"<|>pub(crate) struct Foo;
262
263",
264 );
265 }
266
267 #[test]
268 fn fix_visibility_of_struct_field() {
269 check_assist(
270 fix_visibility,
271 r"mod foo { pub struct Foo { bar: (), } }
272 fn main() { foo::Foo { <|>bar: () }; } ",
273 r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } }
274 fn main() { foo::Foo { bar: () }; } ",
275 );
276 check_assist(
277 fix_visibility,
278 r"//- /lib.rs
279 mod foo;
280 fn main() { foo::Foo { <|>bar: () }; }
281 //- /foo.rs
282 pub struct Foo { bar: () }
283 ",
284 r"pub struct Foo { <|>pub(crate) bar: () }
285
286",
287 );
288 check_assist_not_applicable(
289 fix_visibility,
290 r"mod foo { pub struct Foo { pub bar: (), } }
291 fn main() { foo::Foo { <|>bar: () }; } ",
292 );
293 check_assist_not_applicable(
294 fix_visibility,
295 r"//- /lib.rs
296 mod foo;
297 fn main() { foo::Foo { <|>bar: () }; }
298 //- /foo.rs
299 pub struct Foo { pub bar: () }
300 ",
301 );
302 }
303
304 #[test]
305 fn fix_visibility_of_enum_variant_field() {
306 check_assist(
307 fix_visibility,
308 r"mod foo { pub enum Foo { Bar { bar: () } } }
309 fn main() { foo::Foo::Bar { <|>bar: () }; } ",
310 r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } }
311 fn main() { foo::Foo::Bar { bar: () }; } ",
312 );
313 check_assist(
314 fix_visibility,
315 r"//- /lib.rs
316 mod foo;
317 fn main() { foo::Foo::Bar { <|>bar: () }; }
318 //- /foo.rs
319 pub enum Foo { Bar { bar: () } }
320 ",
321 r"pub enum Foo { Bar { <|>pub(crate) bar: () } }
322
323",
324 );
325 check_assist_not_applicable(
326 fix_visibility,
327 r"mod foo { pub struct Foo { pub bar: (), } }
328 fn main() { foo::Foo { <|>bar: () }; } ",
329 );
330 check_assist_not_applicable(
331 fix_visibility,
332 r"//- /lib.rs
333 mod foo;
334 fn main() { foo::Foo { <|>bar: () }; }
335 //- /foo.rs
336 pub struct Foo { pub bar: () }
337 ",
338 );
339 }
340
341 #[test]
342 #[ignore]
343 // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields
344 fn fix_visibility_of_union_field() {
345 check_assist(
346 fix_visibility,
347 r"mod foo { pub union Foo { bar: (), } }
348 fn main() { foo::Foo { <|>bar: () }; } ",
349 r"mod foo { pub union Foo { <|>pub(crate) bar: (), } }
350 fn main() { foo::Foo { bar: () }; } ",
351 );
352 check_assist(
353 fix_visibility,
354 r"//- /lib.rs
355 mod foo;
356 fn main() { foo::Foo { <|>bar: () }; }
357 //- /foo.rs
358 pub union Foo { bar: () }
359 ",
360 r"pub union Foo { <|>pub(crate) bar: () }
361
362",
363 );
364 check_assist_not_applicable(
365 fix_visibility,
366 r"mod foo { pub union Foo { pub bar: (), } }
367 fn main() { foo::Foo { <|>bar: () }; } ",
368 );
369 check_assist_not_applicable(
370 fix_visibility,
371 r"//- /lib.rs
372 mod foo;
373 fn main() { foo::Foo { <|>bar: () }; }
374 //- /foo.rs
375 pub union Foo { pub bar: () }
376 ",
377 );
378 }
379
380 #[test]
381 fn fix_visibility_of_const() {
382 check_assist(
383 fix_visibility,
384 r"mod foo { const FOO: () = (); }
385 fn main() { foo::FOO<|> } ",
386 r"mod foo { <|>pub(crate) const FOO: () = (); }
387 fn main() { foo::FOO } ",
388 );
389 check_assist_not_applicable(
390 fix_visibility,
391 r"mod foo { pub const FOO: () = (); }
392 fn main() { foo::FOO<|> } ",
393 );
394 }
395
396 #[test]
397 fn fix_visibility_of_static() {
398 check_assist(
399 fix_visibility,
400 r"mod foo { static FOO: () = (); }
401 fn main() { foo::FOO<|> } ",
402 r"mod foo { <|>pub(crate) static FOO: () = (); }
403 fn main() { foo::FOO } ",
404 );
405 check_assist_not_applicable(
406 fix_visibility,
407 r"mod foo { pub static FOO: () = (); }
408 fn main() { foo::FOO<|> } ",
409 );
410 }
411
412 #[test]
413 fn fix_visibility_of_trait() {
414 check_assist(
415 fix_visibility,
416 r"mod foo { trait Foo { fn foo(&self) {} } }
417 fn main() { let x: &dyn foo::<|>Foo; } ",
418 r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } }
419 fn main() { let x: &dyn foo::Foo; } ",
420 );
421 check_assist_not_applicable(
422 fix_visibility,
423 r"mod foo { pub trait Foo { fn foo(&self) {} } }
424 fn main() { let x: &dyn foo::Foo<|>; } ",
425 );
426 }
427
428 #[test]
429 fn fix_visibility_of_type_alias() {
430 check_assist(
431 fix_visibility,
432 r"mod foo { type Foo = (); }
433 fn main() { let x: foo::Foo<|>; } ",
434 r"mod foo { <|>pub(crate) type Foo = (); }
435 fn main() { let x: foo::Foo; } ",
436 );
437 check_assist_not_applicable(
438 fix_visibility,
439 r"mod foo { pub type Foo = (); }
440 fn main() { let x: foo::Foo<|>; } ",
441 );
442 }
443
444 #[test]
445 fn fix_visibility_of_module() {
446 check_assist(
447 fix_visibility,
448 r"mod foo { mod bar { fn bar() {} } }
449 fn main() { foo::bar<|>::bar(); } ",
450 r"mod foo { <|>pub(crate) mod bar { fn bar() {} } }
451 fn main() { foo::bar::bar(); } ",
452 );
453
454 check_assist(
455 fix_visibility,
456 r"
457 //- /main.rs
458 mod foo;
459 fn main() { foo::bar<|>::baz(); }
460
461 //- /foo.rs
462 mod bar {
463 pub fn baz() {}
464 }
465 ",
466 r"<|>pub(crate) mod bar {
467 pub fn baz() {}
468}
469
470",
471 );
472
473 check_assist_not_applicable(
474 fix_visibility,
475 r"mod foo { pub mod bar { pub fn bar() {} } }
476 fn main() { foo::bar<|>::bar(); } ",
477 );
478 }
479
480 #[test]
481 fn fix_visibility_of_inline_module_in_other_file() {
482 check_assist(
483 fix_visibility,
484 r"
485 //- /main.rs
486 mod foo;
487 fn main() { foo::bar<|>::baz(); }
488
489 //- /foo.rs
490 mod bar;
491
492 //- /foo/bar.rs
493 pub fn baz() {}
494 }
495 ",
496 r"<|>pub(crate) mod bar;
497",
498 );
499 }
500
501 #[test]
502 fn fix_visibility_of_module_declaration_in_other_file() {
503 check_assist(
504 fix_visibility,
505 r"//- /main.rs
506 mod foo;
507 fn main() { foo::bar<|>>::baz(); }
508
509 //- /foo.rs
510 mod bar {
511 pub fn baz() {}
512 }",
513 r"<|>pub(crate) mod bar {
514 pub fn baz() {}
515}
516",
517 );
518 }
519
520 #[test]
521 fn adds_pub_when_target_is_in_another_crate() {
522 check_assist(
523 fix_visibility,
524 r"//- /main.rs crate:a deps:foo
525 foo::Bar<|>
526 //- /lib.rs crate:foo
527 struct Bar;",
528 r"<|>pub struct Bar;
529",
530 )
531 }
532
533 #[test]
534 #[ignore]
535 // FIXME handle reexports properly
536 fn fix_visibility_of_reexport() {
537 check_assist(
538 fix_visibility,
539 r"
540 mod foo {
541 use bar::Baz;
542 mod bar { pub(super) struct Baz; }
543 }
544 foo::Baz<|>
545 ",
546 r"
547 mod foo {
548 <|>pub(crate) use bar::Baz;
549 mod bar { pub(super) struct Baz; }
550 }
551 foo::Baz
552 ",
553 )
554 }
555}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index a4a497c73..464bc03dd 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -116,6 +116,7 @@ mod handlers {
116 mod change_visibility; 116 mod change_visibility;
117 mod early_return; 117 mod early_return;
118 mod fill_match_arms; 118 mod fill_match_arms;
119 mod fix_visibility;
119 mod flip_binexpr; 120 mod flip_binexpr;
120 mod flip_comma; 121 mod flip_comma;
121 mod flip_trait_bound; 122 mod flip_trait_bound;
@@ -154,6 +155,7 @@ mod handlers {
154 change_visibility::change_visibility, 155 change_visibility::change_visibility,
155 early_return::convert_to_guarded_return, 156 early_return::convert_to_guarded_return,
156 fill_match_arms::fill_match_arms, 157 fill_match_arms::fill_match_arms,
158 fix_visibility::fix_visibility,
157 flip_binexpr::flip_binexpr, 159 flip_binexpr::flip_binexpr,
158 flip_comma::flip_comma, 160 flip_comma::flip_comma,
159 flip_trait_bound::flip_trait_bound, 161 flip_trait_bound::flip_trait_bound,
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index cd6129dc5..417ee89a8 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -345,6 +345,29 @@ fn handle(action: Action) {
345} 345}
346 346
347#[test] 347#[test]
348fn doctest_fix_visibility() {
349 check_doc_test(
350 "fix_visibility",
351 r#####"
352mod m {
353 fn frobnicate() {}
354}
355fn main() {
356 m::frobnicate<|>() {}
357}
358"#####,
359 r#####"
360mod m {
361 pub(crate) fn frobnicate() {}
362}
363fn main() {
364 m::frobnicate() {}
365}
366"#####,
367 )
368}
369
370#[test]
348fn doctest_flip_binexpr() { 371fn doctest_flip_binexpr() {
349 check_doc_test( 372 check_doc_test(
350 "flip_binexpr", 373 "flip_binexpr",