summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/passes
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:50 +0000
commit2e00214b3efbdfeefaa0fe9e8b8fd519de7adc35 (patch)
treed325add32978dbdc1db975a438b3a77d571b1ab8 /src/librustdoc/passes
parentReleasing progress-linux version 1.68.2+dfsg1-1~progress7.99u1. (diff)
downloadrustc-2e00214b3efbdfeefaa0fe9e8b8fd519de7adc35.tar.xz
rustc-2e00214b3efbdfeefaa0fe9e8b8fd519de7adc35.zip
Merging upstream version 1.69.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/librustdoc/passes')
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs8
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs38
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs313
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs407
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs91
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs11
-rw-r--r--src/librustdoc/passes/lint/html_tags.rs10
-rw-r--r--src/librustdoc/passes/mod.rs3
-rw-r--r--src/librustdoc/passes/propagate_doc_cfg.rs17
-rw-r--r--src/librustdoc/passes/strip_hidden.rs117
-rw-r--r--src/librustdoc/passes/stripper.rs32
11 files changed, 281 insertions, 766 deletions
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 02b227896..0b22f943d 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -216,13 +216,7 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
);
let has_doc_example = tests.found_tests != 0;
- // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
- // would presumably panic if a fake `DefIndex` were passed.
- let hir_id = self
- .ctx
- .tcx
- .hir()
- .local_def_id_to_hir_id(i.item_id.expect_def_id().expect_local());
+ let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap();
let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
// In case we have:
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index 6aa2dda98..a39d57d42 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -14,8 +14,8 @@ use crate::visit::DocVisitor;
use crate::visit_ast::inherits_doc_hidden;
use rustc_hir as hir;
use rustc_middle::lint::LintLevelSource;
+use rustc_middle::ty::DefIdTree;
use rustc_session::lint;
-use rustc_span::symbol::sym;
pub(crate) const CHECK_DOC_TEST_VISIBILITY: Pass = Pass {
name: "check_doc_test_visibility",
@@ -79,30 +79,32 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -
// The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
// would presumably panic if a fake `DefIndex` were passed.
- let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.item_id.expect_def_id().expect_local());
+ let def_id = item.item_id.expect_def_id().expect_local();
// check if parent is trait impl
- if let Some(parent_hir_id) = cx.tcx.hir().opt_parent_id(hir_id) {
- if let Some(parent_node) = cx.tcx.hir().find(parent_hir_id) {
- if matches!(
- parent_node,
- hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }),
- ..
- })
- ) {
- return false;
- }
- }
+ if let Some(parent_def_id) = cx.tcx.opt_local_parent(def_id) &&
+ let Some(parent_node) = cx.tcx.hir().find_by_def_id(parent_def_id) &&
+ matches!(
+ parent_node,
+ hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }),
+ ..
+ })
+ )
+ {
+ return false;
}
- if cx.tcx.hir().attrs(hir_id).lists(sym::doc).has_word(sym::hidden)
- || inherits_doc_hidden(cx.tcx, hir_id)
- || cx.tcx.hir().span(hir_id).in_derive_expansion()
+ if cx.tcx.is_doc_hidden(def_id.to_def_id())
+ || inherits_doc_hidden(cx.tcx, def_id)
+ || cx.tcx.def_span(def_id.to_def_id()).in_derive_expansion()
{
return false;
}
- let (level, source) = cx.tcx.lint_level_at_node(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id);
+ let (level, source) = cx.tcx.lint_level_at_node(
+ crate::lint::MISSING_DOC_CODE_EXAMPLES,
+ cx.tcx.hir().local_def_id_to_hir_id(def_id),
+ );
level != lint::Level::Allow || matches!(source, LintLevelSource::Default)
}
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 075951312..cbfc58138 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -15,7 +15,8 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_hir::Mutability;
use rustc_middle::ty::{DefIdTree, Ty, TyCtxt};
use rustc_middle::{bug, ty};
-use rustc_resolve::ParentScope;
+use rustc_resolve::rustdoc::MalformedGenerics;
+use rustc_resolve::rustdoc::{prepare_to_doc_link_resolution, strip_generics_from_path};
use rustc_session::lint::Lint;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{sym, Ident, Symbol};
@@ -34,9 +35,6 @@ use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
use crate::passes::Pass;
use crate::visit::DocVisitor;
-mod early;
-pub(crate) use early::early_resolve_intra_doc_links;
-
pub(crate) const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
name: "collect-intra-doc-links",
run: collect_intra_doc_links,
@@ -179,47 +177,6 @@ enum ResolutionFailure<'a> {
NotResolved(UnresolvedPath<'a>),
}
-#[derive(Clone, Copy, Debug)]
-enum MalformedGenerics {
- /// This link has unbalanced angle brackets.
- ///
- /// For example, `Vec<T` should trigger this, as should `Vec<T>>`.
- UnbalancedAngleBrackets,
- /// The generics are not attached to a type.
- ///
- /// For example, `<T>` should trigger this.
- ///
- /// This is detected by checking if the path is empty after the generics are stripped.
- MissingType,
- /// The link uses fully-qualified syntax, which is currently unsupported.
- ///
- /// For example, `<Vec as IntoIterator>::into_iter` should trigger this.
- ///
- /// This is detected by checking if ` as ` (the keyword `as` with spaces around it) is inside
- /// angle brackets.
- HasFullyQualifiedSyntax,
- /// The link has an invalid path separator.
- ///
- /// For example, `Vec:<T>:new()` should trigger this. Note that `Vec:new()` will **not**
- /// trigger this because it has no generics and thus [`strip_generics_from_path`] will not be
- /// called.
- ///
- /// Note that this will also **not** be triggered if the invalid path separator is inside angle
- /// brackets because rustdoc mostly ignores what's inside angle brackets (except for
- /// [`HasFullyQualifiedSyntax`](MalformedGenerics::HasFullyQualifiedSyntax)).
- ///
- /// This is detected by checking if there is a colon followed by a non-colon in the link.
- InvalidPathSeparator,
- /// The link has too many angle brackets.
- ///
- /// For example, `Vec<<T>>` should trigger this.
- TooManyAngleBrackets,
- /// The link has empty angle brackets.
- ///
- /// For example, `Vec<>` should trigger this.
- EmptyAngleBrackets,
-}
-
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub(crate) enum UrlFragment {
Item(DefId),
@@ -271,7 +228,7 @@ struct ResolutionInfo {
item_id: ItemId,
module_id: DefId,
dis: Option<Disambiguator>,
- path_str: String,
+ path_str: Box<str>,
extra_fragment: Option<String>,
}
@@ -336,9 +293,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
let ty_res = self.resolve_path(&path, TypeNS, item_id, module_id).ok_or_else(no_res)?;
match ty_res {
- Res::Def(DefKind::Enum, did) => match tcx.type_of(did).kind() {
+ Res::Def(DefKind::Enum, did) => match tcx.type_of(did).subst_identity().kind() {
ty::Adt(def, _) if def.is_enum() => {
- if let Some(field) = def.all_fields().find(|f| f.name == variant_field_name) {
+ if let Some(variant) = def.variants().iter().find(|v| v.name == variant_name)
+ && let Some(field) = variant.fields.iter().find(|f| f.name == variant_field_name) {
Ok((ty_res, field.did))
} else {
Err(UnresolvedPath {
@@ -401,16 +359,16 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
_ => def_id,
})
.and_then(|self_id| match tcx.def_kind(self_id) {
- DefKind::Impl => self.def_id_to_res(self_id),
+ DefKind::Impl { .. } => self.def_id_to_res(self_id),
DefKind::Use => None,
def_kind => Some(Res::Def(def_kind, self_id)),
})
}
- /// Convenience wrapper around `resolve_rustdoc_path`.
+ /// Convenience wrapper around `doc_link_resolutions`.
///
/// This also handles resolving `true` and `false` as booleans.
- /// NOTE: `resolve_rustdoc_path` knows only about paths, not about types.
+ /// NOTE: `doc_link_resolutions` knows only about paths, not about types.
/// Associated items will never be resolved by this function.
fn resolve_path(
&self,
@@ -426,17 +384,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// Resolver doesn't know about true, false, and types that aren't paths (e.g. `()`).
let result = self
.cx
- .resolver_caches
- .doc_link_resolutions
- .get(&(Symbol::intern(path_str), ns, module_id))
+ .tcx
+ .doc_link_resolutions(module_id)
+ .get(&(Symbol::intern(path_str), ns))
.copied()
- .unwrap_or_else(|| {
- self.cx.enter_resolver(|resolver| {
- let parent_scope =
- ParentScope::module(resolver.expect_module(module_id), resolver);
- resolver.resolve_rustdoc_path(path_str, ns, parent_scope)
- })
- })
+ .unwrap_or_else(|| panic!("no resolution for {:?} {:?} {:?}", path_str, ns, module_id))
.and_then(|res| res.try_into().ok())
.or_else(|| resolve_primitive(path_str, ns));
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
@@ -519,7 +471,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
/// This is used for resolving type aliases.
fn def_id_to_res(&self, ty_id: DefId) -> Option<Res> {
use PrimitiveType::*;
- Some(match *self.cx.tcx.type_of(ty_id).kind() {
+ Some(match *self.cx.tcx.type_of(ty_id).subst_identity().kind() {
ty::Bool => Res::Primitive(Bool),
ty::Char => Res::Primitive(Char),
ty::Int(ity) => Res::Primitive(ity.into()),
@@ -542,6 +494,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..)
| ty::Dynamic(..)
| ty::Param(_)
| ty::Bound(..)
@@ -561,27 +514,27 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// FIXME: Only simple types are supported here, see if we can support
// other types such as Tuple, Array, Slice, etc.
// See https://github.com/rust-lang/rust/issues/90703#issuecomment-1004263455
- Some(tcx.mk_ty(match prim {
- Bool => ty::Bool,
- Str => ty::Str,
- Char => ty::Char,
- Never => ty::Never,
- I8 => ty::Int(ty::IntTy::I8),
- I16 => ty::Int(ty::IntTy::I16),
- I32 => ty::Int(ty::IntTy::I32),
- I64 => ty::Int(ty::IntTy::I64),
- I128 => ty::Int(ty::IntTy::I128),
- Isize => ty::Int(ty::IntTy::Isize),
- F32 => ty::Float(ty::FloatTy::F32),
- F64 => ty::Float(ty::FloatTy::F64),
- U8 => ty::Uint(ty::UintTy::U8),
- U16 => ty::Uint(ty::UintTy::U16),
- U32 => ty::Uint(ty::UintTy::U32),
- U64 => ty::Uint(ty::UintTy::U64),
- U128 => ty::Uint(ty::UintTy::U128),
- Usize => ty::Uint(ty::UintTy::Usize),
+ Some(match prim {
+ Bool => tcx.types.bool,
+ Str => tcx.types.str_,
+ Char => tcx.types.char,
+ Never => tcx.types.never,
+ I8 => tcx.types.i8,
+ I16 => tcx.types.i16,
+ I32 => tcx.types.i32,
+ I64 => tcx.types.i64,
+ I128 => tcx.types.i128,
+ Isize => tcx.types.isize,
+ F32 => tcx.types.f32,
+ F64 => tcx.types.f64,
+ U8 => tcx.types.u8,
+ U16 => tcx.types.u16,
+ U32 => tcx.types.u32,
+ U64 => tcx.types.u64,
+ U128 => tcx.types.u128,
+ Usize => tcx.types.usize,
_ => return None,
- }))
+ })
}
/// Resolve an associated item, returning its containing page's `Res`
@@ -619,7 +572,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
debug!("looking for associated item named {} for item {:?}", item_name, did);
// Checks if item_name is a variant of the `SomeItem` enum
if ns == TypeNS && def_kind == DefKind::Enum {
- match tcx.type_of(did).kind() {
+ match tcx.type_of(did).subst_identity().kind() {
ty::Adt(adt_def, _) => {
for variant in adt_def.variants() {
if variant.name == item_name {
@@ -653,7 +606,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
.or_else(|| {
resolve_associated_trait_item(
- tcx.type_of(did),
+ tcx.type_of(did).subst_identity(),
module_id,
item_name,
ns,
@@ -686,7 +639,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// they also look like associated items (`module::Type::Variant`),
// because they are real Rust syntax (unlike the intra-doc links
// field syntax) and are handled by the compiler's resolver.
- let def = match tcx.type_of(did).kind() {
+ let def = match tcx.type_of(did).subst_identity().kind() {
ty::Adt(def, _) if !def.is_enum() => def,
_ => return None,
};
@@ -736,12 +689,12 @@ fn resolve_associated_trait_item<'a>(
.find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_)
.map(|trait_assoc| {
trait_assoc_to_impl_assoc_item(cx.tcx, impl_, trait_assoc.def_id)
- .unwrap_or(trait_assoc)
+ .unwrap_or(*trait_assoc)
})
});
// FIXME(#74563): warn about ambiguity
debug!("the candidates were {:?}", candidates.clone().collect::<Vec<_>>());
- candidates.next().copied()
+ candidates.next()
}
/// Find the associated item in the impl `impl_id` that corresponds to the
@@ -758,7 +711,7 @@ fn trait_assoc_to_impl_assoc_item<'tcx>(
tcx: TyCtxt<'tcx>,
impl_id: DefId,
trait_assoc_id: DefId,
-) -> Option<&'tcx ty::AssocItem> {
+) -> Option<ty::AssocItem> {
let trait_to_impl_assoc_map = tcx.impl_item_implementor_ids(impl_id);
debug!(?trait_to_impl_assoc_map);
let impl_assoc_id = *trait_to_impl_assoc_map.get(&trait_assoc_id)?;
@@ -778,8 +731,7 @@ fn trait_impls_for<'a>(
module: DefId,
) -> FxHashSet<(DefId, DefId)> {
let tcx = cx.tcx;
- let iter = cx.resolver_caches.traits_in_scope[&module].iter().flat_map(|trait_candidate| {
- let trait_ = trait_candidate.def_id;
+ let iter = tcx.doc_link_traits_in_scope(module).iter().flat_map(|&trait_| {
trace!("considering explicit impl for trait {:?}", trait_);
// Look at each trait implementation to see if it's an impl for `did`
@@ -845,7 +797,7 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
// 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.
- for (parent_module, doc) in item.attrs.prepare_to_doc_link_resolution() {
+ for (parent_module, doc) in prepare_to_doc_link_resolution(&item.attrs.doc_strings) {
if !may_have_doc_links(&doc) {
continue;
}
@@ -853,22 +805,12 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> {
// 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.
let parent_node = parent_module.or(parent_node);
- let mut tmp_links = self
- .cx
- .resolver_caches
- .markdown_links
- .take()
- .expect("`markdown_links` are already borrowed");
- if !tmp_links.contains_key(&doc) {
- tmp_links.insert(doc.clone(), preprocessed_markdown_links(&doc));
- }
- for md_link in &tmp_links[&doc] {
- let link = self.resolve_link(item, &doc, parent_node, md_link);
+ for md_link in preprocessed_markdown_links(&doc) {
+ let link = self.resolve_link(item, &doc, parent_node, &md_link);
if let Some(link) = link {
self.cx.cache.intra_doc_links.entry(item.item_id).or_default().push(link);
}
}
- self.cx.resolver_caches.markdown_links = Some(tmp_links);
}
if item.is_mod() {
@@ -907,10 +849,10 @@ impl PreprocessingError {
#[derive(Clone)]
struct PreprocessingInfo {
- path_str: String,
+ path_str: Box<str>,
disambiguator: Option<Disambiguator>,
extra_fragment: Option<String>,
- link_text: String,
+ link_text: Box<str>,
}
// Not a typedef to avoid leaking several private structures from this module.
@@ -942,7 +884,8 @@ fn preprocess_link(
let mut parts = stripped.split('#');
let link = parts.next().unwrap();
- if link.trim().is_empty() {
+ let link = link.trim();
+ if link.is_empty() {
// This is an anchor to an element of the current page, nothing to do in here!
return None;
}
@@ -955,7 +898,7 @@ fn preprocess_link(
// Parse and strip the disambiguator from the link, if present.
let (disambiguator, path_str, link_text) = match Disambiguator::from_str(link) {
Ok(Some((d, path, link_text))) => (Some(d), path.trim(), link_text.trim()),
- Ok(None) => (None, link.trim(), link.trim()),
+ Ok(None) => (None, link, link),
Err((err_msg, relative_range)) => {
// Only report error if we would not have ignored this link. See issue #83859.
if !should_ignore_link_with_disambiguators(link) {
@@ -974,16 +917,12 @@ fn preprocess_link(
}
// Strip generics from the path.
- let path_str = if path_str.contains(['<', '>'].as_slice()) {
- match strip_generics_from_path(path_str) {
- Ok(path) => path,
- Err(err) => {
- debug!("link has malformed generics: {}", path_str);
- return Some(Err(PreprocessingError::MalformedGenerics(err, path_str.to_owned())));
- }
+ let path_str = match strip_generics_from_path(path_str) {
+ Ok(path) => path,
+ Err(err) => {
+ debug!("link has malformed generics: {}", path_str);
+ return Some(Err(PreprocessingError::MalformedGenerics(err, path_str.to_owned())));
}
- } else {
- path_str.to_owned()
};
// Sanity check to make sure we don't have any angle brackets after stripping generics.
@@ -998,7 +937,7 @@ fn preprocess_link(
path_str,
disambiguator,
extra_fragment: extra_fragment.map(|frag| frag.to_owned()),
- link_text: link_text.to_owned(),
+ link_text: Box::<str>::from(link_text),
}))
}
@@ -1054,7 +993,7 @@ impl LinkCollector<'_, '_> {
item_id: item.item_id,
module_id,
dis: disambiguator,
- path_str: path_str.to_owned(),
+ path_str: path_str.clone(),
extra_fragment: extra_fragment.clone(),
},
diag_info.clone(), // this struct should really be Copy, but Range is not :(
@@ -1128,7 +1067,7 @@ impl LinkCollector<'_, '_> {
}
res.def_id(self.cx.tcx).map(|page_id| ItemLink {
- link: ori_link.link.clone(),
+ link: Box::<str>::from(&*ori_link.link),
link_text: link_text.clone(),
page_id,
fragment,
@@ -1152,7 +1091,7 @@ impl LinkCollector<'_, '_> {
let page_id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
Some(ItemLink {
- link: ori_link.link.clone(),
+ link: Box::<str>::from(&*ori_link.link),
link_text: link_text.clone(),
page_id,
fragment,
@@ -1194,14 +1133,9 @@ impl LinkCollector<'_, '_> {
}
// item can be non-local e.g. when using #[doc(primitive = "pointer")]
- if let Some((src_id, dst_id)) = id
- .as_local()
- // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
- // would presumably panic if a fake `DefIndex` were passed.
- .and_then(|dst_id| {
- item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
- })
- {
+ if let Some((src_id, dst_id)) = id.as_local().and_then(|dst_id| {
+ item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
+ }) {
if self.cx.tcx.effective_visibilities(()).is_exported(src_id)
&& !self.cx.tcx.effective_visibilities(()).is_exported(dst_id)
{
@@ -1681,7 +1615,7 @@ fn resolution_failure(
// ignore duplicates
let mut variants_seen = SmallVec::<[_; 3]>::new();
for mut failure in kinds {
- let variant = std::mem::discriminant(&failure);
+ let variant = mem::discriminant(&failure);
if variants_seen.contains(&variant) {
continue;
}
@@ -1751,7 +1685,7 @@ fn resolution_failure(
if !path_str.contains("::") {
if disambiguator.map_or(true, |d| d.ns() == MacroNS)
- && let Some(&res) = collector.cx.resolver_caches.all_macro_rules
+ && let Some(&res) = collector.cx.tcx.resolutions(()).all_macro_rules
.get(&Symbol::intern(path_str))
{
diag.note(format!(
@@ -1772,15 +1706,35 @@ fn resolution_failure(
// Otherwise, it must be an associated item or variant
let res = partial_res.expect("None case was handled by `last_found_module`");
- let kind = match res {
- Res::Def(kind, _) => Some(kind),
+ let kind_did = match res {
+ Res::Def(kind, did) => Some((kind, did)),
Res::Primitive(_) => None,
};
- let path_description = if let Some(kind) = kind {
+ let is_struct_variant = |did| {
+ if let ty::Adt(def, _) = tcx.type_of(did).subst_identity().kind()
+ && def.is_enum()
+ && let Some(variant) = def.variants().iter().find(|v| v.name == res.name(tcx)) {
+ // ctor is `None` if variant is a struct
+ variant.ctor.is_none()
+ } else {
+ false
+ }
+ };
+ let path_description = if let Some((kind, did)) = kind_did {
match kind {
Mod | ForeignMod => "inner item",
Struct => "field or associated item",
Enum | Union => "variant or associated item",
+ Variant if is_struct_variant(did) => {
+ let variant = res.name(tcx);
+ let note = format!("variant `{variant}` has no such field");
+ if let Some(span) = sp {
+ diag.span_label(span, &note);
+ } else {
+ diag.note(&note);
+ }
+ return;
+ }
Variant
| Field
| Closure
@@ -1808,7 +1762,7 @@ fn resolution_failure(
}
Trait | TyAlias | ForeignTy | OpaqueTy | ImplTraitPlaceholder
| TraitAlias | TyParam | Static(_) => "associated item",
- Impl | GlobalAsm => unreachable!("not a path"),
+ Impl { .. } | GlobalAsm => unreachable!("not a path"),
}
} else {
"associated item"
@@ -2068,94 +2022,3 @@ fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<Res> {
debug!("resolved primitives {:?}", prim);
Some(Res::Primitive(prim))
}
-
-fn strip_generics_from_path(path_str: &str) -> Result<String, MalformedGenerics> {
- let mut stripped_segments = vec![];
- let mut path = path_str.chars().peekable();
- let mut segment = Vec::new();
-
- while let Some(chr) = path.next() {
- match chr {
- ':' => {
- if path.next_if_eq(&':').is_some() {
- let stripped_segment =
- strip_generics_from_path_segment(mem::take(&mut segment))?;
- if !stripped_segment.is_empty() {
- stripped_segments.push(stripped_segment);
- }
- } else {
- return Err(MalformedGenerics::InvalidPathSeparator);
- }
- }
- '<' => {
- segment.push(chr);
-
- match path.next() {
- Some('<') => {
- return Err(MalformedGenerics::TooManyAngleBrackets);
- }
- Some('>') => {
- return Err(MalformedGenerics::EmptyAngleBrackets);
- }
- Some(chr) => {
- segment.push(chr);
-
- while let Some(chr) = path.next_if(|c| *c != '>') {
- segment.push(chr);
- }
- }
- None => break,
- }
- }
- _ => segment.push(chr),
- }
- trace!("raw segment: {:?}", segment);
- }
-
- if !segment.is_empty() {
- let stripped_segment = strip_generics_from_path_segment(segment)?;
- if !stripped_segment.is_empty() {
- stripped_segments.push(stripped_segment);
- }
- }
-
- debug!("path_str: {:?}\nstripped segments: {:?}", path_str, &stripped_segments);
-
- let stripped_path = stripped_segments.join("::");
-
- if !stripped_path.is_empty() { Ok(stripped_path) } else { Err(MalformedGenerics::MissingType) }
-}
-
-fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<String, MalformedGenerics> {
- let mut stripped_segment = String::new();
- let mut param_depth = 0;
-
- let mut latest_generics_chunk = String::new();
-
- for c in segment {
- if c == '<' {
- param_depth += 1;
- latest_generics_chunk.clear();
- } else if c == '>' {
- param_depth -= 1;
- if latest_generics_chunk.contains(" as ") {
- // The segment tries to use fully-qualified syntax, which is currently unsupported.
- // Give a helpful error message instead of completely ignoring the angle brackets.
- return Err(MalformedGenerics::HasFullyQualifiedSyntax);
- }
- } else {
- if param_depth == 0 {
- stripped_segment.push(c);
- } else {
- latest_generics_chunk.push(c);
- }
- }
- }
-
- if param_depth == 0 {
- Ok(stripped_segment)
- } else {
- // The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>`
- Err(MalformedGenerics::UnbalancedAngleBrackets)
- }
-}
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
deleted file mode 100644
index 42677bd84..000000000
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ /dev/null
@@ -1,407 +0,0 @@
-use crate::clean::Attributes;
-use crate::core::ResolverCaches;
-use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
-use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink};
-
-use rustc_ast::visit::{self, AssocCtxt, Visitor};
-use rustc_ast::{self as ast, ItemKind};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::def::Namespace::*;
-use rustc_hir::def::{DefKind, Namespace, Res};
-use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID};
-use rustc_hir::TraitCandidate;
-use rustc_middle::ty::{DefIdTree, Visibility};
-use rustc_resolve::{ParentScope, Resolver};
-use rustc_span::symbol::sym;
-use rustc_span::{Symbol, SyntaxContext};
-
-use std::collections::hash_map::Entry;
-use std::mem;
-
-pub(crate) fn early_resolve_intra_doc_links(
- resolver: &mut Resolver<'_>,
- krate: &ast::Crate,
- document_private_items: bool,
-) -> ResolverCaches {
- let parent_scope =
- ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
- let mut link_resolver = EarlyDocLinkResolver {
- resolver,
- parent_scope,
- visited_mods: Default::default(),
- markdown_links: Default::default(),
- doc_link_resolutions: Default::default(),
- traits_in_scope: Default::default(),
- all_trait_impls: Default::default(),
- all_macro_rules: Default::default(),
- document_private_items,
- };
-
- // Overridden `visit_item` below doesn't apply to the crate root,
- // so we have to visit its attributes and reexports separately.
- 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);
-
- // 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.
- // DO NOT REMOVE THIS without first testing on the reproducer in
- // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
- for (extern_name, _) in
- link_resolver.resolver.sess().opts.externs.iter().filter(|(_, entry)| entry.add_prelude)
- {
- 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_trait_impls: Some(link_resolver.all_trait_impls),
- all_macro_rules: link_resolver.all_macro_rules,
- }
-}
-
-fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes {
- Attributes::from_ast_iter(attrs.map(|attr| (attr, None)), true)
-}
-
-struct EarlyDocLinkResolver<'r, 'ra> {
- resolver: &'r mut Resolver<'ra>,
- parent_scope: ParentScope<'ra>,
- visited_mods: DefIdSet,
- markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
- doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
- traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
- all_trait_impls: Vec<DefId>,
- all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
- document_private_items: bool,
-}
-
-impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
- fn add_traits_in_scope(&mut self, def_id: DefId) {
- // Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
- // Keys in the `traits_in_scope` cache are always module IDs.
- if let Entry::Vacant(entry) = self.traits_in_scope.entry(def_id) {
- let module = self.resolver.get_nearest_non_block_module(def_id);
- let module_id = module.def_id();
- let entry = if module_id == def_id {
- Some(entry)
- } else if let Entry::Vacant(entry) = self.traits_in_scope.entry(module_id) {
- Some(entry)
- } else {
- None
- };
- if let Some(entry) = entry {
- entry.insert(self.resolver.traits_in_scope(
- None,
- &ParentScope::module(module, self.resolver),
- SyntaxContext::root(),
- None,
- ));
- }
- }
- }
-
- /// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass.
- /// That pass filters impls using type-based information, but we don't yet have such
- /// information here, so we just conservatively calculate traits in scope for *all* modules
- /// having impls in them.
- fn process_extern_impls(&mut self) {
- // Resolving links in already existing crates may trigger loading of new crates.
- let mut start_cnum = 0;
- loop {
- let crates = Vec::from_iter(self.resolver.cstore().crates_untracked());
- for &cnum in &crates[start_cnum..] {
- let all_trait_impls =
- Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum));
- let all_inherent_impls =
- Vec::from_iter(self.resolver.cstore().inherent_impls_in_crate_untracked(cnum));
- let all_incoherent_impls = Vec::from_iter(
- self.resolver.cstore().incoherent_impls_in_crate_untracked(cnum),
- );
-
- // 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 &(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);
- }
- }
- for (ty_def_id, impl_def_id) in all_inherent_impls {
- if self.resolver.cstore().visibility_untracked(ty_def_id).is_public() {
- self.resolve_doc_links_extern_impl(impl_def_id, true);
- }
- }
- for impl_def_id in all_incoherent_impls {
- self.resolve_doc_links_extern_impl(impl_def_id, true);
- }
-
- self.all_trait_impls
- .extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id));
- }
-
- if crates.len() > start_cnum {
- start_cnum = crates.len();
- } else {
- break;
- }
- }
- }
-
- fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) {
- self.resolve_doc_links_extern_outer_fixme(def_id, def_id);
- let assoc_item_def_ids = Vec::from_iter(
- self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.resolver.sess()),
- );
- for assoc_def_id in assoc_item_def_ids {
- if !is_inherent || self.resolver.cstore().visibility_untracked(assoc_def_id).is_public()
- {
- self.resolve_doc_links_extern_outer_fixme(assoc_def_id, def_id);
- }
- }
- }
-
- // FIXME: replace all uses with `resolve_doc_links_extern_outer` to actually resolve links, not
- // just add traits in scope. This may be expensive and require benchmarking and optimization.
- fn resolve_doc_links_extern_outer_fixme(&mut self, def_id: DefId, scope_id: DefId) {
- if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
- return;
- }
- if let Some(parent_id) = self.resolver.opt_parent(scope_id) {
- self.add_traits_in_scope(parent_id);
- }
- }
-
- fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId) {
- if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
- return;
- }
- let attrs = Vec::from_iter(
- self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
- );
- let parent_scope = ParentScope::module(
- self.resolver.get_nearest_non_block_module(
- self.resolver.opt_parent(scope_id).unwrap_or(scope_id),
- ),
- self.resolver,
- );
- self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
- }
-
- fn resolve_doc_links_extern_inner(&mut self, def_id: DefId) {
- if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
- return;
- }
- let attrs = Vec::from_iter(
- self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
- );
- let parent_scope = ParentScope::module(self.resolver.expect_module(def_id), self.resolver);
- self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
- }
-
- fn resolve_doc_links_local(&mut self, attrs: &[ast::Attribute]) {
- if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
- return;
- }
- self.resolve_doc_links(doc_attrs(attrs.iter()), self.parent_scope);
- }
-
- fn resolve_and_cache(
- &mut self,
- path_str: &str,
- ns: Namespace,
- parent_scope: &ParentScope<'ra>,
- ) -> bool {
- // FIXME: This caching may be incorrect in case of multiple `macro_rules`
- // items with the same name in the same module.
- self.doc_link_resolutions
- .entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
- .or_insert_with_key(|(path, ns, _)| {
- self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *parent_scope)
- })
- .is_some()
- }
-
- fn resolve_doc_links(&mut self, attrs: Attributes, parent_scope: ParentScope<'ra>) {
- let mut need_traits_in_scope = false;
- for (doc_module, doc) in attrs.prepare_to_doc_link_resolution() {
- assert_eq!(doc_module, None);
- let mut tmp_links = mem::take(&mut self.markdown_links);
- let links =
- tmp_links.entry(doc).or_insert_with_key(|doc| preprocessed_markdown_links(doc));
- for PreprocessedMarkdownLink(pp_link, _) in links {
- if let Ok(pinfo) = pp_link {
- // The logic here is a conservative approximation for path resolution in
- // `resolve_with_disambiguator`.
- if let Some(ns) = pinfo.disambiguator.map(Disambiguator::ns) {
- if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
- continue;
- }
- }
-
- // Resolve all namespaces due to no disambiguator or for diagnostics.
- let mut any_resolved = false;
- let mut need_assoc = false;
- for ns in [TypeNS, ValueNS, MacroNS] {
- if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
- any_resolved = true;
- } else if ns != MacroNS {
- need_assoc = true;
- }
- }
-
- // Resolve all prefixes for type-relative resolution or for diagnostics.
- if need_assoc || !any_resolved {
- let mut path = &pinfo.path_str[..];
- while let Some(idx) = path.rfind("::") {
- path = &path[..idx];
- need_traits_in_scope = true;
- for ns in [TypeNS, ValueNS, MacroNS] {
- self.resolve_and_cache(path, ns, &parent_scope);
- }
- }
- }
- }
- }
- self.markdown_links = tmp_links;
- }
-
- if need_traits_in_scope {
- self.add_traits_in_scope(parent_scope.module.def_id());
- }
- }
-
- /// When reexports are inlined, they are replaced with item which they refer to, those items
- /// may have links in their doc comments, those links are resolved at the item definition site,
- /// so we need to know traits in scope at that definition site.
- fn process_module_children_or_reexports(&mut self, module_id: DefId) {
- if !self.visited_mods.insert(module_id) {
- return; // avoid infinite recursion
- }
-
- for child in self.resolver.module_children_or_reexports(module_id) {
- // This condition should give a superset of `denied` from `fn clean_use_statement`.
- if child.vis.is_public()
- || self.document_private_items
- && child.vis != Visibility::Restricted(module_id)
- && module_id.is_local()
- {
- if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() {
- let scope_id = match child.res {
- 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
- }
- 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, _) =
- child.res
- {
- let field_def_ids = Vec::from_iter(
- self.resolver
- .cstore()
- .associated_item_def_ids_untracked(def_id, self.resolver.sess()),
- );
- for field_def_id in field_def_ids {
- self.resolve_doc_links_extern_outer(field_def_id, scope_id);
- }
- }
- }
- }
- }
- }
-}
-
-impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
- fn visit_item(&mut self, item: &ast::Item) {
- self.resolve_doc_links_local(&item.attrs); // Outer attribute scope
- if let ItemKind::Mod(..) = item.kind {
- let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
- let module = self.resolver.expect_module(module_def_id);
- let old_module = mem::replace(&mut self.parent_scope.module, module);
- let old_macro_rules = self.parent_scope.macro_rules;
- self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
- self.process_module_children_or_reexports(module_def_id);
- visit::walk_item(self, item);
- if item
- .attrs
- .iter()
- .all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape))
- {
- self.parent_scope.macro_rules = old_macro_rules;
- }
- self.parent_scope.module = old_module;
- } else {
- match &item.kind {
- 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 => {
- let (macro_rules_scope, res) =
- self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
- self.parent_scope.macro_rules = macro_rules_scope;
- self.all_macro_rules.insert(item.ident.name, res);
- }
- _ => {}
- }
- visit::walk_item(self, item);
- }
- }
-
- fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
- self.resolve_doc_links_local(&item.attrs);
- visit::walk_assoc_item(self, item, ctxt)
- }
-
- fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
- self.resolve_doc_links_local(&item.attrs);
- visit::walk_foreign_item(self, item)
- }
-
- fn visit_variant(&mut self, v: &ast::Variant) {
- self.resolve_doc_links_local(&v.attrs);
- visit::walk_variant(self, v)
- }
-
- fn visit_field_def(&mut self, field: &ast::FieldDef) {
- self.resolve_doc_links_local(&field.attrs);
- visit::walk_field_def(self, field)
- }
-
- fn visit_block(&mut self, block: &ast::Block) {
- let old_macro_rules = self.parent_scope.macro_rules;
- visit::walk_block(self, block);
- self.parent_scope.macro_rules = old_macro_rules;
- }
-
- // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
- // then this will have to implement other visitor methods too.
-}
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 79db3c6c3..01ed4a60b 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -7,8 +7,8 @@ use crate::core::DocContext;
use crate::formats::cache::Cache;
use crate::visit::DocVisitor;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE};
use rustc_middle::ty::{self, DefIdTree};
use rustc_span::symbol::sym;
@@ -45,18 +45,20 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
let mut new_items_local = Vec::new();
// External trait impls.
- cx.with_all_trait_impls(|cx, all_trait_impls| {
+ {
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impls");
- for &impl_def_id in all_trait_impls.iter().skip_while(|def_id| def_id.is_local()) {
- inline::build_impl(cx, None, impl_def_id, None, &mut new_items_external);
+ for &cnum in cx.tcx.crates(()) {
+ for &impl_def_id in cx.tcx.trait_impls_in_crate(cnum) {
+ inline::build_impl(cx, None, impl_def_id, None, &mut new_items_external);
+ }
}
- });
+ }
// Local trait impls.
- cx.with_all_trait_impls(|cx, all_trait_impls| {
+ {
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls");
let mut attr_buf = Vec::new();
- for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) {
+ for &impl_def_id in cx.tcx.trait_impls_in_crate(LOCAL_CRATE) {
let mut parent = Some(cx.tcx.parent(impl_def_id));
while let Some(did) = parent {
attr_buf.extend(
@@ -76,7 +78,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items_local);
attr_buf.clear();
}
- });
+ }
cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
for def_id in PrimitiveType::all_impls(cx.tcx) {
@@ -107,7 +109,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
// `Generics`. To avoid relying on the `impl` block, these
// things would need to be created from wholecloth, in a
// form that is valid for use in type inference.
- let ty = tcx.type_of(def_id);
+ let ty = tcx.type_of(def_id).subst_identity();
match ty.kind() {
ty::Slice(ty)
| ty::Ref(_, ty, _)
@@ -126,14 +128,14 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
});
let mut cleaner = BadImplStripper { prims, items: crate_items, cache: &cx.cache };
- let mut type_did_to_deref_target: FxHashMap<DefId, &Type> = FxHashMap::default();
+ let mut type_did_to_deref_target: DefIdMap<&Type> = DefIdMap::default();
// Follow all `Deref` targets of included items and recursively add them as valid
fn add_deref_target(
cx: &DocContext<'_>,
- map: &FxHashMap<DefId, &Type>,
+ map: &DefIdMap<&Type>,
cleaner: &mut BadImplStripper<'_>,
- targets: &mut FxHashSet<DefId>,
+ targets: &mut DefIdSet,
type_did: DefId,
) {
if let Some(target) = map.get(&type_did) {
@@ -154,39 +156,38 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
// scan through included items ahead of time to splice in Deref targets to the "valid" sets
for it in new_items_external.iter().chain(new_items_local.iter()) {
- if let ImplItem(box Impl { ref for_, ref trait_, ref items, .. }) = *it.kind {
- if trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait()
- && cleaner.keep_impl(for_, true)
- {
- let target = items
- .iter()
- .find_map(|item| match *item.kind {
- AssocTypeItem(ref t, _) => Some(&t.type_),
- _ => None,
- })
- .expect("Deref impl without Target type");
+ if let ImplItem(box Impl { ref for_, ref trait_, ref items, .. }) = *it.kind &&
+ trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait() &&
+ cleaner.keep_impl(for_, true)
+ {
+ let target = items
+ .iter()
+ .find_map(|item| match *item.kind {
+ AssocTypeItem(ref t, _) => Some(&t.type_),
+ _ => None,
+ })
+ .expect("Deref impl without Target type");
- if let Some(prim) = target.primitive_type() {
- cleaner.prims.insert(prim);
- } else if let Some(did) = target.def_id(&cx.cache) {
- cleaner.items.insert(did.into());
- }
- if let Some(for_did) = for_.def_id(&cx.cache) {
- if type_did_to_deref_target.insert(for_did, target).is_none() {
- // Since only the `DefId` portion of the `Type` instances is known to be same for both the
- // `Deref` target type and the impl for type positions, this map of types is keyed by
- // `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
- if cleaner.keep_impl_with_def_id(for_did.into()) {
- let mut targets = FxHashSet::default();
- targets.insert(for_did);
- add_deref_target(
- cx,
- &type_did_to_deref_target,
- &mut cleaner,
- &mut targets,
- for_did,
- );
- }
+ if let Some(prim) = target.primitive_type() {
+ cleaner.prims.insert(prim);
+ } else if let Some(did) = target.def_id(&cx.cache) {
+ cleaner.items.insert(did.into());
+ }
+ if let Some(for_did) = for_.def_id(&cx.cache) {
+ if type_did_to_deref_target.insert(for_did, target).is_none() {
+ // Since only the `DefId` portion of the `Type` instances is known to be same for both the
+ // `Deref` target type and the impl for type positions, this map of types is keyed by
+ // `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
+ if cleaner.keep_impl_with_def_id(for_did.into()) {
+ let mut targets = DefIdSet::default();
+ targets.insert(for_did);
+ add_deref_target(
+ cx,
+ &type_did_to_deref_target,
+ &mut cleaner,
+ &mut targets,
+ for_did,
+ );
}
}
}
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 7158355ff..26fbb03a4 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -19,8 +19,7 @@ use crate::passes::source_span_for_markdown_range;
pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
if let Some(dox) = &item.attrs.collapsed_doc_value() {
let sp = item.attr_span(cx.tcx);
- let extra =
- crate::html::markdown::ExtraInfo::new_did(cx.tcx, item.item_id.expect_def_id(), sp);
+ let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp);
for code_block in markdown::rust_code_blocks(dox, &extra) {
check_rust_syntax(cx, item, dox, code_block);
}
@@ -34,8 +33,10 @@ fn check_rust_syntax(
code_block: RustCodeBlock,
) {
let buffer = Lrc::new(Lock::new(Buffer::default()));
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let fallback_bundle = rustc_errors::fallback_fluent_bundle(
+ rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
+ false,
+ );
let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
@@ -73,7 +74,6 @@ fn check_rust_syntax(
return;
};
- let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
let empty_block = code_block.lang_string == Default::default() && code_block.is_fenced;
let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
@@ -93,6 +93,7 @@ fn check_rust_syntax(
// Finally build and emit the completed diagnostic.
// All points of divergence have been handled earlier so this can be
// done the same way whether the span is precise or not.
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
cx.tcx.struct_span_lint_hir(crate::lint::INVALID_RUST_CODEBLOCKS, hir_id, sp, msg, |lint| {
let explanation = if is_ignore {
"`ignore` code blocks require valid Rust code for syntax highlighting; \
diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs
index 070c0aab5..4f72df5a5 100644
--- a/src/librustdoc/passes/lint/html_tags.rs
+++ b/src/librustdoc/passes/lint/html_tags.rs
@@ -113,7 +113,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
if let Some(link) =
link_names.iter().find(|link| *link.original_text == *broken_link.reference)
{
- Some((link.href.as_str().into(), link.new_text.as_str().into()))
+ Some((link.href.as_str().into(), link.new_text.to_string().into()))
} else if matches!(
&broken_link.link_type,
LinkType::Reference | LinkType::ReferenceUnknown
@@ -210,11 +210,9 @@ fn extract_path_backwards(text: &str, end_pos: usize) -> Option<usize> {
.take_while(|(_, c)| is_id_start(*c) || is_id_continue(*c))
.reduce(|_accum, item| item)
.and_then(|(new_pos, c)| is_id_start(c).then_some(new_pos));
- if let Some(new_pos) = new_pos {
- if current_pos != new_pos {
- current_pos = new_pos;
- continue;
- }
+ if let Some(new_pos) = new_pos && current_pos != new_pos {
+ current_pos = new_pos;
+ continue;
}
break;
}
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 634e70ec9..4b1ff68df 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -2,11 +2,12 @@
//! process.
use rustc_middle::ty::TyCtxt;
+use rustc_resolve::rustdoc::DocFragmentKind;
use rustc_span::{InnerSpan, Span, DUMMY_SP};
use std::ops::Range;
use self::Condition::*;
-use crate::clean::{self, DocFragmentKind};
+use crate::clean;
use crate::core::DocContext;
mod stripper;
diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs
index de3a4b339..a4bc48690 100644
--- a/src/librustdoc/passes/propagate_doc_cfg.rs
+++ b/src/librustdoc/passes/propagate_doc_cfg.rs
@@ -9,6 +9,7 @@ use crate::fold::DocFolder;
use crate::passes::Pass;
use rustc_hir::def_id::LocalDefId;
+use rustc_middle::ty::DefIdTree;
pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass {
name: "propagate-doc-cfg",
@@ -41,24 +42,22 @@ impl<'a, 'tcx> CfgPropagator<'a, 'tcx> {
let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local())
else { return };
- let hir = self.cx.tcx.hir();
- let hir_id = hir.local_def_id_to_hir_id(def_id);
-
if check_parent {
- let expected_parent = hir.get_parent_item(hir_id);
+ let expected_parent = self.cx.tcx.opt_local_parent(def_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.def_id) {
+ if self.parent.is_some() && self.parent == expected_parent {
return;
}
}
let mut attrs = Vec::new();
- for (parent_hir_id, _) in hir.parent_iter(hir_id) {
- if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) {
- attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id()));
- }
+ let mut next_def_id = def_id;
+ while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) {
+ attrs.extend_from_slice(load_attrs(self.cx, parent_def_id.to_def_id()));
+ next_def_id = parent_def_id;
}
+
let (_, cfg) = merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
item.cfg = cfg;
}
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index e07a788a7..890b3e8d6 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -1,4 +1,6 @@
//! Strip all doc(hidden) items from the output.
+
+use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::sym;
use std::mem;
@@ -7,6 +9,7 @@ use crate::clean::{Item, ItemIdSet, NestedAttributesExt};
use crate::core::DocContext;
use crate::fold::{strip_item, DocFolder};
use crate::passes::{ImplStripper, Pass};
+use crate::visit_ast::inherits_doc_hidden;
pub(crate) const STRIP_HIDDEN: Pass = Pass {
name: "strip-hidden",
@@ -21,7 +24,12 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
// strip all #[doc(hidden)] items
let krate = {
- let mut stripper = Stripper { retained: &mut retained, update_retained: true };
+ let mut stripper = Stripper {
+ retained: &mut retained,
+ update_retained: true,
+ tcx: cx.tcx,
+ is_in_hidden_item: false,
+ };
stripper.fold_crate(krate)
};
@@ -36,40 +44,97 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
stripper.fold_crate(krate)
}
-struct Stripper<'a> {
+struct Stripper<'a, 'tcx> {
retained: &'a mut ItemIdSet,
update_retained: bool,
+ tcx: TyCtxt<'tcx>,
+ is_in_hidden_item: bool,
}
-impl<'a> DocFolder for Stripper<'a> {
+impl<'a, 'tcx> Stripper<'a, 'tcx> {
+ fn set_is_in_hidden_item_and_fold(&mut self, is_in_hidden_item: bool, i: Item) -> Item {
+ let prev = self.is_in_hidden_item;
+ self.is_in_hidden_item |= is_in_hidden_item;
+ let ret = self.fold_item_recur(i);
+ self.is_in_hidden_item = prev;
+ ret
+ }
+
+ /// In case `i` is a non-hidden impl block, then we special-case it by changing the value
+ /// of `is_in_hidden_item` to `true` because the impl children inherit its visibility.
+ fn recurse_in_impl_or_exported_macro(&mut self, i: Item) -> Item {
+ let prev = mem::replace(&mut self.is_in_hidden_item, false);
+ let ret = self.fold_item_recur(i);
+ self.is_in_hidden_item = prev;
+ ret
+ }
+}
+
+impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
- if i.attrs.lists(sym::doc).has_word(sym::hidden) {
- debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
- // Use a dedicated hidden item for fields, variants, and modules.
- // We need to keep private fields and variants, so that the docs
- // can show a placeholder "// some variants omitted". We need to keep
- // private modules, because they can contain impl blocks, and impl
- // block privacy is inherited from the type and trait, not from the
- // module it's defined in. Both of these are marked "stripped," and
- // not included in the final docs, but since they still have an effect
- // on the final doc, cannot be completely removed from the Clean IR.
- match *i.kind {
- clean::StructFieldItem(..) | clean::ModuleItem(..) | clean::VariantItem(..) => {
- // We need to recurse into stripped modules to
- // strip things like impl methods but when doing so
- // we must not add any items to the `retained` set.
- let old = mem::replace(&mut self.update_retained, false);
- let ret = strip_item(self.fold_item_recur(i));
- self.update_retained = old;
- return Some(ret);
- }
- _ => return None,
+ let has_doc_hidden = i.attrs.lists(sym::doc).has_word(sym::hidden);
+ let is_impl_or_exported_macro = match *i.kind {
+ clean::ImplItem(..) => true,
+ // If the macro has the `#[macro_export]` attribute, it means it's accessible at the
+ // crate level so it should be handled differently.
+ clean::MacroItem(..) => {
+ i.attrs.other_attrs.iter().any(|attr| attr.has_name(sym::macro_export))
}
- } else {
+ _ => false,
+ };
+ let mut is_hidden = has_doc_hidden;
+ if !is_impl_or_exported_macro {
+ is_hidden = self.is_in_hidden_item || has_doc_hidden;
+ if !is_hidden && i.inline_stmt_id.is_none() {
+ // We don't need to check if it's coming from a reexport since the reexport itself was
+ // already checked.
+ is_hidden = i
+ .item_id
+ .as_def_id()
+ .and_then(|def_id| def_id.as_local())
+ .map(|def_id| inherits_doc_hidden(self.tcx, def_id))
+ .unwrap_or(false);
+ }
+ }
+ if !is_hidden {
if self.update_retained {
self.retained.insert(i.item_id);
}
+ return Some(if is_impl_or_exported_macro {
+ self.recurse_in_impl_or_exported_macro(i)
+ } else {
+ self.set_is_in_hidden_item_and_fold(false, i)
+ });
+ }
+ debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
+ // Use a dedicated hidden item for fields, variants, and modules.
+ // We need to keep private fields and variants, so that the docs
+ // can show a placeholder "// some variants omitted". We need to keep
+ // private modules, because they can contain impl blocks, and impl
+ // block privacy is inherited from the type and trait, not from the
+ // module it's defined in. Both of these are marked "stripped," and
+ // not included in the final docs, but since they still have an effect
+ // on the final doc, cannot be completely removed from the Clean IR.
+ match *i.kind {
+ clean::StructFieldItem(..) | clean::ModuleItem(..) | clean::VariantItem(..) => {
+ // We need to recurse into stripped modules to
+ // strip things like impl methods but when doing so
+ // we must not add any items to the `retained` set.
+ let old = mem::replace(&mut self.update_retained, false);
+ let ret = strip_item(self.set_is_in_hidden_item_and_fold(true, i));
+ self.update_retained = old;
+ Some(ret)
+ }
+ _ => {
+ let ret = self.set_is_in_hidden_item_and_fold(true, i);
+ if has_doc_hidden {
+ // If the item itself has `#[doc(hidden)]`, then we simply remove it.
+ None
+ } else {
+ // However if it's a "descendant" of a `#[doc(hidden)]` item, then we strip it.
+ Some(strip_item(ret))
+ }
+ }
}
- Some(self.fold_item_recur(i))
}
}
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 048ed2646..cba55e5fe 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -201,27 +201,25 @@ impl<'a> DocFolder for ImplStripper<'a, '_> {
// 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 !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.for_.def_id(self.cache) &&
+ !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 !self.should_keep_impl(&i, did) {
- debug!("ImplStripper: impl item for stripped trait; removing");
- return None;
- }
+ if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) &&
+ !self.should_keep_impl(&i, did) {
+ debug!("ImplStripper: impl item for stripped trait; removing");
+ return None;
}
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 !self.should_keep_impl(&i, did) {
- debug!(
- "ImplStripper: stripped item in trait's generics; removing impl"
- );
- return None;
- }
+ if let Some(did) = typaram.def_id(self.cache) && !self.should_keep_impl(&i, did)
+ {
+ debug!(
+ "ImplStripper: stripped item in trait's generics; removing impl"
+ );
+ return None;
}
}
}