Skip to content

Commit a501bbe

Browse files
committed
rustdoc: correctly clean cross-crate opaque types
1 parent ffb7ed9 commit a501bbe

File tree

4 files changed

+102
-40
lines changed

4 files changed

+102
-40
lines changed

src/librustdoc/clean/mod.rs

+19-16
Original file line numberDiff line numberDiff line change
@@ -2316,23 +2316,26 @@ fn clean_middle_opaque_bounds<'tcx>(
23162316
let bindings: ThinVec<_> = bounds
23172317
.iter()
23182318
.filter_map(|bound| {
2319-
if let ty::ClauseKind::Projection(proj) = bound.kind().skip_binder() {
2320-
if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() {
2321-
Some(TypeBinding {
2322-
assoc: projection_to_path_segment(
2323-
bound.kind().rebind(proj.projection_ty),
2324-
cx,
2325-
),
2326-
kind: TypeBindingKind::Equality {
2327-
term: clean_middle_term(bound.kind().rebind(proj.term), cx),
2328-
},
2329-
})
2330-
} else {
2331-
None
2332-
}
2333-
} else {
2334-
None
2319+
let ty::ClauseKind::Projection(proj) = bound.kind().skip_binder() else {
2320+
return None;
2321+
};
2322+
if !simplify::trait_is_same_or_supertrait2(
2323+
cx.tcx,
2324+
trait_ref.skip_binder(),
2325+
proj.projection_ty.trait_ref(cx.tcx),
2326+
) {
2327+
return None;
23352328
}
2329+
2330+
Some(TypeBinding {
2331+
assoc: projection_to_path_segment(
2332+
bound.kind().rebind(proj.projection_ty),
2333+
cx,
2334+
),
2335+
kind: TypeBindingKind::Equality {
2336+
term: clean_middle_term(bound.kind().rebind(proj.term), cx),
2337+
},
2338+
})
23362339
})
23372340
.collect();
23382341

src/librustdoc/clean/simplify.rs

+47-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
1414
use rustc_data_structures::fx::FxIndexMap;
1515
use rustc_hir::def_id::DefId;
16-
use rustc_middle::ty;
16+
use rustc_middle::ty::{self, TyCtxt};
1717
use thin_vec::ThinVec;
1818

1919
use crate::clean;
@@ -68,6 +68,7 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> ThinVec<WP
6868
clauses
6969
}
7070

71+
// NOTE(fmease): This basically does the same bound cleaning as `clean_middle_opaque_bounds`.
7172
pub(crate) fn merge_bounds(
7273
cx: &clean::DocContext<'_>,
7374
bounds: &mut Vec<clean::GenericBound>,
@@ -76,9 +77,8 @@ pub(crate) fn merge_bounds(
7677
rhs: &clean::Term,
7778
) -> bool {
7879
!bounds.iter_mut().any(|b| {
79-
let trait_ref = match *b {
80-
clean::GenericBound::TraitBound(ref mut tr, _) => tr,
81-
clean::GenericBound::Outlives(..) => return false,
80+
let clean::GenericBound::TraitBound(trait_ref, _) = b else {
81+
return false;
8282
};
8383
// If this QPath's trait `trait_did` is the same as, or a supertrait
8484
// of, the bound's trait `did` then we can keep going, otherwise
@@ -108,7 +108,21 @@ pub(crate) fn merge_bounds(
108108
})
109109
}
110110

111-
fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) -> bool {
111+
// FIXME(fmease): This doesn't take into account the generic args of traits.
112+
//
113+
// Get rid of this in favor of `trait_is_same_or_supertrait2`.
114+
//
115+
// This is soft-blocked on `merge_bounds` and `where_clauses`
116+
// operating on `rustc_middle` types directly instead of cleaned
117+
// types (part of refactoring #115379).
118+
// I suppose it's possible to not do that and to instead clean
119+
// `ty::TraitRef`s in `trait_is_same_or_supertrait` as we go
120+
// but that seems fragile and bad for perf.
121+
pub(crate) fn trait_is_same_or_supertrait(
122+
cx: &DocContext<'_>,
123+
child: DefId,
124+
trait_: DefId,
125+
) -> bool {
112126
if child == trait_ {
113127
return true;
114128
}
@@ -128,6 +142,34 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId)
128142
.any(|did| trait_is_same_or_supertrait(cx, did, trait_))
129143
}
130144

145+
// FIXME(fmease): Add docs clarifying why we need to check the generics args for equality.
146+
// FIXME(fmease): Drop the `2` from the name.
147+
pub(crate) fn trait_is_same_or_supertrait2<'tcx>(
148+
tcx: TyCtxt<'tcx>,
149+
child: ty::TraitRef<'tcx>,
150+
trait_: ty::TraitRef<'tcx>,
151+
) -> bool {
152+
if child == trait_ {
153+
return true;
154+
}
155+
let predicates = tcx.super_predicates_of(child.def_id);
156+
debug_assert!(tcx.generics_of(child.def_id).has_self);
157+
predicates
158+
.predicates
159+
.iter()
160+
.filter_map(|(pred, _)| {
161+
let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() else {
162+
return None;
163+
};
164+
if pred.trait_ref.self_ty() != tcx.types.self_param {
165+
return None;
166+
}
167+
168+
Some(ty::EarlyBinder::bind(pred.trait_ref).instantiate(tcx, child.args))
169+
})
170+
.any(|child| trait_is_same_or_supertrait2(tcx, child, trait_))
171+
}
172+
131173
/// Move bounds that are (likely) directly attached to generic parameters from the where-clause to
132174
/// the respective parameter.
133175
///

tests/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs renamed to tests/rustdoc/inline_cross/auxiliary/impl_trait.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// edition:2018
2-
31
use std::ops::Deref;
42

53
pub fn func<'a>(_x: impl Clone + Into<Vec<u8>> + 'a) {}
@@ -26,10 +24,22 @@ pub trait Auxiliary<'arena> {
2624
type Item<'input>;
2725
}
2826

29-
pub async fn async_fn() {}
30-
3127
pub struct Foo;
3228

3329
impl Foo {
3430
pub fn method<'a>(_x: impl Clone + Into<Vec<u8>> + 'a) {}
3531
}
32+
33+
// Regression test for issue #113015, subitem (1) / bevyengine/bevy#8898.
34+
// Check that we pick up on the return type of cross-crate opaque `Fn`s and
35+
// `FnMut`s (`FnOnce` already used to work fine).
36+
pub fn rpit_fn() -> impl Fn() -> bool {
37+
|| false
38+
}
39+
40+
pub fn rpit_fn_mut() -> impl for<'a> FnMut(&'a str) -> &'a str {
41+
|source| source
42+
}
43+
44+
// FIXME(fmease): Add more tests that demonstrate the importance of checking the
45+
// generic args of supertrait bounds.
+22-15
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,46 @@
1-
// aux-build:impl_trait_aux.rs
1+
// aux-crate:impl_trait=impl_trait.rs
22
// edition:2018
3+
#![crate_name = "user"]
34

4-
extern crate impl_trait_aux;
5-
6-
// @has impl_trait/fn.func.html
5+
// @has user/fn.func.html
76
// @has - '//pre[@class="rust item-decl"]' "pub fn func<'a>(_x: impl Clone + Into<Vec<u8>> + 'a)"
87
// @!has - '//pre[@class="rust item-decl"]' 'where'
9-
pub use impl_trait_aux::func;
8+
pub use impl_trait::func;
109

11-
// @has impl_trait/fn.func2.html
10+
// @has user/fn.func2.html
1211
// @has - '//pre[@class="rust item-decl"]' "func2<T>("
1312
// @has - '//pre[@class="rust item-decl"]' "_x: impl Deref<Target = Option<T>> + Iterator<Item = T>,"
1413
// @has - '//pre[@class="rust item-decl"]' "_y: impl Iterator<Item = u8> )"
1514
// @!has - '//pre[@class="rust item-decl"]' 'where'
16-
pub use impl_trait_aux::func2;
15+
pub use impl_trait::func2;
1716

18-
// @has impl_trait/fn.func3.html
17+
// @has user/fn.func3.html
1918
// @has - '//pre[@class="rust item-decl"]' "func3("
2019
// @has - '//pre[@class="rust item-decl"]' "_x: impl Iterator<Item = impl Iterator<Item = u8>> + Clone)"
2120
// @!has - '//pre[@class="rust item-decl"]' 'where'
22-
pub use impl_trait_aux::func3;
21+
pub use impl_trait::func3;
2322

24-
// @has impl_trait/fn.func4.html
23+
// @has user/fn.func4.html
2524
// @has - '//pre[@class="rust item-decl"]' "func4<T>("
2625
// @has - '//pre[@class="rust item-decl"]' "T: Iterator<Item = impl Clone>,"
27-
pub use impl_trait_aux::func4;
26+
pub use impl_trait::func4;
2827

29-
// @has impl_trait/fn.func5.html
28+
// @has user/fn.func5.html
3029
// @has - '//pre[@class="rust item-decl"]' "func5("
3130
// @has - '//pre[@class="rust item-decl"]' "_f: impl for<'any> Fn(&'any str, &'any str) -> bool + for<'r> Other<T<'r> = ()>,"
3231
// @has - '//pre[@class="rust item-decl"]' "_a: impl for<'beta, 'alpha, '_gamma> Auxiliary<'alpha, Item<'beta> = fn(_: &'beta ())>"
3332
// @!has - '//pre[@class="rust item-decl"]' 'where'
34-
pub use impl_trait_aux::func5;
33+
pub use impl_trait::func5;
3534

36-
// @has impl_trait/struct.Foo.html
35+
// @has user/struct.Foo.html
3736
// @has - '//*[@id="method.method"]//h4[@class="code-header"]' "pub fn method<'a>(_x: impl Clone + Into<Vec<u8>> + 'a)"
3837
// @!has - '//*[@id="method.method"]//h4[@class="code-header"]' 'where'
39-
pub use impl_trait_aux::Foo;
38+
pub use impl_trait::Foo;
39+
40+
// @has user/fn.rpit_fn.html
41+
// @has - '//pre[@class="rust item-decl"]' "rpit_fn() -> impl Fn() -> bool"
42+
pub use impl_trait::rpit_fn;
43+
44+
// @has user/fn.rpit_fn_mut.html
45+
// @has - '//pre[@class="rust item-decl"]' "rpit_fn_mut() -> impl for<'a> FnMut(&'a str) -> &'a str"
46+
pub use impl_trait::rpit_fn_mut;

0 commit comments

Comments
 (0)