Skip to content

Commit 1106577

Browse files
fix intra-links for trait impls
1 parent 755c02d commit 1106577

File tree

4 files changed

+104
-21
lines changed

4 files changed

+104
-21
lines changed

src/librustc/hir/map/mod.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -709,17 +709,22 @@ impl<'hir> Map<'hir> {
709709
}
710710
}
711711

712-
/// Returns the NodeId of `id`'s nearest module parent, or `id` itself if no
712+
/// Returns the DefId of `id`'s nearest module parent, or `id` itself if no
713713
/// module parent is in this map.
714714
pub fn get_module_parent(&self, id: NodeId) -> DefId {
715-
let id = match self.walk_parent_nodes(id, |node| match *node {
715+
self.local_def_id(self.get_module_parent_node(id))
716+
}
717+
718+
/// Returns the NodeId of `id`'s nearest module parent, or `id` itself if no
719+
/// module parent is in this map.
720+
pub fn get_module_parent_node(&self, id: NodeId) -> NodeId {
721+
match self.walk_parent_nodes(id, |node| match *node {
716722
Node::Item(&Item { node: ItemKind::Mod(_), .. }) => true,
717723
_ => false,
718724
}, |_| false) {
719725
Ok(id) => id,
720726
Err(id) => id,
721-
};
722-
self.local_def_id(id)
727+
}
723728
}
724729

725730
/// Returns the nearest enclosing scope. A scope is an item or block.

src/librustdoc/core.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use rustc_metadata::creader::CrateLoader;
2626
use rustc_metadata::cstore::CStore;
2727
use rustc_target::spec::TargetTriple;
2828

29-
use syntax::ast::{self, Ident};
29+
use syntax::ast::{self, Ident, NodeId};
3030
use syntax::source_map;
3131
use syntax::edition::Edition;
3232
use syntax::feature_gate::UnstableFeatures;
@@ -163,6 +163,16 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocContext<'a, 'tcx, 'rcx, 'cstore> {
163163
def_id.clone()
164164
}
165165

166+
/// Like the function of the same name on the HIR map, but skips calling it on fake DefIds.
167+
/// (This avoids a slice-index-out-of-bounds panic.)
168+
pub fn as_local_node_id(&self, def_id: DefId) -> Option<NodeId> {
169+
if self.all_fake_def_ids.borrow().contains(&def_id) {
170+
None
171+
} else {
172+
self.tcx.hir.as_local_node_id(def_id)
173+
}
174+
}
175+
166176
pub fn get_real_ty<F>(&self,
167177
def_id: DefId,
168178
def_ctor: &F,

src/librustdoc/passes/collect_intra_doc_links.rs

+44-16
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,21 @@ impl<'a, 'tcx, 'rcx, 'cstore> LinkCollector<'a, 'tcx, 'rcx, 'cstore> {
6969
/// Resolve a given string as a path, along with whether or not it is
7070
/// in the value namespace. Also returns an optional URL fragment in the case
7171
/// of variants and methods
72-
fn resolve(&self, path_str: &str, is_val: bool, current_item: &Option<String>)
72+
fn resolve(&self,
73+
path_str: &str,
74+
is_val: bool,
75+
current_item: &Option<String>,
76+
parent_id: Option<NodeId>)
7377
-> Result<(Def, Option<String>), ()>
7478
{
7579
let cx = self.cx;
7680

7781
// In case we're in a module, try to resolve the relative
7882
// path
79-
if let Some(id) = self.mod_ids.last() {
83+
if let Some(id) = parent_id.or(self.mod_ids.last().cloned()) {
84+
// FIXME: `with_scope` requires the NodeId of a module
8085
let result = cx.resolver.borrow_mut()
81-
.with_scope(*id,
86+
.with_scope(id,
8287
|resolver| {
8388
resolver.resolve_str_path_error(DUMMY_SP,
8489
&path_str, is_val)
@@ -129,8 +134,9 @@ impl<'a, 'tcx, 'rcx, 'cstore> LinkCollector<'a, 'tcx, 'rcx, 'cstore> {
129134
}
130135
}
131136

137+
// FIXME: `with_scope` requires the NodeId of a module
132138
let ty = cx.resolver.borrow_mut()
133-
.with_scope(*id,
139+
.with_scope(id,
134140
|resolver| {
135141
resolver.resolve_str_path_error(DUMMY_SP, &path, false)
136142
})?;
@@ -218,6 +224,20 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
218224
None
219225
};
220226

227+
// FIXME: get the resolver to work with non-local resolve scopes
228+
let parent_node = self.cx.as_local_node_id(item.def_id).and_then(|node_id| {
229+
// FIXME: this fails hard for impls in non-module scope, but is necessary for the
230+
// current resolve() implementation
231+
match self.cx.tcx.hir.get_module_parent_node(node_id) {
232+
id if id != node_id => Some(id),
233+
_ => None,
234+
}
235+
});
236+
237+
if parent_node.is_some() {
238+
debug!("got parent node for {} {:?}, id {:?}", item.type_(), item.name, item.def_id);
239+
}
240+
221241
let current_item = match item.inner {
222242
ModuleItem(..) => {
223243
if item.attrs.inner_docs {
@@ -227,10 +247,10 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
227247
None
228248
}
229249
} else {
230-
match self.mod_ids.last() {
231-
Some(parent) if *parent != NodeId::new(0) => {
250+
match parent_node.or(self.mod_ids.last().cloned()) {
251+
Some(parent) if parent != NodeId::new(0) => {
232252
//FIXME: can we pull the parent module's name from elsewhere?
233-
Some(self.cx.tcx.hir.name(*parent).to_string())
253+
Some(self.cx.tcx.hir.name(parent).to_string())
234254
}
235255
_ => None,
236256
}
@@ -294,7 +314,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
294314

295315
match kind {
296316
PathKind::Value => {
297-
if let Ok(def) = self.resolve(path_str, true, &current_item) {
317+
if let Ok(def) = self.resolve(path_str, true, &current_item, parent_node) {
298318
def
299319
} else {
300320
resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
@@ -305,7 +325,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
305325
}
306326
}
307327
PathKind::Type => {
308-
if let Ok(def) = self.resolve(path_str, false, &current_item) {
328+
if let Ok(def) = self.resolve(path_str, false, &current_item, parent_node) {
309329
def
310330
} else {
311331
resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
@@ -316,16 +336,18 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
316336
PathKind::Unknown => {
317337
// try everything!
318338
if let Some(macro_def) = macro_resolve(cx, path_str) {
319-
if let Ok(type_def) = self.resolve(path_str, false, &current_item) {
339+
if let Ok(type_def) =
340+
self.resolve(path_str, false, &current_item, parent_node)
341+
{
320342
let (type_kind, article, type_disambig)
321343
= type_ns_kind(type_def.0, path_str);
322344
ambiguity_error(cx, &item.attrs, path_str,
323345
article, type_kind, &type_disambig,
324346
"a", "macro", &format!("macro@{}", path_str));
325347
continue;
326-
} else if let Ok(value_def) = self.resolve(path_str,
327-
true,
328-
&current_item) {
348+
} else if let Ok(value_def) =
349+
self.resolve(path_str, true, &current_item, parent_node)
350+
{
329351
let (value_kind, value_disambig)
330352
= value_ns_kind(value_def.0, path_str)
331353
.expect("struct and mod cases should have been \
@@ -335,12 +357,16 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
335357
"a", "macro", &format!("macro@{}", path_str));
336358
}
337359
(macro_def, None)
338-
} else if let Ok(type_def) = self.resolve(path_str, false, &current_item) {
360+
} else if let Ok(type_def) =
361+
self.resolve(path_str, false, &current_item, parent_node)
362+
{
339363
// It is imperative we search for not-a-value first
340364
// Otherwise we will find struct ctors for when we are looking
341365
// for structs, and the link won't work.
342366
// if there is something in both namespaces
343-
if let Ok(value_def) = self.resolve(path_str, true, &current_item) {
367+
if let Ok(value_def) =
368+
self.resolve(path_str, true, &current_item, parent_node)
369+
{
344370
let kind = value_ns_kind(value_def.0, path_str);
345371
if let Some((value_kind, value_disambig)) = kind {
346372
let (type_kind, article, type_disambig)
@@ -352,7 +378,9 @@ impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstor
352378
}
353379
}
354380
type_def
355-
} else if let Ok(value_def) = self.resolve(path_str, true, &current_item) {
381+
} else if let Ok(value_def) =
382+
self.resolve(path_str, true, &current_item, parent_node)
383+
{
356384
value_def
357385
} else {
358386
resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// we need to make sure that intra-doc links on trait impls get resolved in the right scope
12+
13+
#![deny(intra_doc_link_resolution_failure)]
14+
15+
pub mod inner {
16+
pub struct SomethingOutOfScope;
17+
}
18+
19+
pub mod other {
20+
use inner::SomethingOutOfScope;
21+
use SomeTrait;
22+
23+
pub struct OtherStruct;
24+
25+
/// Let's link to [SomethingOutOfScope] while we're at it.
26+
impl SomeTrait for OtherStruct {}
27+
}
28+
29+
pub trait SomeTrait {}
30+
31+
pub struct SomeStruct;
32+
33+
fn __implementation_details() {
34+
use inner::SomethingOutOfScope;
35+
36+
// FIXME: intra-links resolve in their nearest module scope, not their actual scope in cases
37+
// like this
38+
// Let's link to [SomethingOutOfScope] while we're at it.
39+
impl SomeTrait for SomeStruct {}
40+
}

0 commit comments

Comments
 (0)