Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

Closed
camelid opened this issue Feb 17, 2021 · 30 comments · Fixed by #82563
Closed

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

camelid opened this issue Feb 17, 2021 · 30 comments · Fixed by #82563
Assignees
Labels
A-intra-doc-links Area: Intra-doc links, the ability to link to items in docs by name C-bug Category: This is a bug. E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. P-medium Medium priority T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.

Comments

@camelid
Copy link
Member

camelid commented Feb 17, 2021

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.)

@camelid camelid added T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. A-intra-doc-links Area: Intra-doc links, the ability to link to items in docs by name labels Feb 17, 2021
@camelid
Copy link
Member Author

camelid commented Feb 17, 2021

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

@camelid camelid added the C-bug Category: This is a bug. label Feb 17, 2021
@jyn514
Copy link
Member

jyn514 commented Feb 17, 2021

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

@jyn514
Copy link
Member

jyn514 commented Feb 17, 2021

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
Copy link
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
Copy link
Member

jyn514 commented Feb 17, 2021

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

@jyn514 jyn514 added E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. labels Feb 17, 2021
@lucas-deangelis
Copy link
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
Copy link
Member Author

camelid commented Feb 21, 2021

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
Copy link
Contributor

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

@lucas-deangelis
Copy link
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?

@jyn514
Copy link
Member

jyn514 commented Feb 22, 2021

@lucas-deangelis yes, that looks fine. I have a couple nits but they can wait until you make a PR to review, that test is enough to show what's going wrong.

@lucas-deangelis
Copy link
Contributor

Thanks! I'll start working on the fix now.

@camelid camelid added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Feb 22, 2021
@camelid
Copy link
Member Author

camelid commented Feb 22, 2021

Assigning P-medium and removing I-prioritize as discussed in the prioritization working group.

@camelid camelid added P-medium Medium priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Feb 22, 2021
@lucas-deangelis
Copy link
Contributor

lucas-deangelis commented Feb 22, 2021

I've tried removing variant_field and all references to it as @jyn514 suggested, however I still had the same behavior as before. There's a part of resolve with FIXME(jynelson): why is this different from variant_field?, but even after removing it, the link is still resolved to Bar::Bar::abc. I think some of the logic is somewhere else.

@jyn514
Copy link
Member

jyn514 commented Feb 22, 2021

Hmm, if that doesn't fix it the bug might be in the Self handling:

let name = self.cx.tcx.opt_item_name(self_id).map(|sym| sym.to_string());

You could try extending
} 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)
to also use the parent item for DefKind::Ctor or DefKind::Variant?

@lucas-deangelis
Copy link
Contributor

lucas-deangelis commented Feb 22, 2021

This alone doesn't seem to fix it, however I've found that after adding DefKind::Ctor on the match, modifying the following part modify the error message for the resolved link:

let is_lone_self = path_str == "Self";
let is_lone_crate = path_str == "crate";
if path_str.starts_with("Self::") || is_lone_self {
if let Some(ref name) = self_name {
if is_lone_self {
path_str = name;
} else {
resolved_self = format!("{}::{}", name, &path_str[6..]);
path_str = &resolved_self;
}
}

The problem here seems to be that name refers to the kind and not the enum.

@camelid
Copy link
Member Author

camelid commented Feb 22, 2021

This alone doesn't seem to fix it, however I've found that after adding DefKind::Ctor on the match

Did you try matching both DefKind::Ctor and DefKind::Variant?

@lucas-deangelis
Copy link
Contributor

This is my naive attempt:

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

I didn't add DefKind::Variant because it is already in the match, however, I think there's something here that I don't understand since you said "matching both", and here I'm matching either of them.

@camelid
Copy link
Member Author

camelid commented Feb 23, 2021

I didn't add DefKind::Variant because it is already in the match, however, I think there's something here that I don't understand since you said "matching both", and here I'm matching either of them.

What you wrote is what I meant :)

I meant match either Ctor or Variant (as in DefKind::Ctor(..) | DefKind::Variant), but I didn't realize that Variant was already in the match.

Does your attempt work?

@lucas-deangelis
Copy link
Contributor

What you wrote is what I meant :)

Nice, thanks for confirming.

I meant match either Ctor or Variant (as in DefKind::Ctor(..) | DefKind::Variant), but I didn't realize that Variant was already in the match.

Does your attempt work?

My attempt doesn't work.

Right now I'm looking at resolve_link, since it's the part where the link is created. I think that by trying to understand more resolve_link, I can figure out from where exactly does the path comes from, and why in that case it gets resolved to Bar::Bar::abc instead of Foo::Bar::abc.

@jyn514
Copy link
Member

jyn514 commented Feb 24, 2021

@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
Copy link
Member Author

camelid commented Feb 25, 2021

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
Copy link
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
Copy link
Member

jyn514 commented Feb 25, 2021

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
Copy link
Contributor

Thanks, I'll try that.

@lucas-deangelis
Copy link
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
Copy link
Member

jyn514 commented Feb 26, 2021

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

@lucas-deangelis
Copy link
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
Copy link
Member

jyn514 commented Feb 26, 2021

That would be great, thanks!

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Feb 27, 2021
…jyn514

Fix intra-doc handling of `Self` in enum

Fixes rust-lang#82209
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Feb 27, 2021
…jyn514

Fix intra-doc handling of `Self` in enum

Fixes rust-lang#82209
@bors bors closed this as completed in 5661fe3 Feb 28, 2021
@lucas-deangelis
Copy link
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
Copy link
Member

jyn514 commented Feb 28, 2021

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
Labels
A-intra-doc-links Area: Intra-doc links, the ability to link to items in docs by name C-bug Category: This is a bug. E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. P-medium Medium priority 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