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 fails to properly resolve Self in a submodule when the type is not in scope #84827

Open
de-vri-es opened this issue May 2, 2021 · 6 comments
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-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.

Comments

@de-vri-es
Copy link
Contributor

de-vri-es commented May 2, 2021

When a doc comment refers to Self inside an impl block for a type that is not in scope, rustdoc can't properly resolve Self.

It does resolve Self to a more specific name (Foo in the example below), but then it can't find that name.

A minimum example:

struct Foo {
	foo: i32,
}

mod bar {
	impl crate::Foo {
		/// Baz the [`Self::foo`].
		pub fn baz(&self) {
			println!("bazzing the foo");
		}
	}
}

This produces a broken link in the documentation and the following warning:

$ cargo doc
 Documenting foo v0.1.0 (/tmp/2021-05-02-18-45-24/foo)
warning: unresolved link to `Foo::foo`
 --> src/main.rs:7:16
  |
7 |         /// Baz the [`Self::foo`].
  |                      ^^^^^^^^^^^ no item named `Foo` in scope
  |
  = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default

warning: 1 warning emitted

    Finished dev [unoptimized + debuginfo] target(s) in 0.66s

Meta

I tested this using:

  • rustdoc 1.53.0-nightly (42816d6 2021-04-24)
  • rustdoc 1.51.0 (2fd73fa 2021-03-23)

Both show the same problem.

@rustbot modify labels: +A-rustdoc

@de-vri-es de-vri-es added the C-bug Category: This is a bug. label May 2, 2021
@rustbot rustbot added the T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. label May 2, 2021
@m-ou-se m-ou-se added the A-intra-doc-links Area: Intra-doc links, the ability to link to items in docs by name label May 2, 2021
@de-vri-es
Copy link
Contributor Author

Also related: if you do import a type with the same name, then rustdoc will think that is Self.

That can be demonstrated with this example:

pub struct Foo {
	pub foo: i32,
}

pub mod bar {
	use crate::other::Foo;

	impl crate::Foo {
		/// Baz the [`Self::foo`].
		pub fn baz(&self) {
			println!("bazzing the foo");
		}
	}
}

pub mod other {
	pub struct Foo;
}

Which produces the following warning:

warning: unresolved link to `Foo::foo`
 --> src/main.rs:9:16
  |
9 |         /// Baz the [`Self::foo`].
  |                      ^^^^^^^^^^^ the struct `Foo` has no field or associated item named `foo`

And if I add a field with the same name, it really links to the wrong Foo.

@jyn514
Copy link
Member

jyn514 commented May 2, 2021

This is basically the issue from #83761 (comment), rustdoc should use the DefId instead of strings.

@jyn514 jyn514 added the E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. label May 2, 2021
@jyn514
Copy link
Member

jyn514 commented May 2, 2021

Relevant code in case someone wants to take a look:

// FIXME(jynelson): this shouldn't go through stringification, rustdoc should just use the DefId directly
let self_name = self_id.and_then(|self_id| {

@jyn514
Copy link
Member

jyn514 commented May 2, 2021

Basically the way to do this is to only do the string munging in

// Try looking for methods and associated items.
let mut split = path_str.rsplitn(2, "::");
// NB: `split`'s first element is always defined, even if the delimiter was not present.
// NB: `item_str` could be empty when resolving in the root namespace (e.g. `::std`).
let item_str = split.next().unwrap();
let item_name = Symbol::intern(item_str);
let path_root = split
.next()
.map(|f| f.to_owned())
// If there's no `::`, it's not an associated item.
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
.ok_or_else(|| {
debug!("found no `::`, assumming {} was correctly not in scope", item_name);
ResolutionFailure::NotResolved {
module_id,
partial_res: None,
unresolved: item_str.into(),
}
})?;
// FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support
// links to primitives when `#[doc(primitive)]` is present. It should give an ambiguity
// error instead and special case *only* modules with `#[doc(primitive)]`, not all
// primitives.
resolve_primitive(&path_root, TypeNS)
.or_else(|| self.resolve_path(&path_root, TypeNS, module_id))
.and_then(|ty_res| {
let (res, fragment, side_channel) =
self.resolve_associated_item(ty_res, item_name, ns, module_id)?;
if self_id.is_none(); otherwise use self_id in the place of ty_res.

@jyn514 jyn514 added the E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. label May 2, 2021
@jyn514
Copy link
Member

jyn514 commented May 2, 2021

Ditto for

// Try looking for methods and associated items.
let mut split = path_str.rsplitn(2, "::");
// NB: `split`'s first element is always defined, even if the delimiter was not present.
// NB: `item_str` could be empty when resolving in the root namespace (e.g. `::std`).
let item_str = split.next().unwrap();
let item_name = Symbol::intern(item_str);
let path_root = split
.next()
.map(|f| f.to_owned())
// If there's no `::`, it's not an associated item.
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
.ok_or_else(|| {
debug!("found no `::`, assumming {} was correctly not in scope", item_name);
ResolutionFailure::NotResolved {
module_id,
partial_res: None,
unresolved: item_str.into(),
}
})?;
// FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support
// links to primitives when `#[doc(primitive)]` is present. It should give an ambiguity
// error instead and special case *only* modules with `#[doc(primitive)]`, not all
// primitives.
resolve_primitive(&path_root, TypeNS)
.or_else(|| self.resolve_path(&path_root, TypeNS, module_id))
.and_then(|ty_res| {
let (res, fragment, side_channel) =
self.resolve_associated_item(ty_res, item_name, ns, module_id)?;

@tdelabro
Copy link
Contributor

tdelabro commented May 2, 2021

@rustbot claim

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-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. 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.

5 participants