aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs329
1 files changed, 328 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..573f94a46 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -5,8 +5,10 @@ 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, PathResolution};
10use test_utils::tested_by; 12use test_utils::tested_by;
11 13
12use crate::{AssistContext, AssistId, Assists}; 14use crate::{AssistContext, AssistId, Assists};
@@ -72,6 +74,88 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
72 }) 74 })
73} 75}
74 76
77fn add_missing_vis(ctx: AssistCtx) -> Option<Assist> {
78 let path: ast::Path = ctx.find_node_at_offset()?;
79 let path_res = dbg!(ctx.sema.resolve_path(&path))?;
80 let def = match path_res {
81 PathResolution::Def(def) => def,
82 _ => return None,
83 };
84 dbg!(&def);
85
86 let current_module = dbg!(ctx.sema.scope(&path.syntax()).module())?;
87 let target_module = dbg!(def.module(ctx.db))?;
88
89 let vis = dbg!(target_module.visibility_of(ctx.db, &def))?;
90 if vis.is_visible_from(ctx.db, current_module.into()) {
91 return None;
92 };
93 let target_name;
94
95 let (offset, target) = match def {
96 hir::ModuleDef::Function(f) => {
97 target_name = Some(f.name(ctx.db));
98 offset_and_target(ctx.db, f)
99 }
100 hir::ModuleDef::Adt(adt) => {
101 target_name = Some(adt.name(ctx.db));
102 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),
106 }
107 }
108 hir::ModuleDef::Const(c) => {
109 target_name = c.name(ctx.db);
110 offset_and_target(ctx.db, c)
111 }
112 hir::ModuleDef::Static(s) => {
113 target_name = s.name(ctx.db);
114 offset_and_target(ctx.db, s)
115 }
116 hir::ModuleDef::Trait(t) => {
117 target_name = Some(t.name(ctx.db));
118 offset_and_target(ctx.db, t)
119 }
120 hir::ModuleDef::TypeAlias(t) => {
121 target_name = Some(t.name(ctx.db));
122 offset_and_target(ctx.db, t)
123 }
124 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())
129 }
130 // Enum variants can't be private, we can't modify builtin types
131 hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
132 };
133
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
149fn offset_and_target<S, Ast>(db: &dyn HirDatabase, x: S) -> (TextSize, TextRange)
150where
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())
157}
158
75fn vis_offset(node: &SyntaxNode) -> TextSize { 159fn vis_offset(node: &SyntaxNode) -> TextSize {
76 node.children_with_tokens() 160 node.children_with_tokens()
77 .skip_while(|it| match it.kind() { 161 .skip_while(|it| match it.kind() {
@@ -192,6 +276,249 @@ mod tests {
192 } 276 }
193 277
194 #[test] 278 #[test]
279 fn change_visibility_of_fn_via_path() {
280 check_assist(
281 change_visibility,
282 r"mod foo { fn foo() {} }
283 fn main() { foo::foo<|>() } ",
284 r"mod foo { <|>pub(crate) fn foo() {} }
285 fn main() { foo::foo() } ",
286 );
287 check_assist_not_applicable(
288 change_visibility,
289 r"mod foo { pub fn foo() {} }
290 fn main() { foo::foo<|>() } ",
291 )
292 }
293
294 #[test]
295 fn change_visibility_of_adt_in_submodule_via_path() {
296 check_assist(
297 change_visibility,
298 r"mod foo { struct Foo; }
299 fn main() { foo::Foo<|> } ",
300 r"mod foo { <|>pub(crate) struct Foo; }
301 fn main() { foo::Foo } ",
302 );
303 check_assist_not_applicable(
304 change_visibility,
305 r"mod foo { pub struct Foo; }
306 fn main() { foo::Foo<|> } ",
307 );
308 check_assist(
309 change_visibility,
310 r"mod foo { enum Foo; }
311 fn main() { foo::Foo<|> } ",
312 r"mod foo { <|>pub(crate) enum Foo; }
313 fn main() { foo::Foo } ",
314 );
315 check_assist_not_applicable(
316 change_visibility,
317 r"mod foo { pub enum Foo; }
318 fn main() { foo::Foo<|> } ",
319 );
320 check_assist(
321 change_visibility,
322 r"mod foo { union Foo; }
323 fn main() { foo::Foo<|> } ",
324 r"mod foo { <|>pub(crate) union Foo; }
325 fn main() { foo::Foo } ",
326 );
327 check_assist_not_applicable(
328 change_visibility,
329 r"mod foo { pub union Foo; }
330 fn main() { foo::Foo<|> } ",
331 );
332 }
333
334 #[test]
335 fn change_visibility_of_adt_in_other_file_via_path() {
336 check_assist(
337 change_visibility,
338 r"
339 //- /main.rs
340 mod foo;
341 fn main() { foo::Foo<|> }
342
343 //- /foo.rs
344 struct Foo;
345 ",
346 r"<|>pub(crate) struct Foo;
347
348",
349 );
350 }
351
352 #[test]
353 // FIXME this requires a separate implementation, struct fields are not a ast::Path
354 fn change_visibility_of_struct_field_via_path() {
355 check_assist(
356 change_visibility,
357 r"mod foo { pub struct Foo { bar: (), } }
358 fn main() { foo::Foo { <|>bar: () }; } ",
359 r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } }
360 fn main() { foo::Foo { bar: () }; } ",
361 );
362 check_assist_not_applicable(
363 change_visibility,
364 r"mod foo { pub struct Foo { pub bar: (), } }
365 fn main() { foo::Foo { <|>bar: () }; } ",
366 );
367 }
368
369 #[test]
370 fn not_applicable_for_enum_variants() {
371 check_assist_not_applicable(
372 change_visibility,
373 r"mod foo { pub enum Foo {Foo1} }
374 fn main() { foo::Foo::Foo1<|> } ",
375 );
376 }
377
378 #[test]
379 fn change_visibility_of_const_via_path() {
380 check_assist(
381 change_visibility,
382 r"mod foo { const FOO: () = (); }
383 fn main() { foo::FOO<|> } ",
384 r"mod foo { <|>pub(crate) const FOO: () = (); }
385 fn main() { foo::FOO } ",
386 );
387 check_assist_not_applicable(
388 change_visibility,
389 r"mod foo { pub const FOO: () = (); }
390 fn main() { foo::FOO<|> } ",
391 );
392 }
393
394 #[test]
395 fn change_visibility_of_static_via_path() {
396 check_assist(
397 change_visibility,
398 r"mod foo { static FOO: () = (); }
399 fn main() { foo::FOO<|> } ",
400 r"mod foo { <|>pub(crate) static FOO: () = (); }
401 fn main() { foo::FOO } ",
402 );
403 check_assist_not_applicable(
404 change_visibility,
405 r"mod foo { pub static FOO: () = (); }
406 fn main() { foo::FOO<|> } ",
407 );
408 }
409
410 #[test]
411 fn change_visibility_of_trait_via_path() {
412 check_assist(
413 change_visibility,
414 r"mod foo { trait Foo { fn foo(&self) {} } }
415 fn main() { let x: &dyn foo::<|>Foo; } ",
416 r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } }
417 fn main() { let x: &dyn foo::Foo; } ",
418 );
419 check_assist_not_applicable(
420 change_visibility,
421 r"mod foo { pub trait Foo { fn foo(&self) {} } }
422 fn main() { let x: &dyn foo::Foo<|>; } ",
423 );
424 }
425
426 #[test]
427 fn change_visibility_of_type_alias_via_path() {
428 check_assist(
429 change_visibility,
430 r"mod foo { type Foo = (); }
431 fn main() { let x: foo::Foo<|>; } ",
432 r"mod foo { <|>pub(crate) type Foo = (); }
433 fn main() { let x: foo::Foo; } ",
434 );
435 check_assist_not_applicable(
436 change_visibility,
437 r"mod foo { pub type Foo = (); }
438 fn main() { let x: foo::Foo<|>; } ",
439 );
440 }
441
442 #[test]
443 fn change_visibility_of_module_via_path() {
444 check_assist(
445 change_visibility,
446 r"mod foo { mod bar { fn bar() {} } }
447 fn main() { foo::bar<|>::bar(); } ",
448 r"mod foo { <|>pub(crate) mod bar { fn bar() {} } }
449 fn main() { foo::bar::bar(); } ",
450 );
451
452 check_assist(
453 change_visibility,
454 r"
455 //- /main.rs
456 mod foo;
457 fn main() { foo::bar<|>::baz(); }
458
459 //- /foo.rs
460 mod bar {
461 pub fn baz() {}
462 }
463 ",
464 r"<|>pub(crate) mod bar {
465 pub fn baz() {}
466}
467
468",
469 );
470
471 check_assist_not_applicable(
472 change_visibility,
473 r"mod foo { pub mod bar { pub fn bar() {} } }
474 fn main() { foo::bar<|>::bar(); } ",
475 );
476 }
477
478 #[test]
479 fn change_visibility_of_inline_module_in_other_file_via_path() {
480 check_assist(
481 change_visibility,
482 r"
483 //- /main.rs
484 mod foo;
485 fn main() { foo::bar<|>::baz(); }
486
487 //- /foo.rs
488 mod bar;
489
490 //- /foo/bar.rs
491 pub fn baz() {}
492 }
493 ",
494 r"<|>pub(crate) mod bar;
495",
496 );
497 }
498
499 #[test]
500 fn change_visibility_of_module_declaration_in_other_file_via_path() {
501 check_assist(
502 change_visibility,
503 r"
504 //- /main.rs
505 mod foo;
506 fn main() { foo::bar<|>>::baz(); }
507
508 //- /foo.rs
509 mod bar {
510 pub fn baz() {}
511 }
512 ",
513 r"<|>pub(crate) mod bar {
514 pub fn baz() {}
515}
516
517",
518 );
519 }
520
521 #[test]
195 fn change_visibility_target() { 522 fn change_visibility_target() {
196 check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); 523 check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
197 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)"); 524 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)");