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

Resolve intra-doc links on additional documentation for re-exports in lexical scope #77519

Merged
merged 3 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 40 additions & 21 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<'_>>,
Expand All @@ -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) => {
Expand All @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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>,
Expand All @@ -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);
Expand Down Expand Up @@ -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)
}
}
Expand Down
24 changes: 17 additions & 7 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
}
Expand Down
127 changes: 85 additions & 42 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/clean/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading