mirror of https://github.com/rust-lang/rust.git
Auto merge of #77519 - jyn514:track-doc-er, r=GuillaumeGomez
Resolve intra-doc links on additional documentation for re-exports in lexical scope Fixes https://github.com/rust-lang/rust/issues/77254. - Preserve the parent module of `DocFragment`s + Add `parent_module` to `DocFragment` + Require the `parent_module` of the item being inlined + Preserve the hir_id for ExternCrates so rustdoc can find the parent module later + Take an optional `parent_module` for `build_impl` and `merge_attrs`. Preserve the difference between parent modules for each doc-comment. + Support a single additional re-exports in from_ast. Originally this took a vec but I ended up not using it. + Don't require the parent_module for all `impl`s, just inlined items In particular, this will be `None` whenever the attribute is not on a re-export. + Only store the parent_module, not the HirId When re-exporting a re-export, the HirId is not available. Fortunately, `collect_intra_doc_links` doesn't actually need all the info from a HirId, just the parent module. - Introduce `Divider` This distinguishes between documentation on the original from docs on the re-export. - Use the new module information for intra-doc links + Make the parent module conditional on whether the docs are on a re-export + Make `resolve_link` take `&Item` instead of `&mut Item` Previously the borrow checker gave an error about multiple mutable borrows, because `dox` borrowed from `item`. + Fix `crate::` for re-exports `crate` means something different depending on where the attribute came from. + Make it work for `#[doc]` attributes too This required combining several attributes as one so they would keep the links. r? `@GuillaumeGomez`
This commit is contained in:
commit
9ba1d21868
|
@ -15,7 +15,7 @@ use rustc_span::hygiene::MacroKind;
|
|||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::clean::{self, GetDefId, ToSource, TypeKind};
|
||||
use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind};
|
||||
use crate::core::DocContext;
|
||||
use crate::doctree;
|
||||
|
||||
|
@ -35,8 +35,11 @@ type Attrs<'hir> = rustc_middle::ty::Attributes<'hir>;
|
|||
///
|
||||
/// The returned value is `None` if the definition could not be inlined,
|
||||
/// and `Some` of a vector of items if it was successfully expanded.
|
||||
///
|
||||
/// `parent_module` refers to the parent of the *re-export*, not the original item.
|
||||
pub fn try_inline(
|
||||
cx: &DocContext<'_>,
|
||||
parent_module: DefId,
|
||||
res: Res,
|
||||
name: Symbol,
|
||||
attrs: Option<Attrs<'_>>,
|
||||
|
@ -48,12 +51,13 @@ pub fn try_inline(
|
|||
}
|
||||
let mut ret = Vec::new();
|
||||
|
||||
debug!("attrs={:?}", attrs);
|
||||
let attrs_clone = attrs;
|
||||
|
||||
let inner = match res {
|
||||
Res::Def(DefKind::Trait, did) => {
|
||||
record_extern_fqn(cx, did, clean::TypeKind::Trait);
|
||||
ret.extend(build_impls(cx, did, attrs));
|
||||
ret.extend(build_impls(cx, Some(parent_module), did, attrs));
|
||||
clean::TraitItem(build_external_trait(cx, did))
|
||||
}
|
||||
Res::Def(DefKind::Fn, did) => {
|
||||
|
@ -62,27 +66,27 @@ pub fn try_inline(
|
|||
}
|
||||
Res::Def(DefKind::Struct, did) => {
|
||||
record_extern_fqn(cx, did, clean::TypeKind::Struct);
|
||||
ret.extend(build_impls(cx, did, attrs));
|
||||
ret.extend(build_impls(cx, Some(parent_module), did, attrs));
|
||||
clean::StructItem(build_struct(cx, did))
|
||||
}
|
||||
Res::Def(DefKind::Union, did) => {
|
||||
record_extern_fqn(cx, did, clean::TypeKind::Union);
|
||||
ret.extend(build_impls(cx, did, attrs));
|
||||
ret.extend(build_impls(cx, Some(parent_module), did, attrs));
|
||||
clean::UnionItem(build_union(cx, did))
|
||||
}
|
||||
Res::Def(DefKind::TyAlias, did) => {
|
||||
record_extern_fqn(cx, did, clean::TypeKind::Typedef);
|
||||
ret.extend(build_impls(cx, did, attrs));
|
||||
ret.extend(build_impls(cx, Some(parent_module), did, attrs));
|
||||
clean::TypedefItem(build_type_alias(cx, did), false)
|
||||
}
|
||||
Res::Def(DefKind::Enum, did) => {
|
||||
record_extern_fqn(cx, did, clean::TypeKind::Enum);
|
||||
ret.extend(build_impls(cx, did, attrs));
|
||||
ret.extend(build_impls(cx, Some(parent_module), did, attrs));
|
||||
clean::EnumItem(build_enum(cx, did))
|
||||
}
|
||||
Res::Def(DefKind::ForeignTy, did) => {
|
||||
record_extern_fqn(cx, did, clean::TypeKind::Foreign);
|
||||
ret.extend(build_impls(cx, did, attrs));
|
||||
ret.extend(build_impls(cx, Some(parent_module), did, attrs));
|
||||
clean::ForeignTypeItem
|
||||
}
|
||||
// Never inline enum variants but leave them shown as re-exports.
|
||||
|
@ -117,7 +121,7 @@ pub fn try_inline(
|
|||
};
|
||||
|
||||
let target_attrs = load_attrs(cx, did);
|
||||
let attrs = merge_attrs(cx, target_attrs, attrs_clone);
|
||||
let attrs = merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone);
|
||||
|
||||
cx.renderinfo.borrow_mut().inlined.insert(did);
|
||||
ret.push(clean::Item {
|
||||
|
@ -291,40 +295,52 @@ pub fn build_ty(cx: &DocContext<'_>, did: DefId) -> Option<clean::Type> {
|
|||
}
|
||||
|
||||
/// Builds all inherent implementations of an ADT (struct/union/enum) or Trait item/path/reexport.
|
||||
pub fn build_impls(cx: &DocContext<'_>, did: DefId, attrs: Option<Attrs<'_>>) -> Vec<clean::Item> {
|
||||
pub fn build_impls(
|
||||
cx: &DocContext<'_>,
|
||||
parent_module: Option<DefId>,
|
||||
did: DefId,
|
||||
attrs: Option<Attrs<'_>>,
|
||||
) -> Vec<clean::Item> {
|
||||
let tcx = cx.tcx;
|
||||
let mut impls = Vec::new();
|
||||
|
||||
// for each implementation of an item represented by `did`, build the clean::Item for that impl
|
||||
for &did in tcx.inherent_impls(did).iter() {
|
||||
build_impl(cx, did, attrs, &mut impls);
|
||||
build_impl(cx, parent_module, did, attrs, &mut impls);
|
||||
}
|
||||
|
||||
impls
|
||||
}
|
||||
|
||||
/// `parent_module` refers to the parent of the re-export, not the original item
|
||||
fn merge_attrs(
|
||||
cx: &DocContext<'_>,
|
||||
attrs: Attrs<'_>,
|
||||
other_attrs: Option<Attrs<'_>>,
|
||||
parent_module: Option<DefId>,
|
||||
old_attrs: Attrs<'_>,
|
||||
new_attrs: Option<Attrs<'_>>,
|
||||
) -> clean::Attributes {
|
||||
// NOTE: If we have additional attributes (from a re-export),
|
||||
// always insert them first. This ensure that re-export
|
||||
// doc comments show up before the original doc comments
|
||||
// when we render them.
|
||||
let merged_attrs = if let Some(inner) = other_attrs {
|
||||
let mut both = inner.to_vec();
|
||||
both.extend_from_slice(attrs);
|
||||
both
|
||||
if let Some(inner) = new_attrs {
|
||||
if let Some(new_id) = parent_module {
|
||||
let diag = cx.sess().diagnostic();
|
||||
Attributes::from_ast(diag, old_attrs, Some((inner, new_id)))
|
||||
} else {
|
||||
let mut both = inner.to_vec();
|
||||
both.extend_from_slice(old_attrs);
|
||||
both.clean(cx)
|
||||
}
|
||||
} else {
|
||||
attrs.to_vec()
|
||||
};
|
||||
merged_attrs.clean(cx)
|
||||
old_attrs.clean(cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a specific implementation of a type. The `did` could be a type method or trait method.
|
||||
pub fn build_impl(
|
||||
cx: &DocContext<'_>,
|
||||
parent_module: impl Into<Option<DefId>>,
|
||||
did: DefId,
|
||||
attrs: Option<Attrs<'_>>,
|
||||
ret: &mut Vec<clean::Item>,
|
||||
|
@ -333,7 +349,8 @@ pub fn build_impl(
|
|||
return;
|
||||
}
|
||||
|
||||
let attrs = merge_attrs(cx, load_attrs(cx, did), attrs);
|
||||
let attrs = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
|
||||
debug!("merged_attrs={:?}", attrs);
|
||||
|
||||
let tcx = cx.tcx;
|
||||
let associated_trait = tcx.impl_trait_ref(did);
|
||||
|
@ -499,7 +516,9 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet<DefId>)
|
|||
},
|
||||
)),
|
||||
});
|
||||
} else if let Some(i) = try_inline(cx, item.res, item.ident.name, None, visited) {
|
||||
} else if let Some(i) =
|
||||
try_inline(cx, did, item.res, item.ident.name, None, visited)
|
||||
{
|
||||
items.extend(i)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,7 +284,7 @@ impl Clean<Item> for doctree::Module<'_> {
|
|||
|
||||
impl Clean<Attributes> for [ast::Attribute] {
|
||||
fn clean(&self, cx: &DocContext<'_>) -> Attributes {
|
||||
Attributes::from_ast(cx.sess().diagnostic(), self)
|
||||
Attributes::from_ast(cx.sess().diagnostic(), self, None)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2205,9 +2205,14 @@ impl Clean<Vec<Item>> for doctree::ExternCrate<'_> {
|
|||
|
||||
let res = Res::Def(DefKind::Mod, DefId { krate: self.cnum, index: CRATE_DEF_INDEX });
|
||||
|
||||
if let Some(items) =
|
||||
inline::try_inline(cx, res, self.name, Some(self.attrs), &mut visited)
|
||||
{
|
||||
if let Some(items) = inline::try_inline(
|
||||
cx,
|
||||
cx.tcx.parent_module(self.hir_id).to_def_id(),
|
||||
res,
|
||||
self.name,
|
||||
Some(self.attrs),
|
||||
&mut visited,
|
||||
) {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
@ -2268,9 +2273,14 @@ impl Clean<Vec<Item>> for doctree::Import<'_> {
|
|||
}
|
||||
if !denied {
|
||||
let mut visited = FxHashSet::default();
|
||||
if let Some(items) =
|
||||
inline::try_inline(cx, path.res, name, Some(self.attrs), &mut visited)
|
||||
{
|
||||
if let Some(items) = inline::try_inline(
|
||||
cx,
|
||||
cx.tcx.parent_module(self.id).to_def_id(),
|
||||
path.res,
|
||||
name,
|
||||
Some(self.attrs),
|
||||
&mut visited,
|
||||
) {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -373,6 +373,11 @@ impl<I: IntoIterator<Item = ast::NestedMetaItem>> NestedAttributesExt for I {
|
|||
pub struct DocFragment {
|
||||
pub line: usize,
|
||||
pub span: rustc_span::Span,
|
||||
/// The module this doc-comment came from.
|
||||
///
|
||||
/// This allows distinguishing between the original documentation and a pub re-export.
|
||||
/// If it is `None`, the item was not re-exported.
|
||||
pub parent_module: Option<DefId>,
|
||||
pub doc: String,
|
||||
pub kind: DocFragmentKind,
|
||||
}
|
||||
|
@ -386,6 +391,24 @@ pub enum DocFragmentKind {
|
|||
/// A doc fragment created from a `#[doc(include="filename")]` attribute. Contains both the
|
||||
/// given filename and the file contents.
|
||||
Include { filename: String },
|
||||
/// A doc fragment used to distinguish between documentation in different modules.
|
||||
///
|
||||
/// In particular, this prevents `collapse_docs` from turning all documentation comments
|
||||
/// into a single giant attributes even when the item is re-exported with documentation on the re-export.
|
||||
Divider,
|
||||
}
|
||||
|
||||
impl DocFragment {
|
||||
/// Creates a dummy doc-fragment which divides earlier and later fragments.
|
||||
fn divider() -> Self {
|
||||
DocFragment {
|
||||
line: 0,
|
||||
span: DUMMY_SP,
|
||||
parent_module: None,
|
||||
doc: String::new(),
|
||||
kind: DocFragmentKind::Divider,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<&'a DocFragment> for String {
|
||||
|
@ -521,57 +544,77 @@ impl Attributes {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn from_ast(diagnostic: &::rustc_errors::Handler, attrs: &[ast::Attribute]) -> Attributes {
|
||||
let mut doc_strings = vec![];
|
||||
pub fn from_ast(
|
||||
diagnostic: &::rustc_errors::Handler,
|
||||
attrs: &[ast::Attribute],
|
||||
additional_attrs: Option<(&[ast::Attribute], DefId)>,
|
||||
) -> Attributes {
|
||||
let doc_strings = RefCell::new(vec![]);
|
||||
let mut sp = None;
|
||||
let mut cfg = Cfg::True;
|
||||
let mut doc_line = 0;
|
||||
|
||||
let other_attrs = attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if let Some(value) = attr.doc_str() {
|
||||
let value = beautify_doc_string(value);
|
||||
let kind = if attr.is_doc_comment() {
|
||||
DocFragmentKind::SugaredDoc
|
||||
} else {
|
||||
DocFragmentKind::RawDoc
|
||||
};
|
||||
|
||||
let line = doc_line;
|
||||
doc_line += value.lines().count();
|
||||
doc_strings.push(DocFragment { line, span: attr.span, doc: value, kind });
|
||||
|
||||
if sp.is_none() {
|
||||
sp = Some(attr.span);
|
||||
}
|
||||
None
|
||||
let clean_attr = |(attr, parent_module): (&ast::Attribute, _)| {
|
||||
if let Some(value) = attr.doc_str() {
|
||||
trace!("got doc_str={:?}", value);
|
||||
let value = beautify_doc_string(value);
|
||||
let kind = if attr.is_doc_comment() {
|
||||
DocFragmentKind::SugaredDoc
|
||||
} else {
|
||||
if attr.has_name(sym::doc) {
|
||||
if let Some(mi) = attr.meta() {
|
||||
if let Some(cfg_mi) = Attributes::extract_cfg(&mi) {
|
||||
// Extracted #[doc(cfg(...))]
|
||||
match Cfg::parse(cfg_mi) {
|
||||
Ok(new_cfg) => cfg &= new_cfg,
|
||||
Err(e) => diagnostic.span_err(e.span, e.msg),
|
||||
}
|
||||
} else if let Some((filename, contents)) =
|
||||
Attributes::extract_include(&mi)
|
||||
{
|
||||
let line = doc_line;
|
||||
doc_line += contents.lines().count();
|
||||
doc_strings.push(DocFragment {
|
||||
line,
|
||||
span: attr.span,
|
||||
doc: contents,
|
||||
kind: DocFragmentKind::Include { filename },
|
||||
});
|
||||
DocFragmentKind::RawDoc
|
||||
};
|
||||
|
||||
let line = doc_line;
|
||||
doc_line += value.lines().count();
|
||||
doc_strings.borrow_mut().push(DocFragment {
|
||||
line,
|
||||
span: attr.span,
|
||||
doc: value,
|
||||
kind,
|
||||
parent_module,
|
||||
});
|
||||
|
||||
if sp.is_none() {
|
||||
sp = Some(attr.span);
|
||||
}
|
||||
None
|
||||
} else {
|
||||
if attr.has_name(sym::doc) {
|
||||
if let Some(mi) = attr.meta() {
|
||||
if let Some(cfg_mi) = Attributes::extract_cfg(&mi) {
|
||||
// Extracted #[doc(cfg(...))]
|
||||
match Cfg::parse(cfg_mi) {
|
||||
Ok(new_cfg) => cfg &= new_cfg,
|
||||
Err(e) => diagnostic.span_err(e.span, e.msg),
|
||||
}
|
||||
} else if let Some((filename, contents)) = Attributes::extract_include(&mi)
|
||||
{
|
||||
let line = doc_line;
|
||||
doc_line += contents.lines().count();
|
||||
doc_strings.borrow_mut().push(DocFragment {
|
||||
line,
|
||||
span: attr.span,
|
||||
doc: contents,
|
||||
kind: DocFragmentKind::Include { filename },
|
||||
parent_module: parent_module,
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(attr.clone())
|
||||
}
|
||||
Some(attr.clone())
|
||||
}
|
||||
};
|
||||
|
||||
// Additional documentation should be shown before the original documentation
|
||||
let other_attrs = additional_attrs
|
||||
.into_iter()
|
||||
.map(|(attrs, id)| {
|
||||
doc_strings.borrow_mut().push(DocFragment::divider());
|
||||
attrs.iter().map(move |attr| (attr, Some(id)))
|
||||
})
|
||||
.flatten()
|
||||
.chain(attrs.iter().map(|attr| (attr, None)))
|
||||
.filter_map(clean_attr)
|
||||
.collect();
|
||||
|
||||
// treat #[target_feature(enable = "feat")] attributes as if they were
|
||||
|
@ -597,7 +640,7 @@ impl Attributes {
|
|||
.map_or(true, |a| a.style == AttrStyle::Inner);
|
||||
|
||||
Attributes {
|
||||
doc_strings,
|
||||
doc_strings: doc_strings.into_inner(),
|
||||
other_attrs,
|
||||
cfg: if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) },
|
||||
span: sp,
|
||||
|
|
|
@ -361,7 +361,7 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V
|
|||
let primitive = match *target {
|
||||
ResolvedPath { did, .. } if did.is_local() => continue,
|
||||
ResolvedPath { did, .. } => {
|
||||
ret.extend(inline::build_impls(cx, did, None));
|
||||
ret.extend(inline::build_impls(cx, None, did, None));
|
||||
continue;
|
||||
}
|
||||
_ => match target.primitive_type() {
|
||||
|
@ -371,7 +371,7 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V
|
|||
};
|
||||
for &did in primitive.impls(tcx) {
|
||||
if !did.is_local() {
|
||||
inline::build_impl(cx, did, None, ret);
|
||||
inline::build_impl(cx, None, did, None, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -927,7 +927,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
|
|||
sp: Span,
|
||||
nested: F,
|
||||
) {
|
||||
let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs);
|
||||
let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs, None);
|
||||
if let Some(ref cfg) = attrs.cfg {
|
||||
if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
|
||||
return;
|
||||
|
|
|
@ -8,6 +8,7 @@ use rustc_span::{self, Span, Symbol};
|
|||
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::CrateNum;
|
||||
use rustc_hir::HirId;
|
||||
|
||||
pub struct Module<'hir> {
|
||||
pub name: Option<Symbol>,
|
||||
|
@ -236,6 +237,7 @@ pub struct Macro<'hir> {
|
|||
|
||||
pub struct ExternCrate<'hir> {
|
||||
pub name: Symbol,
|
||||
pub hir_id: HirId,
|
||||
pub cnum: CrateNum,
|
||||
pub path: Option<String>,
|
||||
pub vis: &'hir hir::Visibility<'hir>,
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_hir::def::{
|
|||
Namespace::{self, *},
|
||||
PerNS, Res,
|
||||
};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::{CrateNum, DefId};
|
||||
use rustc_middle::ty;
|
||||
use rustc_resolve::ParentScope;
|
||||
use rustc_session::lint::{
|
||||
|
@ -767,9 +767,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
|||
self.mod_ids.push(item.def_id);
|
||||
}
|
||||
|
||||
let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
|
||||
trace!("got documentation '{}'", dox);
|
||||
|
||||
// find item's parent to resolve `Self` in item's docs below
|
||||
let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| {
|
||||
let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir);
|
||||
|
@ -807,16 +804,53 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
|||
}
|
||||
});
|
||||
|
||||
for (ori_link, link_range) in markdown_links(&dox) {
|
||||
self.resolve_link(
|
||||
&mut item,
|
||||
&dox,
|
||||
¤t_item,
|
||||
parent_node,
|
||||
&parent_name,
|
||||
ori_link,
|
||||
link_range,
|
||||
);
|
||||
// We want to resolve in the lexical scope of the documentation.
|
||||
// In the presence of re-exports, this is not the same as the module of the item.
|
||||
// Rather than merging all documentation into one, resolve it one attribute at a time
|
||||
// so we know which module it came from.
|
||||
let mut attrs = item.attrs.doc_strings.iter().peekable();
|
||||
while let Some(attr) = attrs.next() {
|
||||
// `collapse_docs` does not have the behavior we want:
|
||||
// we want `///` and `#[doc]` to count as the same attribute,
|
||||
// but currently it will treat them as separate.
|
||||
// As a workaround, combine all attributes with the same parent module into the same attribute.
|
||||
let mut combined_docs = attr.doc.clone();
|
||||
loop {
|
||||
match attrs.peek() {
|
||||
Some(next) if next.parent_module == attr.parent_module => {
|
||||
combined_docs.push('\n');
|
||||
combined_docs.push_str(&attrs.next().unwrap().doc);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
debug!("combined_docs={}", combined_docs);
|
||||
|
||||
let (krate, parent_node) = if let Some(id) = attr.parent_module {
|
||||
trace!("docs {:?} came from {:?}", attr.doc, id);
|
||||
(id.krate, Some(id))
|
||||
} else {
|
||||
trace!("no parent found for {:?}", attr.doc);
|
||||
(item.def_id.krate, parent_node)
|
||||
};
|
||||
// NOTE: if there are links that start in one crate and end in another, this will not resolve them.
|
||||
// This is a degenerate case and it's not supported by rustdoc.
|
||||
// FIXME: this will break links that start in `#[doc = ...]` and end as a sugared doc. Should this be supported?
|
||||
for (ori_link, link_range) in markdown_links(&combined_docs) {
|
||||
let link = self.resolve_link(
|
||||
&item,
|
||||
&combined_docs,
|
||||
¤t_item,
|
||||
parent_node,
|
||||
&parent_name,
|
||||
krate,
|
||||
ori_link,
|
||||
link_range,
|
||||
);
|
||||
if let Some(link) = link {
|
||||
item.attrs.links.push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if item.is_mod() && !item.attrs.inner_docs {
|
||||
|
@ -838,24 +872,25 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
|||
impl LinkCollector<'_, '_> {
|
||||
fn resolve_link(
|
||||
&self,
|
||||
item: &mut Item,
|
||||
item: &Item,
|
||||
dox: &str,
|
||||
current_item: &Option<String>,
|
||||
parent_node: Option<DefId>,
|
||||
parent_name: &Option<String>,
|
||||
krate: CrateNum,
|
||||
ori_link: String,
|
||||
link_range: Option<Range<usize>>,
|
||||
) {
|
||||
) -> Option<ItemLink> {
|
||||
trace!("considering link '{}'", ori_link);
|
||||
|
||||
// Bail early for real links.
|
||||
if ori_link.contains('/') {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
// [] is mostly likely not supposed to be a link
|
||||
if ori_link.is_empty() {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let cx = self.cx;
|
||||
|
@ -863,11 +898,11 @@ impl LinkCollector<'_, '_> {
|
|||
let parts = link.split('#').collect::<Vec<_>>();
|
||||
let (link, extra_fragment) = if parts.len() > 2 {
|
||||
anchor_failure(cx, &item, &link, dox, link_range, AnchorFailure::MultipleAnchors);
|
||||
return;
|
||||
return None;
|
||||
} else if parts.len() == 2 {
|
||||
if parts[0].trim().is_empty() {
|
||||
// This is an anchor to an element of the current page, nothing to do in here!
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
(parts[0], Some(parts[1].to_owned()))
|
||||
} else {
|
||||
|
@ -888,7 +923,7 @@ impl LinkCollector<'_, '_> {
|
|||
.trim();
|
||||
|
||||
if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
// We stripped `()` and `!` when parsing the disambiguator.
|
||||
|
@ -928,7 +963,7 @@ impl LinkCollector<'_, '_> {
|
|||
link_range,
|
||||
smallvec![err_kind],
|
||||
);
|
||||
return;
|
||||
return None;
|
||||
};
|
||||
|
||||
// replace `Self` with suitable item's parent name
|
||||
|
@ -947,7 +982,7 @@ impl LinkCollector<'_, '_> {
|
|||
// (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root.
|
||||
resolved_self = format!("self::{}", &path_str["crate::".len()..]);
|
||||
path_str = &resolved_self;
|
||||
module_id = DefId { krate: item.def_id.krate, index: CRATE_DEF_INDEX };
|
||||
module_id = DefId { krate, index: CRATE_DEF_INDEX };
|
||||
}
|
||||
|
||||
match self.resolve_with_disambiguator(
|
||||
|
@ -962,7 +997,7 @@ impl LinkCollector<'_, '_> {
|
|||
link_range.clone(),
|
||||
) {
|
||||
Some(x) => x,
|
||||
None => return,
|
||||
None => return None,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -986,7 +1021,7 @@ impl LinkCollector<'_, '_> {
|
|||
link_range,
|
||||
AnchorFailure::RustdocAnchorConflict(prim),
|
||||
);
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
res = prim;
|
||||
fragment = Some(path.to_owned());
|
||||
|
@ -994,7 +1029,7 @@ impl LinkCollector<'_, '_> {
|
|||
// `[char]` when a `char` module is in scope
|
||||
let candidates = vec![res, prim];
|
||||
ambiguity_error(cx, &item, path_str, dox, link_range, candidates);
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1018,16 +1053,11 @@ impl LinkCollector<'_, '_> {
|
|||
if let Res::PrimTy(..) = res {
|
||||
match disambiguator {
|
||||
Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
|
||||
item.attrs.links.push(ItemLink {
|
||||
link: ori_link,
|
||||
link_text,
|
||||
did: None,
|
||||
fragment,
|
||||
});
|
||||
Some(ItemLink { link: ori_link, link_text, did: None, fragment })
|
||||
}
|
||||
Some(other) => {
|
||||
report_mismatch(other, Disambiguator::Primitive);
|
||||
return;
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1050,7 +1080,7 @@ impl LinkCollector<'_, '_> {
|
|||
(actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
|
||||
(_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => {
|
||||
report_mismatch(specified, Disambiguator::Kind(kind));
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1073,14 +1103,14 @@ impl LinkCollector<'_, '_> {
|
|||
}
|
||||
}
|
||||
let id = register_res(cx, res);
|
||||
item.attrs.links.push(ItemLink { link: ori_link, link_text, did: Some(id), fragment });
|
||||
Some(ItemLink { link: ori_link, link_text, did: Some(id), fragment })
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_with_disambiguator(
|
||||
&self,
|
||||
disambiguator: Option<Disambiguator>,
|
||||
item: &mut Item,
|
||||
item: &Item,
|
||||
dox: &str,
|
||||
path_str: &str,
|
||||
current_item: &Option<String>,
|
||||
|
|
|
@ -30,7 +30,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
|
|||
for &cnum in cx.tcx.crates().iter() {
|
||||
for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() {
|
||||
cx.tcx.sess.time("build_extern_trait_impl", || {
|
||||
inline::build_impl(cx, did, None, &mut new_items);
|
||||
inline::build_impl(cx, None, did, None, &mut new_items);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
|
|||
// Also try to inline primitive impls from other crates.
|
||||
for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() {
|
||||
if !def_id.is_local() {
|
||||
inline::build_impl(cx, def_id, None, &mut new_items);
|
||||
inline::build_impl(cx, None, def_id, None, &mut new_items);
|
||||
|
||||
// FIXME(eddyb) is this `doc(hidden)` check needed?
|
||||
if !cx.tcx.get_attrs(def_id).lists(sym::doc).has_word(sym::hidden) {
|
||||
|
@ -90,7 +90,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
|
|||
for &impl_node in cx.tcx.hir().trait_impls(trait_did) {
|
||||
let impl_did = cx.tcx.hir().local_def_id(impl_node);
|
||||
cx.tcx.sess.time("build_local_trait_impl", || {
|
||||
inline::build_impl(cx, impl_did.to_def_id(), None, &mut new_items);
|
||||
inline::build_impl(cx, None, impl_did.to_def_id(), None, &mut new_items);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -399,6 +399,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
|
|||
om.extern_crates.push(ExternCrate {
|
||||
cnum: self.cx.tcx.extern_mod_stmt_cnum(def_id).unwrap_or(LOCAL_CRATE),
|
||||
name: ident.name,
|
||||
hir_id: item.hir_id,
|
||||
path: orig_name.map(|x| x.to_string()),
|
||||
vis: &item.vis,
|
||||
attrs: &item.attrs,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#![crate_name = "foo"]
|
||||
|
||||
// @has foo/struct.JoinPathsError.html '//a[@href="../foo/fn.with_code.html"]' 'crate::with_code'
|
||||
/// [crate::with_code]
|
||||
// @has - '//a[@href="../foo/fn.with_code.html"]' 'different text'
|
||||
/// [different text][with_code]
|
||||
// @has - '//a[@href="../foo/fn.me_too.html"]' 'me_too'
|
||||
#[doc = "[me_too]"]
|
||||
// @has - '//a[@href="../foo/fn.me_three.html"]' 'reference link'
|
||||
/// This [reference link]
|
||||
#[doc = "has an attr in the way"]
|
||||
///
|
||||
/// [reference link]: me_three
|
||||
pub use std::env::JoinPathsError;
|
||||
|
||||
pub fn with_code() {}
|
||||
pub fn me_too() {}
|
||||
pub fn me_three() {}
|
Loading…
Reference in New Issue