aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers')
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs516
1 files changed, 515 insertions, 1 deletions
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index e631766ef..40cf4b422 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -5,11 +5,14 @@ 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, TextSize, T, 8 SyntaxNode, TextRange, TextSize, T,
9}; 9};
10
11use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
10use test_utils::tested_by; 12use test_utils::tested_by;
11 13
12use crate::{AssistContext, AssistId, Assists}; 14use crate::{AssistContext, AssistId, Assists};
15use ra_db::FileId;
13 16
14// Assist: change_visibility 17// Assist: change_visibility
15// 18//
@@ -27,6 +30,8 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio
27 return change_vis(acc, vis); 30 return change_vis(acc, vis);
28 } 31 }
29 add_vis(acc, ctx) 32 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))
30} 35}
31 36
32fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 37fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -72,6 +77,143 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
72 }) 77 })
73} 78}
74 79
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
75fn vis_offset(node: &SyntaxNode) -> TextSize { 217fn vis_offset(node: &SyntaxNode) -> TextSize {
76 node.children_with_tokens() 218 node.children_with_tokens()
77 .skip_while(|it| match it.kind() { 219 .skip_while(|it| match it.kind() {
@@ -192,6 +334,378 @@ mod tests {
192 } 334 }
193 335
194 #[test] 336 #[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() {
525 check_assist_not_applicable(
526 change_visibility,
527 r"mod foo { pub enum Foo {Foo1} }
528 fn main() { foo::Foo::Foo1<|> } ",
529 );
530 }
531
532 #[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]
195 fn change_visibility_target() { 709 fn change_visibility_target() {
196 check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); 710 check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
197 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)"); 711 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)");