Skip to content

Rustdoc does not show bounds on associated items in the declaration of re-exported traits #84579

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
Nemo157 opened this issue Apr 26, 2021 · 4 comments · Fixed by #102439
Closed
Assignees
Labels
A-associated-items Area: Associated items (types, constants & functions) A-cross-crate-reexports Area: Documentation that has been re-exported from a different crate A-rustdoc-ui Area: Rustdoc UI (generated HTML) C-bug Category: This is a bug. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.

Comments

@Nemo157
Copy link
Member

Nemo157 commented Apr 26, 2021

Currently on nightly the declaration for IntoIterator looks like:

image

While the actual declaration with attributes stripped is:

pub trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;
    fn into_iter(self) -> Self::IntoIter;
}

Previously up till 1.48.0 this rendered as:

image

Which is not great, but at least it has the information there. I tried to identify the PR which changed this in 1.49.0, but skimming the list from a few search queries none of them stood out to me.

@Nemo157 Nemo157 added T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. C-bug Category: This is a bug. A-rustdoc-ui Area: Rustdoc UI (generated HTML) labels Apr 26, 2021
@cynecx
Copy link
Contributor

cynecx commented May 2, 2021

Rendering the IntoIterator declaration without the attributes and outside core/std works just fine:

image

@Nemo157
Copy link
Member Author

Nemo157 commented May 2, 2021

Ooooh, it works on core::iter::IntoIterator, it's only on the re-exported std::iter::IntoIterator that it has this issue. Similarly on 1.48.0 core showed the associated type bound nicely, only std showed it as a where clause.

@Nemo157 Nemo157 changed the title Rustdoc does not show bounds on associated items in the declaration Rustdoc does not show bounds on associated items in the declaration of re-exported traits May 2, 2021
@cynecx
Copy link
Contributor

cynecx commented May 25, 2021

I may have found the reason why we are skipping the associated type binding.

So when an external item (in our case an associated type) gets documented it goes through clean::inline::build_external_trait which then goes through this clean visitor:

impl Clean<Item> for ty::AssocItem {
fn clean(&self, cx: &mut DocContext<'_>) -> Item {

Further down we get to:

let generics = (tcx.generics_of(self.def_id), predicates).clean(cx);

which produces (for the IntoIter associated item):

[src/librustdoc/clean/mod.rs:1101] tcx.explicit_item_bounds(self.def_id) = [
    (
        Binder(TraitPredicate(<<Self as b::IntoIterator>::IntoIter as std::marker::Sized>), []),
        /home/cynecx/dev/test-rustdoc/b/src/lib.rs:3:5: 3:48 (#0),
    ),
    (
        Binder(TraitPredicate(<<Self as b::IntoIterator>::IntoIter as std::iter::Iterator>), []),
        /home/cynecx/dev/test-rustdoc/b/src/lib.rs:3:20: 3:47 (#0),
    ),
    (
        Binder(ProjectionPredicate(ProjectionTy { substs: [<Self as b::IntoIterator>::IntoIter], item_def_id: DefId(2:7247 ~ core[51e2]::iter::traits::iterator::Iterator::Item) }, <Self as b::IntoIterator>::Item), []),
        /home/cynecx/dev/test-rustdoc/b/src/lib.rs:3:29: 3:46 (#0),
    ),
]

Then

let generics = (tcx.generics_of(self.def_id), predicates).clean(cx);
produces:

Output
[src/librustdoc/clean/mod.rs:1105] &generics = Generics {
    params: [],
    where_predicates: [
        BoundPredicate {
            ty: QPath {
                name: "IntoIter",
                self_type: Generic(
                    "Self",
                ),
                self_def_id: None,
                trait_: ResolvedPath {
                    path: Path {
                        global: false,
                        res: Err,
                        segments: [
                            PathSegment {
                                name: "IntoIterator",
                                args: AngleBracketed {
                                    args: [],
                                    bindings: [],
                                },
                            },
                        ],
                    },
                    param_names: None,
                    did: DefId(19:3 ~ b[78df]::IntoIterator),
                    is_generic: false,
                },
            },
            bounds: [
                TraitBound(
                    PolyTrait {
                        trait_: ResolvedPath {
                            path: Path {
                                global: false,
                                res: Err,
                                segments: [
                                    PathSegment {
                                        name: "Sized",
                                        args: AngleBracketed {
                                            args: [],
                                            bindings: [],
                                        },
                                    },
                                ],
                            },
                            param_names: None,
                            did: DefId(2:2839 ~ core[51e2]::marker::Sized),
                            is_generic: false,
                        },
                        generic_params: [],
                    },
                    None,
                ),
            ],
        },
        BoundPredicate {
            ty: QPath {
                name: "IntoIter",
                self_type: Generic(
                    "Self",
                ),
                self_def_id: None,
                trait_: ResolvedPath {
                    path: Path {
                        global: false,
                        res: Err,
                        segments: [
                            PathSegment {
                                name: "IntoIterator",
                                args: AngleBracketed {
                                    args: [],
                                    bindings: [],
                                },
                            },
                        ],
                    },
                    param_names: None,
                    did: DefId(19:3 ~ b[78df]::IntoIterator),
                    is_generic: false,
                },
            },
            bounds: [
                TraitBound(
                    PolyTrait {
                        trait_: ResolvedPath {
                            path: Path {
                                global: false,
                                res: Err,
                                segments: [
                                    PathSegment {
                                        name: "Iterator",
                                        args: AngleBracketed {
                                            args: [],
                                            bindings: [],
                                        },
                                    },
                                ],
                            },
                            param_names: None,
                            did: DefId(2:7246 ~ core[51e2]::iter::traits::iterator::Iterator),
                            is_generic: false,
                        },
                        generic_params: [],
                    },
                    None,
                ),
            ],
        },
        EqPredicate {
            lhs: QPath {
                name: "Item",
                self_type: QPath {
                    name: "IntoIter",
                    self_type: Generic(
                        "Self",
                    ),
                    self_def_id: None,
                    trait_: ResolvedPath {
                        path: Path {
                            global: false,
                            res: Err,
                            segments: [
                                PathSegment {
                                    name: "IntoIterator",
                                    args: AngleBracketed {
                                        args: [],
                                        bindings: [],
                                    },
                                },
                            ],
                        },
                        param_names: None,
                        did: DefId(19:3 ~ b[78df]::IntoIterator),
                        is_generic: false,
                    },
                },
                self_def_id: None,
                trait_: ResolvedPath {
                    path: Path {
                        global: false,
                        res: Err,
                        segments: [
                            PathSegment {
                                name: "Iterator",
                                args: AngleBracketed {
                                    args: [],
                                    bindings: [],
                                },
                            },
                        ],
                    },
                    param_names: None,
                    did: DefId(2:7246 ~ core[51e2]::iter::traits::iterator::Iterator),
                    is_generic: false,
                },
            },
            rhs: QPath {
                name: "Item",
                self_type: Generic(
                    "Self",
                ),
                self_def_id: None,
                trait_: ResolvedPath {
                    path: Path {
                        global: false,
                        res: Err,
                        segments: [
                            PathSegment {
                                name: "IntoIterator",
                                args: AngleBracketed {
                                    args: [],
                                    bindings: [],
                                },
                            },
                        ],
                    },
                    param_names: None,
                    did: DefId(19:3 ~ b[78df]::IntoIterator),
                    is_generic: false,
                },
            },
        },
    ],
}

The last WherePredicate::EqPredicate boils down to <Self::IntoIter as Iterator>::Item == Self::Item which gets discarded further down here (in a filter_map operation):

.filter_map(|pred| {
let (name, self_type, trait_, bounds) = match *pred {
WherePredicate::BoundPredicate {
ty: QPath { ref name, ref self_type, ref trait_, .. },
ref bounds,
} => (name, self_type, trait_, bounds),
_ => return None,

Afaics, the reason the unexported trait gets properly "documented" is because a different code path is run which doesn't involve clean on a ty::AssocItem.

@fmease
Copy link
Member

fmease commented Sep 22, 2022

@rustbot label A-cross-crate-reexports A-associated-items

Update: The output is still incorrect but it now looks like this:

pub trait IntoIterator {
    type Item;
    type IntoIter: Iterator
    where
        <Self::IntoIter as Iterator>::Item == Self::Item;

    fn into_iter(self) -> Self::IntoIter;
}

@rustbot claim

@rustbot rustbot added A-cross-crate-reexports Area: Documentation that has been re-exported from a different crate A-associated-items Area: Associated items (types, constants & functions) labels Sep 22, 2022
@bors bors closed this as completed in 2e7e17a Oct 3, 2022
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-associated-items Area: Associated items (types, constants & functions) A-cross-crate-reexports Area: Documentation that has been re-exported from a different crate A-rustdoc-ui Area: Rustdoc UI (generated HTML) C-bug Category: This is a bug. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants