]>
Commit | Line | Data |
---|---|---|
487cf647 | 1 | use hir::{db::HirDatabase, HasSource, HasVisibility, ModuleDef, PathResolution, ScopeDef}; |
064997fb FG |
2 | use ide_db::base_db::FileId; |
3 | use syntax::{ | |
4 | ast::{self, HasVisibility as _}, | |
5 | AstNode, TextRange, TextSize, | |
6 | }; | |
7 | ||
8 | use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; | |
9 | ||
10 | // FIXME: this really should be a fix for diagnostic, rather than an assist. | |
11 | ||
12 | // Assist: fix_visibility | |
13 | // | |
14 | // Makes inaccessible item public. | |
15 | // | |
16 | // ``` | |
17 | // mod m { | |
18 | // fn frobnicate() {} | |
19 | // } | |
20 | // fn main() { | |
487cf647 | 21 | // m::frobnicate$0(); |
064997fb FG |
22 | // } |
23 | // ``` | |
24 | // -> | |
25 | // ``` | |
26 | // mod m { | |
27 | // $0pub(crate) fn frobnicate() {} | |
28 | // } | |
29 | // fn main() { | |
487cf647 | 30 | // m::frobnicate(); |
064997fb FG |
31 | // } |
32 | // ``` | |
33 | pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { | |
34 | add_vis_to_referenced_module_def(acc, ctx) | |
35 | .or_else(|| add_vis_to_referenced_record_field(acc, ctx)) | |
36 | } | |
37 | ||
38 | fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { | |
39 | let path: ast::Path = ctx.find_node_at_offset()?; | |
487cf647 FG |
40 | let qualifier = path.qualifier()?; |
41 | let name_ref = path.segment()?.name_ref()?; | |
42 | let qualifier_res = ctx.sema.resolve_path(&qualifier)?; | |
43 | let PathResolution::Def(ModuleDef::Module(module)) = qualifier_res else { return None; }; | |
44 | let (_, def) = module | |
45 | .scope(ctx.db(), None) | |
46 | .into_iter() | |
47 | .find(|(name, _)| name.to_smol_str() == name_ref.text().as_str())?; | |
48 | let ScopeDef::ModuleDef(def) = def else { return None; }; | |
064997fb FG |
49 | |
50 | let current_module = ctx.sema.scope(path.syntax())?.module(); | |
51 | let target_module = def.module(ctx.db())?; | |
52 | ||
53 | if def.visibility(ctx.db()).is_visible_from(ctx.db(), current_module.into()) { | |
54 | return None; | |
55 | }; | |
56 | ||
57 | let (offset, current_visibility, target, target_file, target_name) = | |
58 | target_data_for_def(ctx.db(), def)?; | |
59 | ||
60 | let missing_visibility = | |
61 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | |
62 | ||
63 | let assist_label = match target_name { | |
487cf647 FG |
64 | None => format!("Change visibility to {missing_visibility}"), |
65 | Some(name) => format!("Change visibility of {name} to {missing_visibility}"), | |
064997fb FG |
66 | }; |
67 | ||
68 | acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { | |
69 | builder.edit_file(target_file); | |
70 | match ctx.config.snippet_cap { | |
71 | Some(cap) => match current_visibility { | |
72 | Some(current_visibility) => builder.replace_snippet( | |
73 | cap, | |
74 | current_visibility.syntax().text_range(), | |
487cf647 | 75 | format!("$0{missing_visibility}"), |
064997fb | 76 | ), |
487cf647 | 77 | None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")), |
064997fb FG |
78 | }, |
79 | None => match current_visibility { | |
80 | Some(current_visibility) => { | |
81 | builder.replace(current_visibility.syntax().text_range(), missing_visibility) | |
82 | } | |
487cf647 | 83 | None => builder.insert(offset, format!("{missing_visibility} ")), |
064997fb FG |
84 | }, |
85 | } | |
86 | }) | |
87 | } | |
88 | ||
89 | fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { | |
90 | let record_field: ast::RecordExprField = ctx.find_node_at_offset()?; | |
91 | let (record_field_def, _, _) = ctx.sema.resolve_record_field(&record_field)?; | |
92 | ||
93 | let current_module = ctx.sema.scope(record_field.syntax())?.module(); | |
94 | let visibility = record_field_def.visibility(ctx.db()); | |
95 | if visibility.is_visible_from(ctx.db(), current_module.into()) { | |
96 | return None; | |
97 | } | |
98 | ||
99 | let parent = record_field_def.parent_def(ctx.db()); | |
100 | let parent_name = parent.name(ctx.db()); | |
101 | let target_module = parent.module(ctx.db()); | |
102 | ||
103 | let in_file_source = record_field_def.source(ctx.db())?; | |
104 | let (offset, current_visibility, target) = match in_file_source.value { | |
105 | hir::FieldSource::Named(it) => { | |
106 | let s = it.syntax(); | |
107 | (vis_offset(s), it.visibility(), s.text_range()) | |
108 | } | |
109 | hir::FieldSource::Pos(it) => { | |
110 | let s = it.syntax(); | |
111 | (vis_offset(s), it.visibility(), s.text_range()) | |
112 | } | |
113 | }; | |
114 | ||
115 | let missing_visibility = | |
116 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | |
117 | let target_file = in_file_source.file_id.original_file(ctx.db()); | |
118 | ||
119 | let target_name = record_field_def.name(ctx.db()); | |
120 | let assist_label = | |
487cf647 | 121 | format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}"); |
064997fb FG |
122 | |
123 | acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { | |
124 | builder.edit_file(target_file); | |
125 | match ctx.config.snippet_cap { | |
126 | Some(cap) => match current_visibility { | |
127 | Some(current_visibility) => builder.replace_snippet( | |
128 | cap, | |
129 | current_visibility.syntax().text_range(), | |
487cf647 | 130 | format!("$0{missing_visibility}"), |
064997fb | 131 | ), |
487cf647 | 132 | None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")), |
064997fb FG |
133 | }, |
134 | None => match current_visibility { | |
135 | Some(current_visibility) => { | |
136 | builder.replace(current_visibility.syntax().text_range(), missing_visibility) | |
137 | } | |
487cf647 | 138 | None => builder.insert(offset, format!("{missing_visibility} ")), |
064997fb FG |
139 | }, |
140 | } | |
141 | }) | |
142 | } | |
143 | ||
144 | fn target_data_for_def( | |
145 | db: &dyn HirDatabase, | |
146 | def: hir::ModuleDef, | |
147 | ) -> Option<(TextSize, Option<ast::Visibility>, TextRange, FileId, Option<hir::Name>)> { | |
148 | fn offset_target_and_file_id<S, Ast>( | |
149 | db: &dyn HirDatabase, | |
150 | x: S, | |
151 | ) -> Option<(TextSize, Option<ast::Visibility>, TextRange, FileId)> | |
152 | where | |
153 | S: HasSource<Ast = Ast>, | |
154 | Ast: AstNode + ast::HasVisibility, | |
155 | { | |
156 | let source = x.source(db)?; | |
157 | let in_file_syntax = source.syntax(); | |
158 | let file_id = in_file_syntax.file_id; | |
159 | let syntax = in_file_syntax.value; | |
160 | let current_visibility = source.value.visibility(); | |
161 | Some(( | |
162 | vis_offset(syntax), | |
163 | current_visibility, | |
164 | syntax.text_range(), | |
165 | file_id.original_file(db.upcast()), | |
166 | )) | |
167 | } | |
168 | ||
169 | let target_name; | |
170 | let (offset, current_visibility, target, target_file) = match def { | |
171 | hir::ModuleDef::Function(f) => { | |
172 | target_name = Some(f.name(db)); | |
173 | offset_target_and_file_id(db, f)? | |
174 | } | |
175 | hir::ModuleDef::Adt(adt) => { | |
176 | target_name = Some(adt.name(db)); | |
177 | match adt { | |
178 | hir::Adt::Struct(s) => offset_target_and_file_id(db, s)?, | |
179 | hir::Adt::Union(u) => offset_target_and_file_id(db, u)?, | |
180 | hir::Adt::Enum(e) => offset_target_and_file_id(db, e)?, | |
181 | } | |
182 | } | |
183 | hir::ModuleDef::Const(c) => { | |
184 | target_name = c.name(db); | |
185 | offset_target_and_file_id(db, c)? | |
186 | } | |
187 | hir::ModuleDef::Static(s) => { | |
188 | target_name = Some(s.name(db)); | |
189 | offset_target_and_file_id(db, s)? | |
190 | } | |
191 | hir::ModuleDef::Trait(t) => { | |
192 | target_name = Some(t.name(db)); | |
193 | offset_target_and_file_id(db, t)? | |
194 | } | |
353b0b11 FG |
195 | hir::ModuleDef::TraitAlias(t) => { |
196 | target_name = Some(t.name(db)); | |
197 | offset_target_and_file_id(db, t)? | |
198 | } | |
064997fb FG |
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), in_file_source.value.visibility(), syntax.text_range(), file_id) | |
209 | } | |
210 | // FIXME | |
211 | hir::ModuleDef::Macro(_) => return None, | |
212 | // Enum variants can't be private, we can't modify builtin types | |
213 | hir::ModuleDef::Variant(_) | hir::ModuleDef::BuiltinType(_) => return None, | |
214 | }; | |
215 | ||
216 | Some((offset, current_visibility, target, target_file, target_name)) | |
217 | } | |
218 | ||
219 | #[cfg(test)] | |
220 | mod tests { | |
221 | use crate::tests::{check_assist, check_assist_not_applicable}; | |
222 | ||
223 | use super::*; | |
224 | ||
225 | #[test] | |
226 | fn fix_visibility_of_fn() { | |
227 | check_assist( | |
228 | fix_visibility, | |
229 | r"mod foo { fn foo() {} } | |
230 | fn main() { foo::foo$0() } ", | |
231 | r"mod foo { $0pub(crate) fn foo() {} } | |
232 | fn main() { foo::foo() } ", | |
233 | ); | |
234 | check_assist_not_applicable( | |
235 | fix_visibility, | |
236 | r"mod foo { pub fn foo() {} } | |
237 | fn main() { foo::foo$0() } ", | |
238 | ) | |
239 | } | |
240 | ||
241 | #[test] | |
242 | fn fix_visibility_of_adt_in_submodule() { | |
243 | check_assist( | |
244 | fix_visibility, | |
245 | r"mod foo { struct Foo; } | |
246 | fn main() { foo::Foo$0 } ", | |
247 | r"mod foo { $0pub(crate) struct Foo; } | |
248 | fn main() { foo::Foo } ", | |
249 | ); | |
250 | check_assist_not_applicable( | |
251 | fix_visibility, | |
252 | r"mod foo { pub struct Foo; } | |
253 | fn main() { foo::Foo$0 } ", | |
254 | ); | |
255 | check_assist( | |
256 | fix_visibility, | |
257 | r"mod foo { enum Foo; } | |
258 | fn main() { foo::Foo$0 } ", | |
259 | r"mod foo { $0pub(crate) enum Foo; } | |
260 | fn main() { foo::Foo } ", | |
261 | ); | |
262 | check_assist_not_applicable( | |
263 | fix_visibility, | |
264 | r"mod foo { pub enum Foo; } | |
265 | fn main() { foo::Foo$0 } ", | |
266 | ); | |
267 | check_assist( | |
268 | fix_visibility, | |
269 | r"mod foo { union Foo; } | |
270 | fn main() { foo::Foo$0 } ", | |
271 | r"mod foo { $0pub(crate) union Foo; } | |
272 | fn main() { foo::Foo } ", | |
273 | ); | |
274 | check_assist_not_applicable( | |
275 | fix_visibility, | |
276 | r"mod foo { pub union Foo; } | |
277 | fn main() { foo::Foo$0 } ", | |
278 | ); | |
279 | } | |
280 | ||
281 | #[test] | |
282 | fn fix_visibility_of_adt_in_other_file() { | |
283 | check_assist( | |
284 | fix_visibility, | |
285 | r" | |
286 | //- /main.rs | |
287 | mod foo; | |
288 | fn main() { foo::Foo$0 } | |
289 | ||
290 | //- /foo.rs | |
291 | struct Foo; | |
292 | ", | |
293 | r"$0pub(crate) struct Foo; | |
294 | ", | |
295 | ); | |
296 | } | |
297 | ||
298 | #[test] | |
299 | fn fix_visibility_of_struct_field() { | |
300 | check_assist( | |
301 | fix_visibility, | |
302 | r"mod foo { pub struct Foo { bar: (), } } | |
303 | fn main() { foo::Foo { $0bar: () }; } ", | |
304 | r"mod foo { pub struct Foo { $0pub(crate) bar: (), } } | |
305 | fn main() { foo::Foo { bar: () }; } ", | |
306 | ); | |
307 | check_assist( | |
308 | fix_visibility, | |
309 | r" | |
310 | //- /lib.rs | |
311 | mod foo; | |
312 | fn main() { foo::Foo { $0bar: () }; } | |
313 | //- /foo.rs | |
314 | pub struct Foo { bar: () } | |
315 | ", | |
316 | r"pub struct Foo { $0pub(crate) bar: () } | |
317 | ", | |
318 | ); | |
319 | check_assist_not_applicable( | |
320 | fix_visibility, | |
321 | r"mod foo { pub struct Foo { pub bar: (), } } | |
322 | fn main() { foo::Foo { $0bar: () }; } ", | |
323 | ); | |
324 | check_assist_not_applicable( | |
325 | fix_visibility, | |
326 | r" | |
327 | //- /lib.rs | |
328 | mod foo; | |
329 | fn main() { foo::Foo { $0bar: () }; } | |
330 | //- /foo.rs | |
331 | pub struct Foo { pub bar: () } | |
332 | ", | |
333 | ); | |
334 | } | |
335 | ||
336 | #[test] | |
337 | fn fix_visibility_of_enum_variant_field() { | |
338 | // Enum variants, as well as their fields, always get the enum's visibility. In fact, rustc | |
339 | // rejects any visibility specifiers on them, so this assist should never fire on them. | |
340 | check_assist_not_applicable( | |
341 | fix_visibility, | |
342 | r"mod foo { pub enum Foo { Bar { bar: () } } } | |
343 | fn main() { foo::Foo::Bar { $0bar: () }; } ", | |
344 | ); | |
345 | check_assist_not_applicable( | |
346 | fix_visibility, | |
347 | r" | |
348 | //- /lib.rs | |
349 | mod foo; | |
350 | fn main() { foo::Foo::Bar { $0bar: () }; } | |
351 | //- /foo.rs | |
352 | pub enum Foo { Bar { bar: () } } | |
353 | ", | |
354 | ); | |
355 | check_assist_not_applicable( | |
356 | fix_visibility, | |
357 | r"mod foo { pub struct Foo { pub bar: (), } } | |
358 | fn main() { foo::Foo { $0bar: () }; } ", | |
359 | ); | |
360 | check_assist_not_applicable( | |
361 | fix_visibility, | |
362 | r" | |
363 | //- /lib.rs | |
364 | mod foo; | |
365 | fn main() { foo::Foo { $0bar: () }; } | |
366 | //- /foo.rs | |
367 | pub struct Foo { pub bar: () } | |
368 | ", | |
369 | ); | |
370 | } | |
371 | ||
372 | #[test] | |
373 | fn fix_visibility_of_union_field() { | |
374 | check_assist( | |
375 | fix_visibility, | |
376 | r"mod foo { pub union Foo { bar: (), } } | |
377 | fn main() { foo::Foo { $0bar: () }; } ", | |
378 | r"mod foo { pub union Foo { $0pub(crate) bar: (), } } | |
379 | fn main() { foo::Foo { bar: () }; } ", | |
380 | ); | |
381 | check_assist( | |
382 | fix_visibility, | |
383 | r" | |
384 | //- /lib.rs | |
385 | mod foo; | |
386 | fn main() { foo::Foo { $0bar: () }; } | |
387 | //- /foo.rs | |
388 | pub union Foo { bar: () } | |
389 | ", | |
390 | r"pub union Foo { $0pub(crate) bar: () } | |
391 | ", | |
392 | ); | |
393 | check_assist_not_applicable( | |
394 | fix_visibility, | |
395 | r"mod foo { pub union Foo { pub bar: (), } } | |
396 | fn main() { foo::Foo { $0bar: () }; } ", | |
397 | ); | |
398 | check_assist_not_applicable( | |
399 | fix_visibility, | |
400 | r" | |
401 | //- /lib.rs | |
402 | mod foo; | |
403 | fn main() { foo::Foo { $0bar: () }; } | |
404 | //- /foo.rs | |
405 | pub union Foo { pub bar: () } | |
406 | ", | |
407 | ); | |
408 | } | |
409 | ||
410 | #[test] | |
411 | fn fix_visibility_of_const() { | |
412 | check_assist( | |
413 | fix_visibility, | |
414 | r"mod foo { const FOO: () = (); } | |
415 | fn main() { foo::FOO$0 } ", | |
416 | r"mod foo { $0pub(crate) const FOO: () = (); } | |
417 | fn main() { foo::FOO } ", | |
418 | ); | |
419 | check_assist_not_applicable( | |
420 | fix_visibility, | |
421 | r"mod foo { pub const FOO: () = (); } | |
422 | fn main() { foo::FOO$0 } ", | |
423 | ); | |
424 | } | |
425 | ||
426 | #[test] | |
427 | fn fix_visibility_of_static() { | |
428 | check_assist( | |
429 | fix_visibility, | |
430 | r"mod foo { static FOO: () = (); } | |
431 | fn main() { foo::FOO$0 } ", | |
432 | r"mod foo { $0pub(crate) static FOO: () = (); } | |
433 | fn main() { foo::FOO } ", | |
434 | ); | |
435 | check_assist_not_applicable( | |
436 | fix_visibility, | |
437 | r"mod foo { pub static FOO: () = (); } | |
438 | fn main() { foo::FOO$0 } ", | |
439 | ); | |
440 | } | |
441 | ||
442 | #[test] | |
443 | fn fix_visibility_of_trait() { | |
444 | check_assist( | |
445 | fix_visibility, | |
446 | r"mod foo { trait Foo { fn foo(&self) {} } } | |
447 | fn main() { let x: &dyn foo::$0Foo; } ", | |
448 | r"mod foo { $0pub(crate) trait Foo { fn foo(&self) {} } } | |
449 | fn main() { let x: &dyn foo::Foo; } ", | |
450 | ); | |
451 | check_assist_not_applicable( | |
452 | fix_visibility, | |
453 | r"mod foo { pub trait Foo { fn foo(&self) {} } } | |
454 | fn main() { let x: &dyn foo::Foo$0; } ", | |
455 | ); | |
456 | } | |
457 | ||
458 | #[test] | |
459 | fn fix_visibility_of_type_alias() { | |
460 | check_assist( | |
461 | fix_visibility, | |
462 | r"mod foo { type Foo = (); } | |
463 | fn main() { let x: foo::Foo$0; } ", | |
464 | r"mod foo { $0pub(crate) type Foo = (); } | |
465 | fn main() { let x: foo::Foo; } ", | |
466 | ); | |
467 | check_assist_not_applicable( | |
468 | fix_visibility, | |
469 | r"mod foo { pub type Foo = (); } | |
470 | fn main() { let x: foo::Foo$0; } ", | |
471 | ); | |
472 | } | |
473 | ||
474 | #[test] | |
475 | fn fix_visibility_of_module() { | |
476 | check_assist( | |
477 | fix_visibility, | |
478 | r"mod foo { mod bar { fn bar() {} } } | |
479 | fn main() { foo::bar$0::bar(); } ", | |
480 | r"mod foo { $0pub(crate) mod bar { fn bar() {} } } | |
481 | fn main() { foo::bar::bar(); } ", | |
482 | ); | |
483 | ||
484 | check_assist( | |
485 | fix_visibility, | |
486 | r" | |
487 | //- /main.rs | |
488 | mod foo; | |
489 | fn main() { foo::bar$0::baz(); } | |
490 | ||
491 | //- /foo.rs | |
492 | mod bar { | |
493 | pub fn baz() {} | |
494 | } | |
495 | ", | |
496 | r"$0pub(crate) mod bar { | |
497 | pub fn baz() {} | |
498 | } | |
499 | ", | |
500 | ); | |
501 | ||
502 | check_assist_not_applicable( | |
503 | fix_visibility, | |
504 | r"mod foo { pub mod bar { pub fn bar() {} } } | |
505 | fn main() { foo::bar$0::bar(); } ", | |
506 | ); | |
507 | } | |
508 | ||
509 | #[test] | |
510 | fn fix_visibility_of_inline_module_in_other_file() { | |
511 | check_assist( | |
512 | fix_visibility, | |
513 | r" | |
514 | //- /main.rs | |
515 | mod foo; | |
516 | fn main() { foo::bar$0::baz(); } | |
517 | ||
518 | //- /foo.rs | |
519 | mod bar; | |
520 | //- /foo/bar.rs | |
521 | pub fn baz() {} | |
522 | ", | |
523 | r"$0pub(crate) mod bar; | |
524 | ", | |
525 | ); | |
526 | } | |
527 | ||
528 | #[test] | |
529 | fn fix_visibility_of_module_declaration_in_other_file() { | |
530 | check_assist( | |
531 | fix_visibility, | |
532 | r" | |
533 | //- /main.rs | |
534 | mod foo; | |
535 | fn main() { foo::bar$0>::baz(); } | |
536 | ||
537 | //- /foo.rs | |
538 | mod bar { | |
539 | pub fn baz() {} | |
540 | } | |
541 | ", | |
542 | r"$0pub(crate) mod bar { | |
543 | pub fn baz() {} | |
544 | } | |
545 | ", | |
546 | ); | |
547 | } | |
548 | ||
549 | #[test] | |
550 | fn adds_pub_when_target_is_in_another_crate() { | |
551 | check_assist( | |
552 | fix_visibility, | |
553 | r" | |
554 | //- /main.rs crate:a deps:foo | |
555 | foo::Bar$0 | |
556 | //- /lib.rs crate:foo | |
557 | struct Bar; | |
558 | ", | |
559 | r"$0pub struct Bar; | |
560 | ", | |
561 | ) | |
562 | } | |
563 | ||
564 | #[test] | |
565 | fn replaces_pub_crate_with_pub() { | |
566 | check_assist( | |
567 | fix_visibility, | |
568 | r" | |
569 | //- /main.rs crate:a deps:foo | |
570 | foo::Bar$0 | |
571 | //- /lib.rs crate:foo | |
572 | pub(crate) struct Bar; | |
573 | ", | |
574 | r"$0pub struct Bar; | |
575 | ", | |
576 | ); | |
577 | check_assist( | |
578 | fix_visibility, | |
579 | r" | |
580 | //- /main.rs crate:a deps:foo | |
581 | fn main() { | |
582 | foo::Foo { $0bar: () }; | |
583 | } | |
584 | //- /lib.rs crate:foo | |
585 | pub struct Foo { pub(crate) bar: () } | |
586 | ", | |
587 | r"pub struct Foo { $0pub bar: () } | |
588 | ", | |
589 | ); | |
590 | } | |
591 | ||
592 | #[test] | |
593 | fn fix_visibility_of_reexport() { | |
594 | // FIXME: broken test, this should fix visibility of the re-export | |
595 | // rather than the struct. | |
596 | check_assist( | |
597 | fix_visibility, | |
598 | r#" | |
599 | mod foo { | |
600 | use bar::Baz; | |
601 | mod bar { pub(super) struct Baz; } | |
602 | } | |
603 | foo::Baz$0 | |
604 | "#, | |
605 | r#" | |
606 | mod foo { | |
607 | use bar::Baz; | |
608 | mod bar { $0pub(crate) struct Baz; } | |
609 | } | |
610 | foo::Baz | |
611 | "#, | |
612 | ) | |
613 | } | |
614 | } |