summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/passes
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/passes')
-rw-r--r--src/librustdoc/passes/bare_urls.rs6
-rw-r--r--src/librustdoc/passes/check_code_block_syntax.rs95
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs14
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs28
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs44
-rw-r--r--src/librustdoc/passes/html_tags.rs72
-rw-r--r--src/librustdoc/passes/propagate_doc_cfg.rs2
-rw-r--r--src/librustdoc/passes/strip_private.rs2
-rw-r--r--src/librustdoc/passes/stripper.rs44
9 files changed, 187 insertions, 120 deletions
diff --git a/src/librustdoc/passes/bare_urls.rs b/src/librustdoc/passes/bare_urls.rs
index 392e26ea6..7ff3ccef9 100644
--- a/src/librustdoc/passes/bare_urls.rs
+++ b/src/librustdoc/passes/bare_urls.rs
@@ -71,16 +71,14 @@ impl<'a, 'tcx> DocVisitor for BareUrlsLinter<'a, 'tcx> {
let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
let sp = super::source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
.unwrap_or_else(|| item.attr_span(cx.tcx));
- cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, |lint| {
- lint.build(msg)
- .note("bare URLs are not automatically turned into clickable links")
+ cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| {
+ lint.note("bare URLs are not automatically turned into clickable links")
.span_suggestion(
sp,
"use an automatic link instead",
format!("<{}>", url),
Applicability::MachineApplicable,
)
- .emit();
});
};
diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs
index 381ac7a5d..2e651b538 100644
--- a/src/librustdoc/passes/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/check_code_block_syntax.rs
@@ -1,8 +1,9 @@
//! Validates syntax inside Rust code blocks (\`\`\`rust).
use rustc_data_structures::sync::{Lock, Lrc};
use rustc_errors::{
- emitter::Emitter, translation::Translate, Applicability, Diagnostic, Handler,
- LazyFallbackBundle, LintDiagnosticBuilder,
+ emitter::Emitter,
+ translation::{to_fluent_args, Translate},
+ Applicability, Diagnostic, Handler, LazyFallbackBundle,
};
use rustc_parse::parse_stream_from_source_str;
use rustc_session::parse::ParseSess;
@@ -97,48 +98,10 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
None => (item.attr_span(self.cx.tcx), false),
};
- // lambda that will use the lint to start a new diagnostic and add
- // a suggestion to it when needed.
- let diag_builder = |lint: LintDiagnosticBuilder<'_, ()>| {
- let explanation = if is_ignore {
- "`ignore` code blocks require valid Rust code for syntax highlighting; \
- mark blocks that do not contain Rust code as text"
- } else {
- "mark blocks that do not contain Rust code as text"
- };
- let msg = if buffer.has_errors {
- "could not parse code block as Rust code"
- } else {
- "Rust code block is empty"
- };
- let mut diag = lint.build(msg);
-
- if precise_span {
- if is_ignore {
- // giving an accurate suggestion is hard because `ignore` might not have come first in the list.
- // just give a `help` instead.
- diag.span_help(
- sp.from_inner(InnerSpan::new(0, 3)),
- &format!("{}: ```text", explanation),
- );
- } else if empty_block {
- diag.span_suggestion(
- sp.from_inner(InnerSpan::new(0, 3)).shrink_to_hi(),
- explanation,
- "text",
- Applicability::MachineApplicable,
- );
- }
- } else if empty_block || is_ignore {
- diag.help(&format!("{}: ```text", explanation));
- }
-
- // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
- for message in buffer.messages.iter() {
- diag.note(message);
- }
-
- diag.emit();
+ let msg = if buffer.has_errors {
+ "could not parse code block as Rust code"
+ } else {
+ "Rust code block is empty"
};
// Finally build and emit the completed diagnostic.
@@ -148,7 +111,42 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
crate::lint::INVALID_RUST_CODEBLOCKS,
hir_id,
sp,
- diag_builder,
+ msg,
+ |lint| {
+ let explanation = if is_ignore {
+ "`ignore` code blocks require valid Rust code for syntax highlighting; \
+ mark blocks that do not contain Rust code as text"
+ } else {
+ "mark blocks that do not contain Rust code as text"
+ };
+
+ if precise_span {
+ if is_ignore {
+ // giving an accurate suggestion is hard because `ignore` might not have come first in the list.
+ // just give a `help` instead.
+ lint.span_help(
+ sp.from_inner(InnerSpan::new(0, 3)),
+ &format!("{}: ```text", explanation),
+ );
+ } else if empty_block {
+ lint.span_suggestion(
+ sp.from_inner(InnerSpan::new(0, 3)).shrink_to_hi(),
+ explanation,
+ "text",
+ Applicability::MachineApplicable,
+ );
+ }
+ } else if empty_block || is_ignore {
+ lint.help(&format!("{}: ```text", explanation));
+ }
+
+ // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
+ for message in buffer.messages.iter() {
+ lint.note(message);
+ }
+
+ lint
+ },
);
}
}
@@ -195,8 +193,11 @@ impl Translate for BufferEmitter {
impl Emitter for BufferEmitter {
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
let mut buffer = self.buffer.borrow_mut();
- // FIXME(davidtwco): need to support translation here eventually
- buffer.messages.push(format!("error from rustc: {}", diag.message[0].0.expect_str()));
+
+ let fluent_args = to_fluent_args(diag.args());
+ let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args);
+
+ buffer.messages.push(format!("error from rustc: {}", translated_main_message));
if diag.is_error() {
buffer.has_errors = true;
}
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index 55d5f303d..7740c6d5b 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -56,7 +56,7 @@ impl crate::doctest::Tester for Tests {
}
pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool {
- if !cx.cache.access_levels.is_public(item.item_id.expect_def_id())
+ if !cx.cache.effective_visibilities.is_directly_public(item.item_id.expect_def_id())
|| matches!(
*item.kind,
clean::StructFieldItem(_)
@@ -125,21 +125,19 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item
crate::lint::MISSING_DOC_CODE_EXAMPLES,
hir_id,
sp,
- |lint| {
- lint.build("missing code example in this documentation").emit();
- },
+ "missing code example in this documentation",
+ |lint| lint,
);
}
} else if tests.found_tests > 0
- && !cx.cache.access_levels.is_exported(item.item_id.expect_def_id())
+ && !cx.cache.effective_visibilities.is_exported(item.item_id.expect_def_id())
{
cx.tcx.struct_span_lint_hir(
crate::lint::PRIVATE_DOC_TESTS,
hir_id,
item.attr_span(cx.tcx),
- |lint| {
- lint.build("documentation test in private item").emit();
- },
+ "documentation test in private item",
+ |lint| lint,
);
}
}
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index b2a41bfa4..8aa0abd36 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -80,10 +80,10 @@ impl Res {
}
}
- fn def_id(self, tcx: TyCtxt<'_>) -> DefId {
+ fn def_id(self, tcx: TyCtxt<'_>) -> Option<DefId> {
match self {
- Res::Def(_, id) => id,
- Res::Primitive(prim) => *PrimitiveType::primitive_locations(tcx).get(&prim).unwrap(),
+ Res::Def(_, id) => Some(id),
+ Res::Primitive(prim) => PrimitiveType::primitive_locations(tcx).get(&prim).copied(),
}
}
@@ -1127,10 +1127,10 @@ impl LinkCollector<'_, '_> {
}
}
- Some(ItemLink {
+ res.def_id(self.cx.tcx).map(|page_id| ItemLink {
link: ori_link.link.clone(),
link_text: link_text.clone(),
- page_id: res.def_id(self.cx.tcx),
+ page_id,
fragment,
})
}
@@ -1202,8 +1202,8 @@ impl LinkCollector<'_, '_> {
item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
})
{
- if self.cx.tcx.privacy_access_levels(()).is_exported(src_id)
- && !self.cx.tcx.privacy_access_levels(()).is_exported(dst_id)
+ if self.cx.tcx.effective_visibilities(()).is_exported(src_id)
+ && !self.cx.tcx.effective_visibilities(()).is_exported(dst_id)
{
privacy_error(self.cx, diag_info, path_str);
}
@@ -1609,9 +1609,7 @@ fn report_diagnostic(
let sp = item.attr_span(tcx);
- tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
- let mut diag = lint.build(msg);
-
+ tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |lint| {
let span =
super::source_span_for_markdown_range(tcx, dox, link_range, &item.attrs).map(|sp| {
if dox.as_bytes().get(link_range.start) == Some(&b'`')
@@ -1624,7 +1622,7 @@ fn report_diagnostic(
});
if let Some(sp) = span {
- diag.set_span(sp);
+ lint.set_span(sp);
} else {
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
// ^ ~~~~
@@ -1634,7 +1632,7 @@ fn report_diagnostic(
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
// Print the line containing the `link_range` and manually mark it with '^'s.
- diag.note(&format!(
+ lint.note(&format!(
"the link appears in this line:\n\n{line}\n\
{indicator: <before$}{indicator:^<found$}",
line = line,
@@ -1644,9 +1642,9 @@ fn report_diagnostic(
));
}
- decorate(&mut diag, span);
+ decorate(lint, span);
- diag.emit();
+ lint
});
}
@@ -1895,7 +1893,7 @@ fn disambiguator_error(
diag_info.link_range = disambiguator_range;
report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, &diag_info, |diag, _sp| {
let msg = format!(
- "see {}/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators",
+ "see {}/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators",
crate::DOC_RUST_LANG_ORG_CHANNEL
);
diag.note(&msg);
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
index 38cfd7a27..1b373cfe5 100644
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs
@@ -37,7 +37,6 @@ pub(crate) fn early_resolve_intra_doc_links(
markdown_links: Default::default(),
doc_link_resolutions: Default::default(),
traits_in_scope: Default::default(),
- all_traits: Default::default(),
all_trait_impls: Default::default(),
all_macro_rules: Default::default(),
document_private_items,
@@ -48,7 +47,6 @@ pub(crate) fn early_resolve_intra_doc_links(
link_resolver.resolve_doc_links_local(&krate.attrs);
link_resolver.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
visit::walk_crate(&mut link_resolver, krate);
- link_resolver.process_extern_impls();
// FIXME: somehow rustdoc is still missing crates even though we loaded all
// the known necessary crates. Load them all unconditionally until we find a way to fix this.
@@ -58,11 +56,12 @@ pub(crate) fn early_resolve_intra_doc_links(
link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
}
+ link_resolver.process_extern_impls();
+
ResolverCaches {
markdown_links: Some(link_resolver.markdown_links),
doc_link_resolutions: link_resolver.doc_link_resolutions,
traits_in_scope: link_resolver.traits_in_scope,
- all_traits: Some(link_resolver.all_traits),
all_trait_impls: Some(link_resolver.all_trait_impls),
all_macro_rules: link_resolver.all_macro_rules,
}
@@ -80,7 +79,6 @@ struct EarlyDocLinkResolver<'r, 'ra> {
markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
- all_traits: Vec<DefId>,
all_trait_impls: Vec<DefId>,
all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
document_private_items: bool,
@@ -121,8 +119,6 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
loop {
let crates = Vec::from_iter(self.resolver.cstore().crates_untracked());
for &cnum in &crates[start_cnum..] {
- let all_traits =
- Vec::from_iter(self.resolver.cstore().traits_in_crate_untracked(cnum));
let all_trait_impls =
Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum));
let all_inherent_impls =
@@ -131,20 +127,18 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
self.resolver.cstore().incoherent_impls_in_crate_untracked(cnum),
);
- // Querying traits in scope is expensive so we try to prune the impl and traits lists
- // using privacy, private traits and impls from other crates are never documented in
+ // Querying traits in scope is expensive so we try to prune the impl lists using
+ // privacy, private traits and impls from other crates are never documented in
// the current crate, and links in their doc comments are not resolved.
- for &def_id in &all_traits {
- if self.resolver.cstore().visibility_untracked(def_id).is_public() {
- self.resolve_doc_links_extern_impl(def_id, false);
- }
- }
for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
if self.resolver.cstore().visibility_untracked(trait_def_id).is_public()
&& simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| {
self.resolver.cstore().visibility_untracked(ty_def_id).is_public()
})
{
+ if self.visited_mods.insert(trait_def_id) {
+ self.resolve_doc_links_extern_impl(trait_def_id, false);
+ }
self.resolve_doc_links_extern_impl(impl_def_id, false);
}
}
@@ -157,7 +151,6 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
self.resolve_doc_links_extern_impl(impl_def_id, true);
}
- self.all_traits.extend(all_traits);
self.all_trait_impls
.extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id));
}
@@ -306,15 +299,20 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
{
if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() {
let scope_id = match child.res {
- Res::Def(DefKind::Variant, ..) => self.resolver.parent(def_id),
+ Res::Def(
+ DefKind::Variant
+ | DefKind::AssocTy
+ | DefKind::AssocFn
+ | DefKind::AssocConst,
+ ..,
+ ) => self.resolver.parent(def_id),
_ => def_id,
};
self.resolve_doc_links_extern_outer(def_id, scope_id); // Outer attribute scope
if let Res::Def(DefKind::Mod, ..) = child.res {
self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope
}
- // `DefKind::Trait`s are processed in `process_extern_impls`.
- if let Res::Def(DefKind::Mod | DefKind::Enum, ..) = child.res {
+ if let Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, ..) = child.res {
self.process_module_children_or_reexports(def_id);
}
if let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Variant, _) =
@@ -356,10 +354,14 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
self.parent_scope.module = old_module;
} else {
match &item.kind {
- ItemKind::Trait(..) => {
- self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
- }
- ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
+ ItemKind::Impl(box ast::Impl { of_trait: Some(trait_ref), .. }) => {
+ if let Some(partial_res) = self.resolver.get_partial_res(trait_ref.ref_id)
+ && let Some(res) = partial_res.full_res()
+ && let Some(trait_def_id) = res.opt_def_id()
+ && !trait_def_id.is_local()
+ && self.visited_mods.insert(trait_def_id) {
+ self.resolve_doc_links_extern_impl(trait_def_id, false);
+ }
self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
}
ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs
index 885dadb32..a89ed7c7e 100644
--- a/src/librustdoc/passes/html_tags.rs
+++ b/src/librustdoc/passes/html_tags.rs
@@ -22,10 +22,8 @@ struct InvalidHtmlTagsLinter<'a, 'tcx> {
}
pub(crate) fn check_invalid_html_tags(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
- if cx.tcx.sess.is_nightly_build() {
- let mut coll = InvalidHtmlTagsLinter { cx };
- coll.visit_crate(&krate);
- }
+ let mut coll = InvalidHtmlTagsLinter { cx };
+ coll.visit_crate(&krate);
krate
}
@@ -186,7 +184,60 @@ fn extract_html_tag(
}
drop_tag(tags, tag_name, r, f);
} else {
- tags.push((tag_name, r));
+ let mut is_self_closing = false;
+ let mut quote_pos = None;
+ if c != '>' {
+ let mut quote = None;
+ let mut after_eq = false;
+ for (i, c) in text[pos..].char_indices() {
+ if !c.is_whitespace() {
+ if let Some(q) = quote {
+ if c == q {
+ quote = None;
+ quote_pos = None;
+ after_eq = false;
+ }
+ } else if c == '>' {
+ break;
+ } else if c == '/' && !after_eq {
+ is_self_closing = true;
+ } else {
+ if is_self_closing {
+ is_self_closing = false;
+ }
+ if (c == '"' || c == '\'') && after_eq {
+ quote = Some(c);
+ quote_pos = Some(pos + i);
+ } else if c == '=' {
+ after_eq = true;
+ }
+ }
+ } else if quote.is_none() {
+ after_eq = false;
+ }
+ }
+ }
+ if let Some(quote_pos) = quote_pos {
+ let qr = Range { start: quote_pos, end: quote_pos };
+ f(
+ &format!("unclosed quoted HTML attribute on tag `{}`", tag_name),
+ &qr,
+ false,
+ );
+ }
+ if is_self_closing {
+ // https://html.spec.whatwg.org/#parse-error-non-void-html-element-start-tag-with-trailing-solidus
+ let valid = ALLOWED_UNCLOSED.contains(&&tag_name[..])
+ || tags.iter().take(pos + 1).any(|(at, _)| {
+ let at = at.to_lowercase();
+ at == "svg" || at == "math"
+ });
+ if !valid {
+ f(&format!("invalid self-closing HTML tag `{}`", tag_name), &r, false);
+ }
+ } else {
+ tags.push((tag_name, r));
+ }
}
}
break;
@@ -240,9 +291,8 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> {
Some(sp) => sp,
None => item.attr_span(tcx),
};
- tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, |lint| {
+ tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, msg, |lint| {
use rustc_lint_defs::Applicability;
- let mut diag = lint.build(msg);
// If a tag looks like `<this>`, it might actually be a generic.
// We don't try to detect stuff `<like, this>` because that's not valid HTML,
// and we don't try to detect stuff `<like this>` because that's not valid Rust.
@@ -305,11 +355,10 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> {
if (generics_start > 0 && dox.as_bytes()[generics_start - 1] == b'<')
|| (generics_end < dox.len() && dox.as_bytes()[generics_end] == b'>')
{
- diag.emit();
- return;
+ return lint;
}
// multipart form is chosen here because ``Vec<i32>`` would be confusing.
- diag.multipart_suggestion(
+ lint.multipart_suggestion(
"try marking as source code",
vec![
(generics_sp.shrink_to_lo(), String::from("`")),
@@ -318,7 +367,8 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> {
Applicability::MaybeIncorrect,
);
}
- diag.emit()
+
+ lint
});
};
diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs
index 765f7c61b..de3a4b339 100644
--- a/src/librustdoc/passes/propagate_doc_cfg.rs
+++ b/src/librustdoc/passes/propagate_doc_cfg.rs
@@ -48,7 +48,7 @@ impl<'a, 'tcx> CfgPropagator<'a, 'tcx> {
let expected_parent = hir.get_parent_item(hir_id);
// If parents are different, it means that `item` is a reexport and we need
// to compute the actual `cfg` by iterating through its "real" parents.
- if self.parent == Some(expected_parent) {
+ if self.parent == Some(expected_parent.def_id) {
return;
}
}
diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs
index f3aa3c7ce..450f69e15 100644
--- a/src/librustdoc/passes/strip_private.rs
+++ b/src/librustdoc/passes/strip_private.rs
@@ -23,7 +23,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) ->
{
let mut stripper = Stripper {
retained: &mut retained,
- access_levels: &cx.cache.access_levels,
+ effective_visibilities: &cx.cache.effective_visibilities,
update_retained: true,
is_json_output,
};
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index a9d768f01..0089ce63d 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -1,15 +1,17 @@
//! A collection of utility functions for the `strip_*` passes.
use rustc_hir::def_id::DefId;
-use rustc_middle::middle::privacy::AccessLevels;
+use rustc_middle::middle::privacy::EffectiveVisibilities;
+use rustc_span::symbol::sym;
+
use std::mem;
-use crate::clean::{self, Item, ItemId, ItemIdSet};
+use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt};
use crate::fold::{strip_item, DocFolder};
use crate::formats::cache::Cache;
pub(crate) struct Stripper<'a> {
pub(crate) retained: &'a mut ItemIdSet,
- pub(crate) access_levels: &'a AccessLevels<DefId>,
+ pub(crate) effective_visibilities: &'a EffectiveVisibilities<DefId>,
pub(crate) update_retained: bool,
pub(crate) is_json_output: bool,
}
@@ -20,13 +22,13 @@ pub(crate) struct Stripper<'a> {
#[inline]
fn is_item_reachable(
is_json_output: bool,
- access_levels: &AccessLevels<DefId>,
+ effective_visibilities: &EffectiveVisibilities<DefId>,
item_id: ItemId,
) -> bool {
if is_json_output {
- access_levels.is_reachable(item_id.expect_def_id())
+ effective_visibilities.is_reachable(item_id.expect_def_id())
} else {
- access_levels.is_exported(item_id.expect_def_id())
+ effective_visibilities.is_exported(item_id.expect_def_id())
}
}
@@ -64,7 +66,7 @@ impl<'a> DocFolder for Stripper<'a> {
| clean::ForeignTypeItem => {
let item_id = i.item_id;
if item_id.is_local()
- && !is_item_reachable(self.is_json_output, self.access_levels, item_id)
+ && !is_item_reachable(self.is_json_output, self.effective_visibilities, item_id)
{
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
return None;
@@ -151,6 +153,22 @@ pub(crate) struct ImplStripper<'a> {
pub(crate) document_private: bool,
}
+impl<'a> ImplStripper<'a> {
+ #[inline]
+ fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool {
+ if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) {
+ true
+ } else if self.is_json_output {
+ // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we
+ // need to keep it.
+ self.cache.effective_visibilities.is_exported(for_def_id)
+ && !item.attrs.lists(sym::doc).has_word(sym::hidden)
+ } else {
+ false
+ }
+ }
+}
+
impl<'a> DocFolder for ImplStripper<'a> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
if let clean::ImplItem(ref imp) = *i.kind {
@@ -168,7 +186,7 @@ impl<'a> DocFolder for ImplStripper<'a> {
item_id.is_local()
&& !is_item_reachable(
self.is_json_output,
- &self.cache.access_levels,
+ &self.cache.effective_visibilities,
item_id,
)
})
@@ -178,15 +196,17 @@ impl<'a> DocFolder for ImplStripper<'a> {
return None;
}
}
+ // Because we don't inline in `maybe_inline_local` if the output format is JSON,
+ // we need to make a special check for JSON output: we want to keep it unless it has
+ // a `#[doc(hidden)]` attribute if the `for_` type is exported.
if let Some(did) = imp.for_.def_id(self.cache) {
- if did.is_local() && !imp.for_.is_assoc_ty() && !self.retained.contains(&did.into())
- {
+ if !imp.for_.is_assoc_ty() && !self.should_keep_impl(&i, did) {
debug!("ImplStripper: impl item for stripped type; removing");
return None;
}
}
if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) {
- if did.is_local() && !self.retained.contains(&did.into()) {
+ if !self.should_keep_impl(&i, did) {
debug!("ImplStripper: impl item for stripped trait; removing");
return None;
}
@@ -194,7 +214,7 @@ impl<'a> DocFolder for ImplStripper<'a> {
if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
for typaram in generics {
if let Some(did) = typaram.def_id(self.cache) {
- if did.is_local() && !self.retained.contains(&did.into()) {
+ if !self.should_keep_impl(&i, did) {
debug!(
"ImplStripper: stripped item in trait's generics; removing impl"
);