Skip to content

Commit d5366b5

Browse files
committed
Auto merge of rust-lang#16265 - Patryk27:suggest-pub-crate-imports, r=Veykril
fix: Acknowledge `pub(crate)` imports in import suggestions rust-analyzer has logic that discounts suggesting `use`s for private imports, but that logic is unnecessarily strict - for instance given this code: ```rust mod foo { pub struct Foo; } pub(crate) use self::foo::*; mod bar { fn main() { Foo$0; } } ``` ... RA will suggest to add `use crate::foo::Foo;`, which not only makes the code overly verbose (especially in larger code bases), but also is disjoint with what rustc itself suggests. This commit adjusts the logic, so that `pub(crate)` imports are taken into account when generating the suggestions; considering rustc's behavior, I think this change doesn't warrant any extra configuration flag. Note that this is my first commit to RA, so I guess the approach taken here might be suboptimal - certainly feels somewhat hacky, maybe there's some better way of finding out the optimal import path 😅
2 parents e4344f5 + 76aaf17 commit d5366b5

File tree

9 files changed

+106
-18
lines changed

9 files changed

+106
-18
lines changed

crates/hir-def/src/find_path.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,18 @@ fn find_local_import_locations(
551551
if let Some((name, vis)) = data.scope.name_of(item) {
552552
if vis.is_visible_from(db, from) {
553553
let is_private = match vis {
554-
Visibility::Module(private_to) => private_to.local_id == module.local_id,
554+
Visibility::Module(private_mod, private_vis) => {
555+
if private_mod == def_map.module_id(DefMap::ROOT)
556+
&& private_vis.is_explicit()
557+
{
558+
// Treat `pub(crate)` imports as non-private, so
559+
// that we suggest adding `use crate::Foo;` instead
560+
// of `use crate::foo::Foo;` etc.
561+
false
562+
} else {
563+
private_mod.local_id == module.local_id
564+
}
565+
}
555566
Visibility::Public => false,
556567
};
557568
let is_original_def = match item.as_module_def_id() {
@@ -1021,6 +1032,24 @@ $0
10211032
);
10221033
}
10231034

1035+
#[test]
1036+
fn promote_pub_crate_imports() {
1037+
check_found_path(
1038+
r#"
1039+
//- /main.rs
1040+
mod foo;
1041+
pub mod bar { pub struct S; }
1042+
pub(crate) use bar::S;
1043+
//- /foo.rs
1044+
$0
1045+
"#,
1046+
"crate::S",
1047+
"crate::S",
1048+
"crate::S",
1049+
"crate::S",
1050+
);
1051+
}
1052+
10241053
#[test]
10251054
fn import_cycle() {
10261055
check_found_path(

crates/hir-def/src/item_scope.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -628,14 +628,14 @@ impl ItemScope {
628628
.chain(self.values.values_mut().map(|(def, vis, _)| (def, vis)))
629629
.map(|(_, v)| v)
630630
.chain(self.unnamed_trait_imports.values_mut().map(|(vis, _)| vis))
631-
.for_each(|vis| *vis = Visibility::Module(this_module));
631+
.for_each(|vis| *vis = Visibility::Module(this_module, Default::default()));
632632

633633
for (mac, vis, import) in self.macros.values_mut() {
634634
if matches!(mac, MacroId::ProcMacroId(_) if import.is_none()) {
635635
continue;
636636
}
637637

638-
*vis = Visibility::Module(this_module);
638+
*vis = Visibility::Module(this_module, Default::default());
639639
}
640640
}
641641

crates/hir-def/src/nameres.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,8 @@ impl DefMap {
332332
// NB: we use `None` as block here, which would be wrong for implicit
333333
// modules declared by blocks with items. At the moment, we don't use
334334
// this visibility for anything outside IDE, so that's probably OK.
335-
let visibility = Visibility::Module(ModuleId { krate, local_id, block: None });
335+
let visibility =
336+
Visibility::Module(ModuleId { krate, local_id, block: None }, Default::default());
336337
let module_data = ModuleData::new(
337338
ModuleOrigin::BlockExpr { block: block.ast_id, id: block_id },
338339
visibility,

crates/hir-def/src/nameres/path_resolution.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::{
2121
nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
2222
path::{ModPath, PathKind},
2323
per_ns::PerNs,
24-
visibility::{RawVisibility, Visibility},
24+
visibility::{RawVisibility, Visibility, VisibilityExplicity},
2525
AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
2626
};
2727

@@ -94,8 +94,13 @@ impl DefMap {
9494
return None;
9595
}
9696
let types = result.take_types()?;
97+
let mv = if path.is_pub_crate() {
98+
VisibilityExplicity::Explicit
99+
} else {
100+
VisibilityExplicity::Implicit
101+
};
97102
match types {
98-
ModuleDefId::ModuleId(m) => Visibility::Module(m),
103+
ModuleDefId::ModuleId(m) => Visibility::Module(m, mv),
99104
// error: visibility needs to refer to module
100105
_ => {
101106
return None;
@@ -108,11 +113,11 @@ impl DefMap {
108113
// In block expressions, `self` normally refers to the containing non-block module, and
109114
// `super` to its parent (etc.). However, visibilities must only refer to a module in the
110115
// DefMap they're written in, so we restrict them when that happens.
111-
if let Visibility::Module(m) = vis {
116+
if let Visibility::Module(m, mv) = vis {
112117
// ...unless we're resolving visibility for an associated item in an impl.
113118
if self.block_id() != m.block && !within_impl {
114119
cov_mark::hit!(adjust_vis_in_block_def_map);
115-
vis = Visibility::Module(self.module_id(Self::ROOT));
120+
vis = Visibility::Module(self.module_id(Self::ROOT), mv);
116121
tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
117122
}
118123
}

crates/hir-def/src/visibility.rs

+22-8
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,15 @@ impl RawVisibility {
9494
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
9595
pub enum Visibility {
9696
/// Visibility is restricted to a certain module.
97-
Module(ModuleId),
97+
Module(ModuleId, VisibilityExplicity),
9898
/// Visibility is unrestricted.
9999
Public,
100100
}
101101

102102
impl Visibility {
103103
pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
104104
let to_module = match self {
105-
Visibility::Module(m) => m,
105+
Visibility::Module(m, _) => m,
106106
Visibility::Public => return true,
107107
};
108108
// if they're not in the same crate, it can't be visible
@@ -124,7 +124,7 @@ impl Visibility {
124124
mut from_module: LocalModuleId,
125125
) -> bool {
126126
let mut to_module = match self {
127-
Visibility::Module(m) => m,
127+
Visibility::Module(m, _) => m,
128128
Visibility::Public => return true,
129129
};
130130

@@ -181,9 +181,9 @@ impl Visibility {
181181
/// visible in unrelated modules).
182182
pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
183183
match (self, other) {
184-
(Visibility::Module(_) | Visibility::Public, Visibility::Public)
185-
| (Visibility::Public, Visibility::Module(_)) => Some(Visibility::Public),
186-
(Visibility::Module(mod_a), Visibility::Module(mod_b)) => {
184+
(Visibility::Module(_, _) | Visibility::Public, Visibility::Public)
185+
| (Visibility::Public, Visibility::Module(_, _)) => Some(Visibility::Public),
186+
(Visibility::Module(mod_a, vis_a), Visibility::Module(mod_b, vis_b)) => {
187187
if mod_a.krate != mod_b.krate {
188188
return None;
189189
}
@@ -199,12 +199,12 @@ impl Visibility {
199199

200200
if a_ancestors.any(|m| m == mod_b.local_id) {
201201
// B is above A
202-
return Some(Visibility::Module(mod_b));
202+
return Some(Visibility::Module(mod_b, vis_b));
203203
}
204204

205205
if b_ancestors.any(|m| m == mod_a.local_id) {
206206
// A is above B
207-
return Some(Visibility::Module(mod_a));
207+
return Some(Visibility::Module(mod_a, vis_a));
208208
}
209209

210210
None
@@ -213,6 +213,20 @@ impl Visibility {
213213
}
214214
}
215215

216+
/// Whether the item was imported through `pub(crate) use` or just `use`.
217+
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
218+
pub enum VisibilityExplicity {
219+
Explicit,
220+
#[default]
221+
Implicit,
222+
}
223+
224+
impl VisibilityExplicity {
225+
pub fn is_explicit(&self) -> bool {
226+
matches!(self, Self::Explicit)
227+
}
228+
}
229+
216230
/// Resolve visibility of all specific fields of a struct or union variant.
217231
pub(crate) fn field_visibilities_query(
218232
db: &dyn DefDatabase,

crates/hir-expand/src/mod_path.rs

+4
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ impl ModPath {
9696
self.kind == PathKind::Super(0) && self.segments.is_empty()
9797
}
9898

99+
pub fn is_pub_crate(&self) -> bool {
100+
self.kind == PathKind::Crate && self.segments.is_empty()
101+
}
102+
99103
#[allow(non_snake_case)]
100104
pub fn is_Self(&self) -> bool {
101105
self.kind == PathKind::Plain

crates/hir-ty/src/display.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1629,7 +1629,7 @@ pub fn write_visibility(
16291629
) -> Result<(), HirDisplayError> {
16301630
match vis {
16311631
Visibility::Public => write!(f, "pub "),
1632-
Visibility::Module(vis_id) => {
1632+
Visibility::Module(vis_id, _) => {
16331633
let def_map = module_id.def_map(f.db.upcast());
16341634
let root_module_id = def_map.module_id(DefMap::ROOT);
16351635
if vis_id == module_id {

crates/ide-assists/src/handlers/auto_import.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1551,4 +1551,39 @@ use foo::Foo$0;
15511551
",
15521552
);
15531553
}
1554+
1555+
#[test]
1556+
fn considers_pub_crate() {
1557+
check_assist(
1558+
auto_import,
1559+
r#"
1560+
mod foo {
1561+
pub struct Foo;
1562+
}
1563+
1564+
pub(crate) use self::foo::*;
1565+
1566+
mod bar {
1567+
fn main() {
1568+
Foo$0;
1569+
}
1570+
}
1571+
"#,
1572+
r#"
1573+
mod foo {
1574+
pub struct Foo;
1575+
}
1576+
1577+
pub(crate) use self::foo::*;
1578+
1579+
mod bar {
1580+
use crate::Foo;
1581+
1582+
fn main() {
1583+
Foo;
1584+
}
1585+
}
1586+
"#,
1587+
);
1588+
}
15541589
}

crates/ide-db/src/search.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ impl Definition {
356356
if let Some(Visibility::Public) = vis {
357357
return SearchScope::reverse_dependencies(db, module.krate());
358358
}
359-
if let Some(Visibility::Module(module)) = vis {
359+
if let Some(Visibility::Module(module, _)) = vis {
360360
return SearchScope::module_and_children(db, module.into());
361361
}
362362

0 commit comments

Comments
 (0)