-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Resolve items for cross-crate imports relative to the original module #73101
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
Changes from all commits
5271e98
848a766
69bd13f
c3d9a73
20106d5
24c3d85
9542e23
99f34d8
e78d499
9eb6394
71fe8f7
432b043
769acba
5f49f55
e63e5cd
82b3b07
0ad1dcd
8387e38
c46e038
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ use rustc_hir::def::{ | |
Namespace::{self, *}, | ||
PerNS, Res, | ||
}; | ||
use rustc_hir::def_id::{DefId, LocalDefId}; | ||
use rustc_hir::def_id::DefId; | ||
use rustc_middle::ty; | ||
use rustc_resolve::ParentScope; | ||
use rustc_session::lint; | ||
|
@@ -50,7 +50,8 @@ enum ErrorKind { | |
|
||
struct LinkCollector<'a, 'tcx> { | ||
cx: &'a DocContext<'tcx>, | ||
mod_ids: Vec<hir::HirId>, | ||
// NOTE: this may not necessarily be a module in the current crate | ||
mod_ids: Vec<DefId>, | ||
} | ||
|
||
impl<'a, 'tcx> LinkCollector<'a, 'tcx> { | ||
|
@@ -62,7 +63,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { | |
&self, | ||
path_str: &str, | ||
current_item: &Option<String>, | ||
module_id: LocalDefId, | ||
module_id: DefId, | ||
) -> Result<(Res, Option<String>), ErrorKind> { | ||
let cx = self.cx; | ||
|
||
|
@@ -124,7 +125,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { | |
} | ||
|
||
/// Resolves a string as a macro. | ||
fn macro_resolve(&self, path_str: &str, parent_id: Option<hir::HirId>) -> Option<Res> { | ||
fn macro_resolve(&self, path_str: &str, parent_id: Option<DefId>) -> Option<Res> { | ||
let cx = self.cx; | ||
let path = ast::Path::from_ident(Ident::from_str(path_str)); | ||
cx.enter_resolver(|resolver| { | ||
|
@@ -142,8 +143,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { | |
if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) { | ||
return Some(res.map_id(|_| panic!("unexpected id"))); | ||
} | ||
if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) { | ||
let module_id = cx.tcx.hir().local_def_id(module_id); | ||
if let Some(module_id) = parent_id { | ||
if let Ok((_, res)) = | ||
resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id) | ||
{ | ||
|
@@ -167,15 +167,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { | |
disambiguator: Option<&str>, | ||
ns: Namespace, | ||
current_item: &Option<String>, | ||
parent_id: Option<hir::HirId>, | ||
parent_id: Option<DefId>, | ||
extra_fragment: &Option<String>, | ||
item_opt: Option<&Item>, | ||
) -> Result<(Res, Option<String>), ErrorKind> { | ||
let cx = self.cx; | ||
|
||
// In case we're in a module, try to resolve the relative path. | ||
if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) { | ||
let module_id = cx.tcx.hir().local_def_id(module_id); | ||
if let Some(module_id) = parent_id { | ||
let result = cx.enter_resolver(|resolver| { | ||
resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id) | ||
}); | ||
|
@@ -445,40 +444,40 @@ fn is_derive_trait_collision<T>(ns: &PerNS<Option<(Res, T)>>) -> bool { | |
|
||
impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { | ||
fn fold_item(&mut self, mut item: Item) -> Option<Item> { | ||
let item_hir_id = if item.is_mod() { | ||
if let Some(def_id) = item.def_id.as_local() { | ||
Some(self.cx.tcx.hir().as_local_hir_id(def_id)) | ||
} else { | ||
debug!("attempting to fold on a non-local item: {:?}", item); | ||
return self.fold_item_recur(item); | ||
} | ||
} else { | ||
None | ||
}; | ||
use rustc_middle::ty::DefIdTree; | ||
|
||
// FIXME: get the resolver to work with non-local resolve scopes. | ||
let parent_node = self.cx.as_local_hir_id(item.def_id).and_then(|hir_id| { | ||
// FIXME: this fails hard for impls in non-module scope, but is necessary for the | ||
// current `resolve()` implementation. | ||
match self.cx.as_local_hir_id(self.cx.tcx.parent_module(hir_id).to_def_id()).unwrap() { | ||
id if id != hir_id => Some(id), | ||
_ => None, | ||
let parent_node = if item.is_fake() { | ||
// FIXME: is this correct? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect we'll never hit this case, it's only for auto traits and blanket impls, which don't get docs resolved on the local scope anyway. What happens if you panic here? Especially if you try and document a blanket impl that uses intra doc links? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rustdoc panics if I don't check this (with or without an explicit panic):
This was the motivation for #73098. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And actually it will panic on any file, including empty files. |
||
None | ||
} else { | ||
let mut current = item.def_id; | ||
// The immediate parent might not always be a module. | ||
// Find the first parent which is. | ||
loop { | ||
if let Some(parent) = self.cx.tcx.parent(current) { | ||
if self.cx.tcx.def_kind(parent) == DefKind::Mod { | ||
break Some(parent); | ||
} | ||
current = parent; | ||
} else { | ||
break None; | ||
} | ||
} | ||
}); | ||
}; | ||
|
||
if parent_node.is_some() { | ||
debug!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id); | ||
trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id); | ||
} | ||
|
||
let current_item = match item.inner { | ||
ModuleItem(..) => { | ||
if item.attrs.inner_docs { | ||
if item_hir_id.unwrap() != hir::CRATE_HIR_ID { item.name.clone() } else { None } | ||
if item.def_id.is_top_level_module() { item.name.clone() } else { None } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bug, there should be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This bit is getting removed altogether in #76467, so I don't think there's much point in fixing it. |
||
} else { | ||
match parent_node.or(self.mod_ids.last().cloned()) { | ||
Some(parent) if parent != hir::CRATE_HIR_ID => { | ||
match parent_node.or(self.mod_ids.last().copied()) { | ||
Some(parent) if !parent.is_top_level_module() => { | ||
// FIXME: can we pull the parent module's name from elsewhere? | ||
Some(self.cx.tcx.hir().name(parent).to_string()) | ||
Some(self.cx.tcx.item_name(parent).to_string()) | ||
} | ||
_ => None, | ||
} | ||
|
@@ -488,18 +487,22 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { | |
for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string()) | ||
} | ||
// we don't display docs on `extern crate` items anyway, so don't process them. | ||
ExternCrateItem(..) => return self.fold_item_recur(item), | ||
ExternCrateItem(..) => { | ||
debug!("ignoring extern crate item {:?}", item.def_id); | ||
return self.fold_item_recur(item); | ||
} | ||
ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()), | ||
MacroItem(..) => None, | ||
_ => item.name.clone(), | ||
}; | ||
|
||
if item.is_mod() && item.attrs.inner_docs { | ||
self.mod_ids.push(item_hir_id.unwrap()); | ||
self.mod_ids.push(item.def_id); | ||
} | ||
|
||
let cx = self.cx; | ||
let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new); | ||
trace!("got documentation '{}'", dox); | ||
|
||
look_for_tests(&cx, &dox, &item, true); | ||
|
||
|
@@ -541,6 +544,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { | |
}); | ||
|
||
for (ori_link, link_range) in markdown_links(&dox) { | ||
trace!("considering link '{}'", ori_link); | ||
|
||
// Bail early for real links. | ||
if ori_link.contains('/') { | ||
continue; | ||
|
@@ -641,8 +646,11 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { | |
// we've already pushed this node onto the resolution stack but | ||
// for outer comments we explicitly try and resolve against the | ||
// parent_node first. | ||
let base_node = | ||
if item.is_mod() && item.attrs.inner_docs { None } else { parent_node }; | ||
let base_node = if item.is_mod() && item.attrs.inner_docs { | ||
self.mod_ids.last().copied() | ||
} else { | ||
parent_node | ||
}; | ||
|
||
// replace `Self` with suitable item's parent name | ||
if path_str.starts_with("Self::") { | ||
|
@@ -826,7 +834,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { | |
} | ||
|
||
if item.is_mod() && !item.attrs.inner_docs { | ||
self.mod_ids.push(item_hir_id.unwrap()); | ||
self.mod_ids.push(item.def_id); | ||
} | ||
|
||
if item.is_mod() { | ||
|
@@ -864,6 +872,7 @@ fn build_diagnostic( | |
Some(hir_id) => hir_id, | ||
None => { | ||
// If non-local, no need to check anything. | ||
info!("ignoring warning from parent crate: {}", err_msg); | ||
return; | ||
} | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// aux-build:additional_doc.rs | ||
// build-aux-docs | ||
#![deny(intra_doc_link_resolution_failure)] | ||
|
||
extern crate my_rand; | ||
|
||
// @has 'additional_doc/trait.Rng.html' '//a[@href="../additional_doc/trait.Rng.html"]' 'Rng' | ||
// @has 'additional_doc/trait.Rng.html' '//a[@href="../my_rand/trait.RngCore.html"]' 'RngCore' | ||
/// This is an [`Rng`]. | ||
pub use my_rand::Rng; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#![crate_name = "my_rand"] | ||
#![deny(intra_doc_link_resolution_failure)] | ||
|
||
pub trait RngCore {} | ||
/// Rng extends [`RngCore`]. | ||
pub trait Rng: RngCore {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#![crate_name = "a"] | ||
#![deny(intra_doc_link_resolution_failure)] | ||
|
||
pub struct Foo; | ||
|
||
/// Link to [Foo] | ||
pub struct Bar; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#![crate_name = "macro_inner"] | ||
#![deny(intra_doc_link_resolution_failure)] | ||
|
||
pub struct Foo; | ||
|
||
/// See also [`Foo`] | ||
#[macro_export] | ||
macro_rules! my_macro { | ||
() => {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#![crate_name = "module_inner"] | ||
#![deny(intra_doc_link_resolution_failure)] | ||
/// [SomeType] links to [bar] | ||
pub struct SomeType; | ||
pub trait SomeTrait {} | ||
/// [bar] links to [SomeTrait] and also [SomeType] | ||
pub mod bar {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// force-host | ||
// no-prefer-dynamic | ||
// compile-flags: --crate-type proc-macro | ||
#![crate_type="proc-macro"] | ||
#![crate_name="proc_macro_inner"] | ||
|
||
extern crate proc_macro; | ||
|
||
use proc_macro::TokenStream; | ||
|
||
/// Links to [`OtherDerive`] | ||
#[proc_macro_derive(DeriveA)] | ||
pub fn a_derive(input: TokenStream) -> TokenStream { | ||
input | ||
} | ||
|
||
#[proc_macro_derive(OtherDerive)] | ||
pub fn other_derive(input: TokenStream) -> TokenStream { | ||
input | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#![crate_name = "a"] | ||
#![deny(intra_doc_link_resolution_failure)] | ||
|
||
pub mod bar { | ||
pub struct Bar; | ||
} | ||
|
||
pub mod foo { | ||
use crate::bar; | ||
/// link to [bar::Bar] | ||
pub struct Foo; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#![crate_name = "bar"] | ||
#![deny(intra_doc_link_resolution_failure)] | ||
|
||
pub trait Foo { | ||
/// [`Bar`] [`Baz`] | ||
fn foo(); | ||
} | ||
|
||
pub trait Bar { | ||
} | ||
|
||
pub trait Baz { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#![crate_name = "inner"] | ||
/// this is a trait | ||
pub trait SomeTrait { | ||
/// this is a method for [a trait][SomeTrait] | ||
fn foo(); | ||
} | ||
|
||
pub mod bar { | ||
use super::SomeTrait; | ||
|
||
pub struct BarStruct; | ||
|
||
impl SomeTrait for BarStruct { | ||
fn foo() {} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// aux-build:intra-doc-basic.rs | ||
// build-aux-docs | ||
#![deny(intra_doc_link_resolution_failure)] | ||
|
||
// from https://github.com/rust-lang/rust/issues/65983 | ||
extern crate a; | ||
|
||
// @has 'basic/struct.Bar.html' '//a[@href="../a/struct.Foo.html"]' 'Foo' | ||
pub use a::Bar; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// ignore-tidy-linelength | ||
// aux-build:macro_inner.rs | ||
// aux-build:proc_macro.rs | ||
// build-aux-docs | ||
#![deny(intra_doc_link_resolution_failure)] | ||
extern crate macro_inner; | ||
extern crate proc_macro_inner; | ||
|
||
// @has 'macro/macro.my_macro.html' '//a[@href="../macro_inner/struct.Foo.html"]' 'Foo' | ||
pub use macro_inner::my_macro; | ||
// @has 'macro/derive.DeriveA.html' '//a[@href="../proc_macro_inner/derive.OtherDerive.html"]' 'OtherDerive' | ||
pub use proc_macro_inner::DeriveA; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// outer.rs | ||
// aux-build: module.rs | ||
// build-aux-docs | ||
#![deny(intra_doc_link_resolution_failure)] | ||
extern crate module_inner; | ||
// @has 'module/bar/index.html' '//a[@href="../../module_inner/trait.SomeTrait.html"]' 'SomeTrait' | ||
// @has 'module/bar/index.html' '//a[@href="../../module_inner/struct.SomeType.html"]' 'SomeType' | ||
pub use module_inner::bar; |
Uh oh!
There was an error while loading. Please reload this page.