Skip to content

Commit 06b0273

Browse files
committed
Handle two different glob re-exports
Signed-off-by: longfangsong <[email protected]>
1 parent f9df01a commit 06b0273

File tree

2 files changed

+66
-11
lines changed

2 files changed

+66
-11
lines changed

src/librustdoc/doctree.rs

+45-9
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,18 @@ impl Module<'hir> {
6565

6666
pub(crate) fn push_item(&mut self, ctx: &DocContext<'_>, new_item: Item<'hir>) {
6767
let new_item_ns = ctx.tcx.def_kind(new_item.hir_item.def_id).ns();
68+
let mut to_remove = None;
6869
if !new_item.name().is_empty() && new_item_ns.is_some() {
69-
if let Some(existing_item) = self.items.iter_mut().find(|item| {
70-
item.name() == new_item.name()
71-
&& ctx.tcx.def_kind(item.hir_item.def_id).ns() == new_item_ns
72-
}) {
70+
if let Some((index, existing_item)) =
71+
self.items.iter_mut().enumerate().find(|(_, item)| {
72+
item.name() == new_item.name()
73+
&& ctx.tcx.def_kind(item.hir_item.def_id).ns() == new_item_ns
74+
})
75+
{
7376
match (existing_item.from_glob, new_item.from_glob) {
74-
(true, _) => {
75-
// `existing_item` is from glob, no matter whether `new_item` is from glob,
76-
// `new_item` should always shadow `existing_item`
77+
(true, false) => {
78+
// `existing_item` is from glob, `new_item` is not,
79+
// `new_item` should shadow `existing_item`
7780
debug!("push_item: {:?} shadowed by {:?}", existing_item, new_item);
7881
*existing_item = new_item;
7982
return;
@@ -83,27 +86,56 @@ impl Module<'hir> {
8386
// just keep `existing_item` and return at once
8487
return;
8588
}
89+
(true, true) => {
90+
// both `existing_item` and `new_item` are from glob
91+
if existing_item.hir_item.def_id == new_item.hir_item.def_id {
92+
// they actually point to same object, it is ok to just keep one of them
93+
return;
94+
} else {
95+
// a "glob import vs glob import in the same module" will raise when actually use these item
96+
// we should accept neither of these two items
97+
to_remove = Some(index);
98+
}
99+
}
86100
(false, false) => {
87101
// should report "defined multiple time" error before reach this
88102
unreachable!()
89103
}
90104
}
91105
}
92106
}
107+
if let Some(index) = to_remove {
108+
self.items.swap_remove(index);
109+
return;
110+
}
93111
// no item with same name and namespace exists, just collect `new_item`
94112
self.items.push(new_item);
95113
}
96114

97115
pub(crate) fn push_mod(&mut self, new_mod: Module<'hir>) {
98-
if let Some(existing_mod) = self.mods.iter_mut().find(|mod_| mod_.name == new_mod.name) {
116+
let mut to_remove = None;
117+
if let Some((index, existing_mod)) =
118+
self.mods.iter_mut().enumerate().find(|(_, mod_)| mod_.name == new_mod.name)
119+
{
99120
match (existing_mod.from_glob, new_mod.from_glob) {
100-
(true, _) => {
121+
(true, false) => {
101122
// `existing_mod` is from glob, no matter whether `new_mod` is from glob,
102123
// `new_mod` should always shadow `existing_mod`
103124
debug!("push_mod: {:?} shadowed by {:?}", existing_mod.name, new_mod.name);
104125
*existing_mod = new_mod;
105126
return;
106127
}
128+
(true, true) => {
129+
// both `existing_item` and `new_item` are from glob
130+
if existing_mod.id == new_mod.id {
131+
// they actually point to same object, it is ok to just keep one of them
132+
return;
133+
} else {
134+
// a "glob import vs glob import in the same module" will raise when actually use these item
135+
// we should accept neither of these two items
136+
to_remove = Some(index);
137+
}
138+
}
107139
(false, true) => {
108140
// `existing_mod` is not from glob but `new_mod` is,
109141
// just keep `existing_mod` and return at once
@@ -115,6 +147,10 @@ impl Module<'hir> {
115147
}
116148
}
117149
}
150+
if let Some(index) = to_remove {
151+
self.mods.swap_remove(index);
152+
return;
153+
}
118154
// no mod with same name exists, just collect `new_mod`
119155
self.mods.push(new_mod);
120156
}

src/test/rustdoc/glob-shadowing.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// @has 'glob_shadowing/index.html'
22
// @count - '//tr[@class="module-item"]' 4
33
// @!has - '//tr[@class="module-item"]' 'sub1::describe'
4+
// @!has - '//tr[@class="module-item"]' 'sub1::describe2'
45
// @has - '//tr[@class="module-item"]' 'mod::prelude'
56
// @has - '//tr[@class="module-item"]' 'sub2::describe'
67
// @has - '//tr[@class="module-item"]' 'sub1::Foo (struct)'
78
// @has - '//tr[@class="module-item"]' 'mod::Foo (function)'
8-
99
// @has 'glob_shadowing/fn.describe.html'
1010
// @has - '//div[@class="docblock"]' 'sub2::describe'
1111

@@ -22,9 +22,16 @@ mod sub1 {
2222
pub use super::describe;
2323
}
2424

25-
// this should not be shadowed, because sub1::Foo and mod::Foo are in different namespace
25+
// this should *not* be shadowed, because sub1::Foo and mod::Foo are in different namespace
2626
/// sub1::Foo (struct)
2727
pub struct Foo;
28+
29+
// this should be shadowed,
30+
// because both sub1::describe2 and sub3::describe2 are from glob reexport
31+
/// sub1::describe2
32+
pub fn describe2() -> &'static str {
33+
"sub1::describe2"
34+
}
2835
}
2936

3037
mod sub2 {
@@ -34,6 +41,15 @@ mod sub2 {
3441
}
3542
}
3643

44+
mod sub3 {
45+
// this should be shadowed
46+
// because both sub1::describe2 and sub3::describe2 are from glob reexport
47+
/// sub3::describe2
48+
pub fn describe2() -> &'static str {
49+
"sub3::describe2"
50+
}
51+
}
52+
3753
/// mod::prelude
3854
pub mod prelude {}
3955

@@ -45,3 +61,6 @@ pub use sub2::describe;
4561

4662
#[doc(inline)]
4763
pub use sub1::*;
64+
65+
#[doc(inline)]
66+
pub use sub3::*;

0 commit comments

Comments
 (0)