diff options
-rw-r--r-- | crates/ra_assists/src/handlers/change_visibility.rs | 329 |
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 | |||
11 | use hir::{db::HirDatabase, HasSource, PathResolution}; | ||
10 | use test_utils::tested_by; | 12 | use test_utils::tested_by; |
11 | 13 | ||
12 | use crate::{AssistContext, AssistId, Assists}; | 14 | use crate::{AssistContext, AssistId, Assists}; |
@@ -72,6 +74,88 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
72 | }) | 74 | }) |
73 | } | 75 | } |
74 | 76 | ||
77 | fn 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 | |||
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()) | ||
157 | } | ||
158 | |||
75 | fn vis_offset(node: &SyntaxNode) -> TextSize { | 159 | fn 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)"); |