Skip to content

rustdoc: Confusing intra-doc handling of Self in enum #82209

@camelid

Description

@camelid
Member

This code works fine:

pub enum Foo {
    /// [Self::Bar::abc]
    Bar {
        abc: i32,
        xyz: i32,
    },
}

But this code's intra-doc link doesn't resolve:

pub enum Foo {
    Bar {
        abc: i32,
        /// [Self::Bar::abc]
        xyz: i32,
    },
}
warning: unresolved link to `Bar::Bar::abc`
 --> foo.rs:4:14
  |
4 |         /// [Self::Bar::abc]
  |              ^^^^^^^^^^^^^^ no item named `Bar` in scope
  |
  = note: `#[warn(broken_intra_doc_links)]` on by default

Doesn't Self usually only refer to types, not enum variants? (Barring the proposed RFC to make enum variant types.)

Activity

added
T-rustdocRelevant to the rustdoc team, which will review and decide on the PR/issue.
A-intra-doc-linksArea: Intra-doc links, the ability to link to items in docs by name
on Feb 17, 2021
camelid

camelid commented on Feb 17, 2021

@camelid
MemberAuthor

Yeah, this definitely seems like a bug. I have to use Foo::Self::abc (or Foo::Bar::abc). Self::abc doesn't work because it looks for the enum variant as if it's a free-floating type:

warning: unresolved link to `Bar::abc`
 --> foo.rs:4:14
  |
4 |         /// [Self::abc]
  |              ^^^^^^^^^ no item named `Bar` in scope
  |
  = note: `#[warn(broken_intra_doc_links)]` on by default
jyn514

jyn514 commented on Feb 17, 2021

@jyn514
Member

@camelid do you know if this ever worked? I might have broken it in #76467.

jyn514

jyn514 commented on Feb 17, 2021

@jyn514
Member

But also I agree that this seems like a misfeature, I'd be ok with getting rid of it. To do that, just delete variant_field and all references to it.

hellow554

hellow554 commented on Feb 17, 2021

@hellow554
Contributor

hey @jyn514

you are correct. Bisecting this leads to b7ebc6b

So the fix is to get rid of the variant_field method completly (and of course delete any code that calls it)? Or am I misreading it?

jyn514

jyn514 commented on Feb 17, 2021

@jyn514
Member

Yes, I think so. No one noticed this breaking for 12 weeks and Self always refers to the type in normal code, not the variant: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9655fe188d99f728e90e65a08164e825

added
E-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.
E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
on Feb 17, 2021
lucas-deangelis

lucas-deangelis commented on Feb 21, 2021

@lucas-deangelis
Contributor

Hi, I don't know if anyone is already planning to work on it, but if not, would it be a good first issue? I'm still new to the language but I'd like to start contributing.

camelid

camelid commented on Feb 21, 2021

@camelid
MemberAuthor

Yes, this should be a good first issue. You can claim it with @rustbot claim. Post here or on Zulip in #t-compiler/help if you have any questions :)

lucas-deangelis

lucas-deangelis commented on Feb 21, 2021

@lucas-deangelis
Contributor

Thanks! I'll have a look at it. @rustbot claim

lucas-deangelis

lucas-deangelis commented on Feb 22, 2021

@lucas-deangelis
Contributor

I've taken a deeper look at the issue, and at the doc on contributions to Rust. Correct me if I'm wrong, but I think I'm supposed to start with making a failing test case. I made a issue-82209.rs file in src/test/rustdoc/. Here's the file:

#![deny(broken_intra_doc_links)]
pub enum Foo {
    Bar {
        abc: i32,
        /// [Self::Bar::abc]
        xyz: i32,
    },
}

I'm not sure about the #![deny(broken_intra_doc_links)], but I couldn't get the test to fail without something like that, and it's more precise than the #![deny(warnings)] that I used at first. Does this look okay to you?

13 remaining items

jyn514

jyn514 commented on Feb 24, 2021

@jyn514
Member

@lucas-deangelis all the "preprocessing" of the link (turning Self into a path) happens before resolve_link is called, in

let self_name = self_id.and_then(|self_id| {
.

camelid

camelid commented on Feb 25, 2021

@camelid
MemberAuthor

Aha, I bet this is the issue:

} else if matches!(
self.cx.tcx.def_kind(item.def_id),
DefKind::AssocConst
| DefKind::AssocFn
| DefKind::AssocTy
| DefKind::Variant
| DefKind::Field
) {
self.cx.tcx.parent(item.def_id)

For fields in enum variants, the parent is the variant, not the enum.

lucas-deangelis

lucas-deangelis commented on Feb 25, 2021

@lucas-deangelis
Contributor

@lucas-deangelis all the "preprocessing" of the link (turning Self into a path) happens before resolve_link is called, in

Thanks for the clarification.

For fields in enum variants, the parent is the variant, not the enum.

So if I understand correctly, if we encounter a variant, we should return the parent of the parent of item.def_id?

jyn514

jyn514 commented on Feb 25, 2021

@jyn514
Member

So if I understand correctly, if we encounter a variant, we should return the parent of the parent of item.def_id?

If you encounter a field that's in a variant. Basically make the check recursive (since fields may not be in a variant, e.g. struct S { x: usize }).

lucas-deangelis

lucas-deangelis commented on Feb 25, 2021

@lucas-deangelis
Contributor

Thanks, I'll try that.

lucas-deangelis

lucas-deangelis commented on Feb 26, 2021

@lucas-deangelis
Contributor

I think I'm making some progress. I added a new condition to check if we encounter a field that's in a variant before the matches! line 837:

else if (matches!(self.cx.tcx.def_kind(item.def_id), DefKind::Field) && matches!(self.cx.tcx.def_kind(self.cx.tcx.parent(item.def_id).unwrap()), DefKind::Variant)) {
            self.cx.tcx.parent(item.def_id).and_then(|item_id| self.cx.tcx.parent(item_id))
} else if matches!(
            self.cx.tcx.def_kind(item.def_id),
            DefKind::AssocConst
                | DefKind::AssocFn
                | DefKind::AssocTy
                | DefKind::Field
                | DefKind::Variant
) {

I'll have to find a way to handle the call to .unwrap() better once I get this working. Now the error I get is:

error: unresolved link to `Foo::Bar::abc`
 --> rust/src/test/rustdoc/issue-82209.rs:5:14
  |
5 |         /// [Self::Bar::abc]
  |              ^^^^^^^^^^^^^^ the enum `Foo` has no variant or associated item named `abc`
  |

Am I correct in assuming that this is now an error in the link resolution?

jyn514

jyn514 commented on Feb 26, 2021

@jyn514
Member

Hmm, is this still with the calls to variant_field removed? Try adding those back.

lucas-deangelis

lucas-deangelis commented on Feb 26, 2021

@lucas-deangelis
Contributor

Hmm, is this still with the calls to variant_field removed? Try adding those back.

It was, and now with it back it works. Thanks. Should I make a pull request now?

jyn514

jyn514 commented on Feb 26, 2021

@jyn514
Member

That would be great, thanks!

added 2 commits that reference this issue on Feb 27, 2021
d14b18e
b5f5c10
lucas-deangelis

lucas-deangelis commented on Feb 28, 2021

@lucas-deangelis
Contributor

And it's merged! Thank you @jyn514 and @camelid for the help on the issue, @hellow554 for some of the initial work and @Dylan-DPC for the rollups.

jyn514

jyn514 commented on Feb 28, 2021

@jyn514
Member

You're very welcome! Thank you for tackling the fix :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

A-intra-doc-linksArea: Intra-doc links, the ability to link to items in docs by nameC-bugCategory: This is a bug.E-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.P-mediumMedium priorityT-rustdocRelevant to the rustdoc team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    Participants

    @hellow554@jyn514@camelid@lucas-deangelis

    Issue actions

      rustdoc: Confusing intra-doc handling of `Self` in enum · Issue #82209 · rust-lang/rust