Skip to content

Commit c07eaf8

Browse files
author
Jonas Schievink
committed
Support Trait as _ imports
1 parent 65b89b5 commit c07eaf8

File tree

6 files changed

+266
-26
lines changed

6 files changed

+266
-26
lines changed

crates/ra_hir_def/src/item_scope.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ pub struct ItemScope {
3636

3737
defs: Vec<ModuleDefId>,
3838
impls: Vec<ImplId>,
39+
/// Traits imported via `use Trait as _;`.
40+
unnamed_trait_imports: FxHashMap<TraitId, Visibility>,
3941
/// Macros visible in current module in legacy textual scope
4042
///
4143
/// For macros invoked by an unqualified identifier like `bar!()`, `legacy_macros` will be searched in first.
@@ -126,10 +128,13 @@ impl ItemScope {
126128
}
127129

128130
pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
129-
self.types.values().filter_map(|(def, _)| match def {
130-
ModuleDefId::TraitId(t) => Some(*t),
131-
_ => None,
132-
})
131+
self.types
132+
.values()
133+
.filter_map(|(def, _)| match def {
134+
ModuleDefId::TraitId(t) => Some(*t),
135+
_ => None,
136+
})
137+
.chain(self.unnamed_trait_imports.keys().copied())
133138
}
134139

135140
pub(crate) fn define_def(&mut self, def: ModuleDefId) {
@@ -148,6 +153,14 @@ impl ItemScope {
148153
self.legacy_macros.insert(name, mac);
149154
}
150155

156+
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
157+
self.unnamed_trait_imports.get(&tr).copied()
158+
}
159+
160+
pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) {
161+
self.unnamed_trait_imports.insert(tr, vis);
162+
}
163+
151164
pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool {
152165
let mut changed = false;
153166

@@ -241,8 +254,12 @@ impl ItemScope {
241254
changed
242255
}
243256

244-
pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a {
245-
self.entries().map(|(name, res)| (name.clone(), res))
257+
pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Option<Name>, PerNs)> + 'a {
258+
self.entries().map(|(name, res)| (Some(name.clone()), res)).chain(
259+
self.unnamed_trait_imports
260+
.iter()
261+
.map(|(tr, vis)| (None, PerNs::types(ModuleDefId::TraitId(*tr), *vis))),
262+
)
246263
}
247264

248265
pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {

crates/ra_hir_def/src/nameres.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ impl CrateDefMap {
239239
entries.sort_by_key(|(name, _)| name.clone());
240240

241241
for (name, def) in entries {
242-
format_to!(buf, "{}:", name);
242+
format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string()));
243243

244244
if def.types.is_some() {
245245
buf.push_str(" t");

crates/ra_hir_def/src/nameres/collector.rs

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ impl DefCollector<'_> {
310310
if export {
311311
self.update(
312312
self.def_map.root,
313-
&[(name, PerNs::macros(macro_, Visibility::Public))],
313+
&[(Some(name), PerNs::macros(macro_, Visibility::Public))],
314314
Visibility::Public,
315315
ImportType::Named,
316316
);
@@ -336,7 +336,7 @@ impl DefCollector<'_> {
336336
fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) {
337337
self.update(
338338
self.def_map.root,
339-
&[(name, PerNs::macros(macro_, Visibility::Public))],
339+
&[(Some(name), PerNs::macros(macro_, Visibility::Public))],
340340
Visibility::Public,
341341
ImportType::Named,
342342
);
@@ -534,7 +534,7 @@ impl DefCollector<'_> {
534534
let name = variant_data.name.clone();
535535
let variant = EnumVariantId { parent: e, local_id };
536536
let res = PerNs::both(variant.into(), variant.into(), vis);
537-
(name, res)
537+
(Some(name), res)
538538
})
539539
.collect::<Vec<_>>();
540540
self.update(module_id, &resolutions, vis, ImportType::Glob);
@@ -550,15 +550,15 @@ impl DefCollector<'_> {
550550
match import.path.segments.last() {
551551
Some(last_segment) => {
552552
let name = match &import.alias {
553-
Some(ImportAlias::Alias(name)) => name.clone(),
554-
Some(ImportAlias::Underscore) => last_segment.clone(), // FIXME rust-analyzer#2736
555-
None => last_segment.clone(),
553+
Some(ImportAlias::Alias(name)) => Some(name.clone()),
554+
Some(ImportAlias::Underscore) => None,
555+
None => Some(last_segment.clone()),
556556
};
557557
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
558558

559559
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
560560
if import.is_extern_crate && module_id == self.def_map.root {
561-
if let Some(def) = def.take_types() {
561+
if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) {
562562
self.def_map.extern_prelude.insert(name.clone(), def);
563563
}
564564
}
@@ -573,7 +573,7 @@ impl DefCollector<'_> {
573573
fn update(
574574
&mut self,
575575
module_id: LocalModuleId,
576-
resolutions: &[(Name, PerNs)],
576+
resolutions: &[(Option<Name>, PerNs)],
577577
vis: Visibility,
578578
import_type: ImportType,
579579
) {
@@ -584,7 +584,7 @@ impl DefCollector<'_> {
584584
fn update_recursive(
585585
&mut self,
586586
module_id: LocalModuleId,
587-
resolutions: &[(Name, PerNs)],
587+
resolutions: &[(Option<Name>, PerNs)],
588588
// All resolutions are imported with this visibility; the visibilies in
589589
// the `PerNs` values are ignored and overwritten
590590
vis: Visibility,
@@ -595,15 +595,46 @@ impl DefCollector<'_> {
595595
// prevent stack overflows (but this shouldn't be possible)
596596
panic!("infinite recursion in glob imports!");
597597
}
598-
let scope = &mut self.def_map.modules[module_id].scope;
599598
let mut changed = false;
599+
600600
for (name, res) in resolutions {
601-
changed |= scope.push_res_with_import(
602-
&mut self.from_glob_import,
603-
(module_id, name.clone()),
604-
res.with_visibility(vis),
605-
import_type,
606-
);
601+
match name {
602+
Some(name) => {
603+
let scope = &mut self.def_map.modules[module_id].scope;
604+
changed |= scope.push_res_with_import(
605+
&mut self.from_glob_import,
606+
(module_id, name.clone()),
607+
res.with_visibility(vis),
608+
import_type,
609+
);
610+
}
611+
None => {
612+
let tr = match res.take_types() {
613+
Some(ModuleDefId::TraitId(tr)) => tr,
614+
Some(other) => {
615+
log::debug!("non-trait `_` import of {:?}", other);
616+
continue;
617+
}
618+
None => continue,
619+
};
620+
let old_vis = self.def_map.modules[module_id].scope.unnamed_trait_vis(tr);
621+
let should_update = match old_vis {
622+
None => true,
623+
Some(old_vis) => {
624+
let max_vis = old_vis.max(vis, &self.def_map).unwrap_or_else(|| {
625+
panic!("`Tr as _` imports with unrelated visibilities {:?} and {:?} (trait {:?})", old_vis, vis, tr);
626+
});
627+
628+
max_vis != old_vis
629+
}
630+
};
631+
632+
if should_update {
633+
changed = true;
634+
self.def_map.modules[module_id].scope.push_unnamed_trait(tr, vis);
635+
}
636+
}
637+
}
607638
}
608639

609640
if !changed {
@@ -950,7 +981,7 @@ impl ModCollector<'_, '_> {
950981
.unwrap_or(Visibility::Public);
951982
self.def_collector.update(
952983
self.module_id,
953-
&[(name.clone(), PerNs::from_def(id, vis, has_constructor))],
984+
&[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
954985
vis,
955986
ImportType::Named,
956987
)
@@ -1057,7 +1088,7 @@ impl ModCollector<'_, '_> {
10571088
self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
10581089
self.def_collector.update(
10591090
self.module_id,
1060-
&[(name, PerNs::from_def(def, vis, false))],
1091+
&[(Some(name), PerNs::from_def(def, vis, false))],
10611092
vis,
10621093
ImportType::Named,
10631094
);

crates/ra_hir_def/src/nameres/tests.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,3 +558,134 @@ mod b {
558558
"#]],
559559
);
560560
}
561+
562+
#[test]
563+
fn underscore_import() {
564+
check(
565+
r#"
566+
//- /main.rs
567+
use tr::Tr as _;
568+
use tr::Tr2 as _;
569+
570+
mod tr {
571+
pub trait Tr {}
572+
pub trait Tr2 {}
573+
}
574+
"#,
575+
expect![[r#"
576+
crate
577+
_: t
578+
_: t
579+
tr: t
580+
581+
crate::tr
582+
Tr: t
583+
Tr2: t
584+
"#]],
585+
);
586+
}
587+
588+
#[test]
589+
fn underscore_reexport() {
590+
check(
591+
r#"
592+
//- /main.rs
593+
mod tr {
594+
pub trait PubTr {}
595+
pub trait PrivTr {}
596+
}
597+
mod reex {
598+
use crate::tr::PrivTr as _;
599+
pub use crate::tr::PubTr as _;
600+
}
601+
use crate::reex::*;
602+
"#,
603+
expect![[r#"
604+
crate
605+
_: t
606+
reex: t
607+
tr: t
608+
609+
crate::tr
610+
PrivTr: t
611+
PubTr: t
612+
613+
crate::reex
614+
_: t
615+
_: t
616+
"#]],
617+
);
618+
}
619+
620+
#[test]
621+
fn underscore_pub_crate_reexport() {
622+
check(
623+
r#"
624+
//- /main.rs crate:main deps:lib
625+
use lib::*;
626+
627+
//- /lib.rs crate:lib
628+
use tr::Tr as _;
629+
pub use tr::Tr as _;
630+
631+
mod tr {
632+
pub trait Tr {
633+
fn method(&self) {}
634+
}
635+
}
636+
"#,
637+
expect![[r#"
638+
crate
639+
_: t
640+
"#]],
641+
);
642+
}
643+
644+
#[test]
645+
fn underscore_nontrait() {
646+
check(
647+
r#"
648+
//- /main.rs
649+
mod m {
650+
pub struct Struct;
651+
pub enum Enum {}
652+
pub const CONST: () = ();
653+
}
654+
use crate::m::{Struct as _, Enum as _, CONST as _};
655+
"#,
656+
expect![[r#"
657+
crate
658+
m: t
659+
660+
crate::m
661+
CONST: v
662+
Enum: t
663+
Struct: t v
664+
"#]],
665+
);
666+
}
667+
668+
#[test]
669+
fn underscore_name_conflict() {
670+
check(
671+
r#"
672+
//- /main.rs
673+
struct Tr;
674+
675+
use tr::Tr as _;
676+
677+
mod tr {
678+
pub trait Tr {}
679+
}
680+
"#,
681+
expect![[r#"
682+
crate
683+
_: t
684+
Tr: t v
685+
tr: t
686+
687+
crate::tr
688+
Tr: t
689+
"#]],
690+
);
691+
}

crates/ra_hir_def/src/visibility.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use ra_syntax::ast;
55

66
use crate::{
77
db::DefDatabase,
8+
nameres::CrateDefMap,
89
path::{ModPath, PathKind},
910
ModuleId,
1011
};
@@ -115,7 +116,7 @@ impl Visibility {
115116

116117
pub(crate) fn is_visible_from_def_map(
117118
self,
118-
def_map: &crate::nameres::CrateDefMap,
119+
def_map: &CrateDefMap,
119120
from_module: crate::LocalModuleId,
120121
) -> bool {
121122
let to_module = match self {
@@ -129,4 +130,42 @@ impl Visibility {
129130
});
130131
ancestors.any(|m| m == to_module.local_id)
131132
}
133+
134+
/// Returns the most permissive visibility of `self` and `other`.
135+
///
136+
/// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only
137+
/// visible in unrelated modules).
138+
pub(crate) fn max(self, other: Visibility, def_map: &CrateDefMap) -> Option<Visibility> {
139+
match (self, other) {
140+
(Visibility::Module(_), Visibility::Public)
141+
| (Visibility::Public, Visibility::Module(_))
142+
| (Visibility::Public, Visibility::Public) => Some(Visibility::Public),
143+
(Visibility::Module(mod_a), Visibility::Module(mod_b)) => {
144+
if mod_a.krate != mod_b.krate {
145+
return None;
146+
}
147+
148+
let mut a_ancestors = std::iter::successors(Some(mod_a.local_id), |m| {
149+
let parent_id = def_map[*m].parent?;
150+
Some(parent_id)
151+
});
152+
let mut b_ancestors = std::iter::successors(Some(mod_b.local_id), |m| {
153+
let parent_id = def_map[*m].parent?;
154+
Some(parent_id)
155+
});
156+
157+
if a_ancestors.any(|m| m == mod_b.local_id) {
158+
// B is above A
159+
return Some(Visibility::Module(mod_b));
160+
}
161+
162+
if b_ancestors.any(|m| m == mod_a.local_id) {
163+
// A is above B
164+
return Some(Visibility::Module(mod_a));
165+
}
166+
167+
None
168+
}
169+
}
170+
}
132171
}

0 commit comments

Comments
 (0)