Skip to content

Commit 7568d07

Browse files
committed
Trigger change_visibility assist when on a path to an invisible def
1 parent 4578154 commit 7568d07

File tree

1 file changed

+328
-1
lines changed

1 file changed

+328
-1
lines changed

crates/ra_assists/src/handlers/change_visibility.rs

Lines changed: 328 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ use ra_syntax::{
55
ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
66
WHITESPACE,
77
},
8-
SyntaxNode, TextSize, T,
8+
SyntaxNode, TextRange, TextSize, T,
99
};
10+
11+
use hir::{db::HirDatabase, HasSource, PathResolution};
1012
use test_utils::tested_by;
1113

1214
use crate::{AssistContext, AssistId, Assists};
@@ -72,6 +74,88 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
7274
})
7375
}
7476

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+
75159
fn vis_offset(node: &SyntaxNode) -> TextSize {
76160
node.children_with_tokens()
77161
.skip_while(|it| match it.kind() {
@@ -191,6 +275,249 @@ mod tests {
191275
)
192276
}
193277

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+
194521
#[test]
195522
fn change_visibility_target() {
196523
check_assist_target(change_visibility, "<|>fn foo() {}", "fn");

0 commit comments

Comments
 (0)