From 631cd5845e8de329d0e227aaa707d7ea228b8f8f Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:20:29 +0200 Subject: Merging upstream version 1.70.0+dfsg1. Signed-off-by: Daniel Baumann --- src/librustdoc/clean/blanket_impl.rs | 5 +- src/librustdoc/clean/cfg.rs | 1 + src/librustdoc/clean/inline.rs | 106 +++++-------- src/librustdoc/clean/mod.rs | 288 +++++++++++++---------------------- src/librustdoc/clean/simplify.rs | 6 +- src/librustdoc/clean/types.rs | 218 +++++++++++++++----------- src/librustdoc/clean/types/tests.rs | 13 +- src/librustdoc/clean/utils.rs | 6 +- 8 files changed, 298 insertions(+), 345 deletions(-) (limited to 'src/librustdoc/clean') diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index bcdbbcacc..3a3bf6a7a 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -1,6 +1,6 @@ use crate::rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_hir as hir; -use rustc_infer::infer::{InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits; use rustc_middle::ty::ToPredicate; use rustc_span::DUMMY_SP; @@ -47,8 +47,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { // Require the type the impl is implemented on to match // our type, and ignore the impl if there was a mismatch. - let cause = traits::ObligationCause::dummy(); - let Ok(eq_result) = infcx.at(&cause, param_env).eq(impl_trait_ref.self_ty(), impl_ty) else { + let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq(DefineOpaqueTypes::No, impl_trait_ref.self_ty(), impl_ty) else { continue }; let InferOk { value: (), obligations } = eq_result; diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index dd58a5b51..5177cffe6 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -517,6 +517,7 @@ impl<'a> fmt::Display for Display<'a> { "aarch64" => "AArch64", "arm" => "ARM", "asmjs" => "JavaScript", + "loongarch64" => "LoongArch LA64", "m68k" => "M68k", "mips" => "MIPS", "mips64" => "MIPS-64", diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 148243683..cc5d13808 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -36,15 +36,11 @@ use crate::formats::item_type::ItemType; /// /// 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(crate) fn try_inline( cx: &mut DocContext<'_>, - parent_module: DefId, - import_def_id: Option, res: Res, name: Symbol, - attrs: Option<&[ast::Attribute]>, + attrs: Option<(&[ast::Attribute], Option)>, visited: &mut DefIdSet, ) -> Option> { let did = res.opt_def_id()?; @@ -55,38 +51,17 @@ pub(crate) fn try_inline( debug!("attrs={:?}", attrs); - let attrs_without_docs = attrs.map(|attrs| { - attrs.into_iter().filter(|a| a.doc_str().is_none()).cloned().collect::>() + let attrs_without_docs = attrs.map(|(attrs, def_id)| { + (attrs.into_iter().filter(|a| a.doc_str().is_none()).cloned().collect::>(), def_id) }); - // We need this ugly code because: - // - // ``` - // attrs_without_docs.map(|a| a.as_slice()) - // ``` - // - // will fail because it returns a temporary slice and: - // - // ``` - // attrs_without_docs.map(|s| { - // vec = s.as_slice(); - // vec - // }) - // ``` - // - // will fail because we're moving an uninitialized variable into a closure. - let vec; - let attrs_without_docs = match attrs_without_docs { - Some(s) => { - vec = s; - Some(vec.as_slice()) - } - None => None, - }; + let attrs_without_docs = + attrs_without_docs.as_ref().map(|(attrs, def_id)| (&attrs[..], *def_id)); + let import_def_id = attrs.and_then(|(_, def_id)| def_id); let kind = match res { Res::Def(DefKind::Trait, did) => { record_extern_fqn(cx, did, ItemType::Trait); - build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); + build_impls(cx, did, attrs_without_docs, &mut ret); clean::TraitItem(Box::new(build_external_trait(cx, did))) } Res::Def(DefKind::Fn, did) => { @@ -95,27 +70,27 @@ pub(crate) fn try_inline( } Res::Def(DefKind::Struct, did) => { record_extern_fqn(cx, did, ItemType::Struct); - build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); + build_impls(cx, did, attrs_without_docs, &mut ret); clean::StructItem(build_struct(cx, did)) } Res::Def(DefKind::Union, did) => { record_extern_fqn(cx, did, ItemType::Union); - build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); + build_impls(cx, did, attrs_without_docs, &mut ret); clean::UnionItem(build_union(cx, did)) } Res::Def(DefKind::TyAlias, did) => { record_extern_fqn(cx, did, ItemType::Typedef); - build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); + build_impls(cx, did, attrs_without_docs, &mut ret); clean::TypedefItem(build_type_alias(cx, did)) } Res::Def(DefKind::Enum, did) => { record_extern_fqn(cx, did, ItemType::Enum); - build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); + build_impls(cx, did, attrs_without_docs, &mut ret); clean::EnumItem(build_enum(cx, did)) } Res::Def(DefKind::ForeignTy, did) => { record_extern_fqn(cx, did, ItemType::ForeignType); - build_impls(cx, Some(parent_module), did, attrs_without_docs, &mut ret); + build_impls(cx, did, attrs_without_docs, &mut ret); clean::ForeignTypeItem } // Never inline enum variants but leave them shown as re-exports. @@ -136,7 +111,7 @@ pub(crate) fn try_inline( clean::ConstantItem(build_const(cx, did)) } Res::Def(DefKind::Macro(kind), did) => { - let mac = build_macro(cx, did, name, import_def_id); + let mac = build_macro(cx, did, name, import_def_id, kind); let type_kind = match kind { MacroKind::Bang => ItemType::Macro, @@ -149,7 +124,7 @@ pub(crate) fn try_inline( _ => return None, }; - let (attrs, cfg) = merge_attrs(cx, Some(parent_module), load_attrs(cx, did), attrs); + let (attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs); cx.inlined.insert(did.into()); let mut item = clean::Item::from_def_id_and_attrs_and_parts(did, Some(name), kind, Box::new(attrs), cfg); @@ -177,8 +152,7 @@ pub(crate) fn try_inline_glob( // reexported by the glob, e.g. because they are shadowed by something else. let reexports = cx .tcx - .module_reexports(current_mod) - .unwrap_or_default() + .module_children_reexports(current_mod) .iter() .filter_map(|child| child.res.opt_def_id()) .collect(); @@ -316,9 +290,8 @@ fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box /// Builds all inherent implementations of an ADT (struct/union/enum) or Trait item/path/reexport. pub(crate) fn build_impls( cx: &mut DocContext<'_>, - parent_module: Option, did: DefId, - attrs: Option<&[ast::Attribute]>, + attrs: Option<(&[ast::Attribute], Option)>, ret: &mut Vec, ) { let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls"); @@ -326,7 +299,7 @@ pub(crate) fn build_impls( // 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, parent_module, did, attrs, ret); + build_impl(cx, did, attrs, ret); } // This pretty much exists expressly for `dyn Error` traits that exist in the `alloc` crate. @@ -340,28 +313,26 @@ pub(crate) fn build_impls( let type_ = if tcx.is_trait(did) { TraitSimplifiedType(did) } else { AdtSimplifiedType(did) }; for &did in tcx.incoherent_impls(type_) { - build_impl(cx, parent_module, did, attrs, ret); + build_impl(cx, did, attrs, ret); } } } -/// `parent_module` refers to the parent of the re-export, not the original item pub(crate) fn merge_attrs( cx: &mut DocContext<'_>, - parent_module: Option, old_attrs: &[ast::Attribute], - new_attrs: Option<&[ast::Attribute]>, + new_attrs: Option<(&[ast::Attribute], Option)>, ) -> (clean::Attributes, Option>) { // 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. - if let Some(inner) = new_attrs { + if let Some((inner, item_id)) = new_attrs { let mut both = inner.to_vec(); both.extend_from_slice(old_attrs); ( - if let Some(new_id) = parent_module { - Attributes::from_ast_with_additional(old_attrs, (inner, new_id)) + if let Some(item_id) = item_id { + Attributes::from_ast_with_additional(old_attrs, (inner, item_id)) } else { Attributes::from_ast(&both) }, @@ -375,9 +346,8 @@ pub(crate) fn merge_attrs( /// Inline an `impl`, inherent or of a trait. The `did` must be for an `impl`. pub(crate) fn build_impl( cx: &mut DocContext<'_>, - parent_module: Option, did: DefId, - attrs: Option<&[ast::Attribute]>, + attrs: Option<(&[ast::Attribute], Option)>, ret: &mut Vec, ) { if !cx.inlined.insert(did.into()) { @@ -539,7 +509,7 @@ pub(crate) fn build_impl( record_extern_trait(cx, did); } - let (merged_attrs, cfg) = merge_attrs(cx, parent_module, load_attrs(cx, did), attrs); + let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs); trace!("merged_attrs={:?}", merged_attrs); trace!( @@ -587,7 +557,7 @@ fn build_module_items( // If we're re-exporting a re-export it may actually re-export something in // two namespaces, so the target may be listed twice. Make sure we only // visit each node at most once. - for &item in cx.tcx.module_children(did).iter() { + for item in cx.tcx.module_children(did).iter() { if item.vis.is_public() { let res = item.res.expect_non_local(); if let Some(def_id) = res.opt_def_id() @@ -635,7 +605,7 @@ fn build_module_items( cfg: None, inline_stmt_id: None, }); - } else if let Some(i) = try_inline(cx, did, None, res, item.ident.name, None, visited) { + } else if let Some(i) = try_inline(cx, res, item.ident.name, None, visited) { items.extend(i) } } @@ -681,18 +651,24 @@ fn build_macro( def_id: DefId, name: Symbol, import_def_id: Option, + macro_kind: MacroKind, ) -> clean::ItemKind { match CStore::from_tcx(cx.tcx).load_macro_untracked(def_id, cx.sess()) { - LoadedMacro::MacroDef(item_def, _) => { - if let ast::ItemKind::MacroDef(ref def) = item_def.kind { - let vis = cx.tcx.visibility(import_def_id.unwrap_or(def_id)); - clean::MacroItem(clean::Macro { - source: utils::display_macro_source(cx, name, def, def_id, vis), - }) - } else { - unreachable!() + LoadedMacro::MacroDef(item_def, _) => match macro_kind { + MacroKind::Bang => { + if let ast::ItemKind::MacroDef(ref def) = item_def.kind { + let vis = cx.tcx.visibility(import_def_id.unwrap_or(def_id)); + clean::MacroItem(clean::Macro { + source: utils::display_macro_source(cx, name, def, def_id, vis), + }) + } else { + unreachable!() + } } - } + MacroKind::Derive | MacroKind::Attr => { + clean::ProcMacroItem(clean::ProcMacro { kind: macro_kind, helpers: Vec::new() }) + } + }, LoadedMacro::ProcMacro(ext) => clean::ProcMacroItem(clean::ProcMacro { kind: ext.macro_kind(), helpers: ext.helper_attrs, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 3edc2cd2e..5fa0c120f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -21,25 +21,24 @@ use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; use rustc_hir::PredicateOrigin; use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; +use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, AdtKind, DefIdTree, EarlyBinder, Ty, TyCtxt}; +use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, ExpnKind}; -use std::assert_matches::assert_matches; +use std::borrow::Cow; use std::collections::hash_map::Entry; use std::collections::BTreeMap; -use std::default::Default; use std::hash::Hash; use std::mem; use thin_vec::ThinVec; -use crate::clean::inline::merge_attrs; use crate::core::{self, DocContext, ImplTraitParam}; use crate::formats::item_type::ItemType; use crate::visit_ast::Module as DocModule; @@ -270,15 +269,7 @@ fn clean_where_predicate<'tcx>( let bound_params = wbp .bound_generic_params .iter() - .map(|param| { - // Higher-ranked params must be lifetimes. - // Higher-ranked lifetimes can't have bounds. - assert_matches!( - param, - hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. } - ); - Lifetime(param.name.ident().name) - }) + .map(|param| clean_generic_param(cx, None, param)) .collect(); WherePredicate::BoundPredicate { ty: clean_ty(wbp.bounded_ty, cx), @@ -324,7 +315,7 @@ pub(crate) fn clean_predicate<'tcx>( ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => None, ty::PredicateKind::Subtype(..) - | ty::PredicateKind::AliasEq(..) + | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) @@ -410,7 +401,7 @@ fn clean_projection_predicate<'tcx>( .collect_referenced_late_bound_regions(&pred) .into_iter() .filter_map(|br| match br { - ty::BrNamed(_, name) if br.is_named() => Some(Lifetime(name)), + ty::BrNamed(_, name) if br.is_named() => Some(GenericParamDef::lifetime(name)), _ => None, }) .collect(); @@ -427,7 +418,7 @@ fn clean_projection<'tcx>( cx: &mut DocContext<'tcx>, def_id: Option, ) -> Type { - if cx.tcx.def_kind(ty.skip_binder().def_id) == DefKind::ImplTraitPlaceholder { + if cx.tcx.is_impl_trait_in_trait(ty.skip_binder().def_id) { let bounds = cx .tcx .explicit_item_bounds(ty.skip_binder().def_id) @@ -508,7 +499,6 @@ fn clean_generic_param_def<'tcx>( ty::GenericParamDefKind::Const { has_default } => ( def.name, GenericParamDefKind::Const { - did: def.def_id, ty: Box::new(clean_middle_ty( ty::Binder::dummy( cx.tcx @@ -578,7 +568,6 @@ fn clean_generic_param<'tcx>( hir::GenericParamKind::Const { ty, default } => ( param.name.ident().name, GenericParamDefKind::Const { - did: param.def_id.to_def_id(), ty: Box::new(clean_ty(ty, cx)), default: default .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())), @@ -831,7 +820,7 @@ fn clean_ty_generics<'tcx>( p.get_bound_params() .into_iter() .flatten() - .map(|param| GenericParamDef::lifetime(param.0)) + .cloned() .collect(), )); } @@ -919,6 +908,38 @@ fn clean_ty_generics<'tcx>( } } +fn clean_proc_macro<'tcx>( + item: &hir::Item<'tcx>, + name: &mut Symbol, + kind: MacroKind, + cx: &mut DocContext<'tcx>, +) -> ItemKind { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + if kind == MacroKind::Derive && + let Some(derive_name) = attrs + .lists(sym::proc_macro_derive) + .find_map(|mi| mi.ident()) + { + *name = derive_name.name; + } + + let mut helpers = Vec::new(); + for mi in attrs.lists(sym::proc_macro_derive) { + if !mi.has_name(sym::attributes) { + continue; + } + + if let Some(list) = mi.meta_item_list() { + for inner_mi in list { + if let Some(ident) = inner_mi.ident() { + helpers.push(ident.name); + } + } + } + } + ProcMacroItem(ProcMacro { kind, helpers }) +} + fn clean_fn_or_proc_macro<'tcx>( item: &hir::Item<'tcx>, sig: &hir::FnSig<'tcx>, @@ -940,31 +961,7 @@ fn clean_fn_or_proc_macro<'tcx>( } }); match macro_kind { - Some(kind) => { - if kind == MacroKind::Derive { - *name = attrs - .lists(sym::proc_macro_derive) - .find_map(|mi| mi.ident()) - .expect("proc-macro derives require a name") - .name; - } - - let mut helpers = Vec::new(); - for mi in attrs.lists(sym::proc_macro_derive) { - if !mi.has_name(sym::attributes) { - continue; - } - - if let Some(list) = mi.meta_item_list() { - for inner_mi in list { - if let Some(ident) = inner_mi.ident() { - helpers.push(ident.name); - } - } - } - } - ProcMacroItem(ProcMacro { kind, helpers }) - } + Some(kind) => clean_proc_macro(item, name, kind, cx), None => { let mut func = clean_function(cx, sig, generics, FunctionArgs::Body(body_id)); clean_fn_decl_legacy_const_generics(&mut func, attrs); @@ -2013,7 +2010,8 @@ fn clean_generic_args<'tcx>( generic_args: &hir::GenericArgs<'tcx>, cx: &mut DocContext<'tcx>, ) -> GenericArgs { - if generic_args.parenthesized { + // FIXME(return_type_notation): Fix RTN parens rendering + if generic_args.parenthesized == hir::GenericArgsParentheses::ParenSugar { let output = clean_ty(generic_args.bindings[0].ty(), cx); let output = if output != Type::Tuple(Vec::new()) { Some(Box::new(output)) } else { None }; let inputs = @@ -2066,110 +2064,44 @@ fn clean_bare_fn_ty<'tcx>( BareFunctionDecl { unsafety: bare_fn.unsafety, abi: bare_fn.abi, decl, generic_params } } -/// This visitor is used to go through only the "top level" of a item and not enter any sub -/// item while looking for a given `Ident` which is stored into `item` if found. -struct OneLevelVisitor<'hir> { - map: rustc_middle::hir::map::Map<'hir>, - item: Option<&'hir hir::Item<'hir>>, - looking_for: Ident, +pub(crate) fn reexport_chain<'tcx>( + tcx: TyCtxt<'tcx>, + import_def_id: LocalDefId, target_def_id: LocalDefId, -} - -impl<'hir> OneLevelVisitor<'hir> { - fn new(map: rustc_middle::hir::map::Map<'hir>, target_def_id: LocalDefId) -> Self { - Self { map, item: None, looking_for: Ident::empty(), target_def_id } - } - - fn reset(&mut self, looking_for: Ident) { - self.looking_for = looking_for; - self.item = None; - } -} - -impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> { - type NestedFilter = rustc_middle::hir::nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.map - } - - fn visit_item(&mut self, item: &'hir hir::Item<'hir>) { - if self.item.is_none() - && item.ident == self.looking_for - && (matches!(item.kind, hir::ItemKind::Use(_, _)) - || item.owner_id.def_id == self.target_def_id) +) -> &'tcx [Reexport] { + for child in tcx.module_children_reexports(tcx.local_parent(import_def_id)) { + if child.res.opt_def_id() == Some(target_def_id.to_def_id()) + && child.reexport_chain[0].id() == Some(import_def_id.to_def_id()) { - self.item = Some(item); + return &child.reexport_chain; } } + &[] } -/// Because a `Use` item directly links to the imported item, we need to manually go through each -/// import one by one. To do so, we go to the parent item and look for the `Ident` into it. Then, -/// if we found the "end item" (the imported one), we stop there because we don't need its -/// documentation. Otherwise, we repeat the same operation until we find the "end item". +/// Collect attributes from the whole import chain. fn get_all_import_attributes<'hir>( - mut item: &hir::Item<'hir>, - tcx: TyCtxt<'hir>, + cx: &mut DocContext<'hir>, + import_def_id: LocalDefId, target_def_id: LocalDefId, - attributes: &mut Vec, is_inline: bool, -) { +) -> Vec<(Cow<'hir, ast::Attribute>, Option)> { + let mut attrs = Vec::new(); let mut first = true; - let hir_map = tcx.hir(); - let mut visitor = OneLevelVisitor::new(hir_map, target_def_id); - let mut visited = FxHashSet::default(); - - // If the item is an import and has at least a path with two parts, we go into it. - while let hir::ItemKind::Use(path, _) = item.kind && visited.insert(item.hir_id()) { + for def_id in reexport_chain(cx.tcx, import_def_id, target_def_id) + .iter() + .flat_map(|reexport| reexport.id()) + { + let import_attrs = inline::load_attrs(cx, def_id); if first { // This is the "original" reexport so we get all its attributes without filtering them. - attributes.extend_from_slice(hir_map.attrs(item.hir_id())); + attrs = import_attrs.iter().map(|attr| (Cow::Borrowed(attr), Some(def_id))).collect(); first = false; } else { - add_without_unwanted_attributes(attributes, hir_map.attrs(item.hir_id()), is_inline); - } - - let def_id = if let [.., parent_segment, _] = &path.segments { - match parent_segment.res { - hir::def::Res::Def(_, def_id) => def_id, - _ if parent_segment.ident.name == kw::Crate => { - // In case the "parent" is the crate, it'll give `Res::Err` so we need to - // circumvent it this way. - tcx.parent(item.owner_id.def_id.to_def_id()) - } - _ => break, - } - } else { - // If the path doesn't have a parent, then the parent is the current module. - tcx.parent(item.owner_id.def_id.to_def_id()) - }; - - let Some(parent) = hir_map.get_if_local(def_id) else { break }; - - // We get the `Ident` we will be looking for into `item`. - let looking_for = path.segments[path.segments.len() - 1].ident; - visitor.reset(looking_for); - - match parent { - hir::Node::Item(parent_item) => { - hir::intravisit::walk_item(&mut visitor, parent_item); - } - hir::Node::Crate(m) => { - hir::intravisit::walk_mod( - &mut visitor, - m, - tcx.local_def_id_to_hir_id(def_id.as_local().unwrap()), - ); - } - _ => break, - } - if let Some(i) = visitor.item { - item = i; - } else { - break; + add_without_unwanted_attributes(&mut attrs, import_attrs, is_inline, Some(def_id)); } } + attrs } fn filter_tokens_from_list( @@ -2215,17 +2147,24 @@ fn filter_tokens_from_list( /// * `doc(inline)` /// * `doc(no_inline)` /// * `doc(hidden)` -fn add_without_unwanted_attributes( - attrs: &mut Vec, - new_attrs: &[ast::Attribute], +fn add_without_unwanted_attributes<'hir>( + attrs: &mut Vec<(Cow<'hir, ast::Attribute>, Option)>, + new_attrs: &'hir [ast::Attribute], is_inline: bool, + import_parent: Option, ) { - // If it's `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything. + // If it's not `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything. if !is_inline { - attrs.extend_from_slice(new_attrs); + for attr in new_attrs { + attrs.push((Cow::Borrowed(attr), import_parent)); + } return; } for attr in new_attrs { + if matches!(attr.kind, ast::AttrKind::DocComment(..)) { + attrs.push((Cow::Borrowed(attr), import_parent)); + continue; + } let mut attr = attr.clone(); match attr.kind { ast::AttrKind::Normal(ref mut normal) => { @@ -2252,18 +2191,15 @@ fn add_without_unwanted_attributes( ) }); args.tokens = TokenStream::new(tokens); - attrs.push(attr); + attrs.push((Cow::Owned(attr), import_parent)); } ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => { - attrs.push(attr); - continue; + attrs.push((Cow::Owned(attr), import_parent)); } } } } - ast::AttrKind::DocComment(..) => { - attrs.push(attr); - } + _ => unreachable!(), } } } @@ -2318,16 +2254,17 @@ fn clean_maybe_renamed_item<'tcx>( fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx), - // proc macros can have a name set by attributes - ItemKind::Fn(ref sig, generics, body_id) => { - clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) - } - ItemKind::Macro(ref macro_def, _) => { + ItemKind::Macro(ref macro_def, MacroKind::Bang) => { let ty_vis = cx.tcx.visibility(def_id); MacroItem(Macro { source: display_macro_source(cx, name, macro_def, def_id, ty_vis), }) } + ItemKind::Macro(_, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx), + // proc macros can have a name set by attributes + ItemKind::Fn(ref sig, generics, body_id) => { + clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) + } ItemKind::Trait(_, _, generics, bounds, item_ids) => { let items = item_ids .iter() @@ -2350,26 +2287,28 @@ fn clean_maybe_renamed_item<'tcx>( _ => unreachable!("not yet converted"), }; - let mut import_attrs = Vec::new(); - let mut target_attrs = Vec::new(); - if let Some(import_id) = import_id && - let Some(hir::Node::Item(use_node)) = cx.tcx.hir().find_by_def_id(import_id) - { - let is_inline = inline::load_attrs(cx, import_id.to_def_id()).lists(sym::doc).get_word_attr(sym::inline).is_some(); - // Then we get all the various imports' attributes. - get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut import_attrs, is_inline); - add_without_unwanted_attributes(&mut target_attrs, inline::load_attrs(cx, def_id), is_inline); + let target_attrs = inline::load_attrs(cx, def_id); + let attrs = if let Some(import_id) = import_id { + let is_inline = inline::load_attrs(cx, import_id.to_def_id()) + .lists(sym::doc) + .get_word_attr(sym::inline) + .is_some(); + let mut attrs = + get_all_import_attributes(cx, import_id, item.owner_id.def_id, is_inline); + add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None); + attrs } else { // We only keep the item's attributes. - target_attrs.extend_from_slice(inline::load_attrs(cx, def_id)); - } + target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect() + }; - let import_parent = import_id.map(|import_id| cx.tcx.local_parent(import_id).to_def_id()); - let (attrs, cfg) = merge_attrs(cx, import_parent, &target_attrs, Some(&import_attrs)); + let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg); + let attrs = + Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false); let mut item = Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg); - item.inline_stmt_id = import_id.map(|def_id| def_id.to_def_id()); + item.inline_stmt_id = import_id.map(|local| local.to_def_id()); vec![item] }) } @@ -2450,22 +2389,17 @@ fn clean_extern_crate<'tcx>( Some(l) => attr::list_contains_name(&l, sym::inline), None => false, } - }); + }) + && !cx.output_format.is_json(); let krate_owner_def_id = krate.owner_id.to_def_id(); if please_inline { - let mut visited = DefIdSet::default(); - - let res = Res::Def(DefKind::Mod, crate_def_id); - if let Some(items) = inline::try_inline( cx, - cx.tcx.parent_module(krate.hir_id()).to_def_id(), - Some(krate_owner_def_id), - res, + Res::Def(DefKind::Mod, crate_def_id), name, - Some(attrs), - &mut visited, + Some((attrs, Some(krate_owner_def_id))), + &mut Default::default(), ) { return items; } @@ -2589,17 +2523,13 @@ fn clean_use_statement_inner<'tcx>( denied = true; } if !denied { - let mut visited = DefIdSet::default(); let import_def_id = import.owner_id.to_def_id(); - if let Some(mut items) = inline::try_inline( cx, - cx.tcx.parent_module(import.hir_id()).to_def_id(), - Some(import_def_id), path.res, name, - Some(attrs), - &mut visited, + Some((attrs, Some(import_def_id))), + &mut Default::default(), ) { items.push(Item::from_def_id_and_parts( import_def_id, diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index dbbc25739..3c72b0bf9 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -49,11 +49,7 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> ThinVec { as_keyword(Res::Def(DefKind::Mod, id.owner_id.to_def_id())) } - hir::ItemKind::Use(path, hir::UseKind::Single) - if tcx.visibility(id.owner_id).is_public() => - { - path.res - .iter() - .find_map(|res| as_keyword(res.expect_non_local())) - .map(|(_, prim)| (id.owner_id.to_def_id(), prim)) - } _ => None, } }) @@ -256,38 +248,24 @@ impl ExternalCrate { // // Note that this loop only searches the top-level items of the crate, // and this is intentional. If we were to search the entire crate for an - // item tagged with `#[doc(primitive)]` then we would also have to + // item tagged with `#[rustc_doc_primitive]` then we would also have to // search the entirety of external modules for items tagged - // `#[doc(primitive)]`, which is a pretty inefficient process (decoding + // `#[rustc_doc_primitive]`, which is a pretty inefficient process (decoding // all that metadata unconditionally). // // In order to keep the metadata load under control, the - // `#[doc(primitive)]` feature is explicitly designed to only allow the + // `#[rustc_doc_primitive]` feature is explicitly designed to only allow the // primitive tags to show up as the top level items in a crate. // // Also note that this does not attempt to deal with modules tagged // duplicately for the same primitive. This is handled later on when // rendering by delegating everything to a hash map. let as_primitive = |res: Res| { - if let Res::Def(DefKind::Mod, def_id) = res { - let mut prim = None; - let meta_items = tcx - .get_attrs(def_id, sym::doc) - .flat_map(|attr| attr.meta_item_list().unwrap_or_default()); - for meta in meta_items { - if let Some(v) = meta.value_str() { - if meta.has_name(sym::primitive) { - prim = PrimitiveType::from_symbol(v); - if prim.is_some() { - break; - } - // FIXME: should warn on unknown primitives? - } - } - } - return prim.map(|p| (def_id, p)); - } - None + let Res::Def(DefKind::Mod, def_id) = res else { return None }; + tcx.get_attrs(def_id, sym::rustc_doc_primitive).find_map(|attr| { + // FIXME: should warn on unknown primitives? + Some((def_id, PrimitiveType::from_symbol(attr.value_str()?)?)) + }) }; if root.is_local() { @@ -301,15 +279,6 @@ impl ExternalCrate { hir::ItemKind::Mod(_) => { as_primitive(Res::Def(DefKind::Mod, id.owner_id.to_def_id())) } - hir::ItemKind::Use(path, hir::UseKind::Single) - if tcx.visibility(id.owner_id).is_public() => - { - path.res - .iter() - .find_map(|res| as_primitive(res.expect_non_local())) - // Pretend the primitive is local. - .map(|(_, prim)| (id.owner_id.to_def_id(), prim)) - } _ => None, } }) @@ -482,10 +451,12 @@ impl Item { pub(crate) fn links(&self, cx: &Context<'_>) -> Vec { use crate::html::format::{href, link_tooltip}; - cx.cache() + let Some(links) = cx.cache() .intra_doc_links - .get(&self.item_id) - .map_or(&[][..], |v| v.as_slice()) + .get(&self.item_id) else { + return vec![] + }; + links .iter() .filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| { debug!(?id); @@ -513,10 +484,12 @@ impl Item { /// the link text, but does need to know which `[]`-bracketed names /// are actually links. pub(crate) fn link_names(&self, cache: &Cache) -> Vec { - cache + let Some(links) = cache .intra_doc_links - .get(&self.item_id) - .map_or(&[][..], |v| v.as_slice()) + .get(&self.item_id) else { + return vec![]; + }; + links .iter() .map(|ItemLink { link: s, link_text, .. }| RenderedLink { original_text: s.clone(), @@ -713,7 +686,7 @@ impl Item { return None; } // Variants always inherit visibility - VariantItem(..) => return None, + VariantItem(..) | ImplItem(..) => return None, // Trait items inherit the trait's visibility AssocConstItem(..) | TyAssocConstItem(..) | AssocTypeItem(..) | TyAssocTypeItem(..) | TyMethodItem(..) | MethodItem(..) => { @@ -867,30 +840,15 @@ pub(crate) struct Module { pub(crate) trait AttributesExt { type AttributeIterator<'a>: Iterator + where + Self: 'a; + type Attributes<'a>: Iterator where Self: 'a; fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a>; - fn span(&self) -> Option; - - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet) -> Option>; -} - -impl AttributesExt for [ast::Attribute] { - type AttributeIterator<'a> = impl Iterator + 'a; - - fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> { - self.iter() - .filter(move |attr| attr.has_name(name)) - .filter_map(ast::Attribute::meta_item_list) - .flatten() - } - - /// Return the span of the first doc-comment, if it exists. - fn span(&self) -> Option { - self.iter().find(|attr| attr.doc_str().is_some()).map(|attr| attr.span) - } + fn iter<'a>(&'a self) -> Self::Attributes<'a>; fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet) -> Option> { let sess = tcx.sess; @@ -980,11 +938,48 @@ impl AttributesExt for [ast::Attribute] { } } +impl AttributesExt for [ast::Attribute] { + type AttributeIterator<'a> = impl Iterator + 'a; + type Attributes<'a> = impl Iterator + 'a; + + fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> { + self.iter() + .filter(move |attr| attr.has_name(name)) + .filter_map(ast::Attribute::meta_item_list) + .flatten() + } + + fn iter<'a>(&'a self) -> Self::Attributes<'a> { + self.into_iter() + } +} + +impl AttributesExt for [(Cow<'_, ast::Attribute>, Option)] { + type AttributeIterator<'a> = impl Iterator + 'a + where Self: 'a; + type Attributes<'a> = impl Iterator + 'a + where Self: 'a; + + fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> { + AttributesExt::iter(self) + .filter(move |attr| attr.has_name(name)) + .filter_map(ast::Attribute::meta_item_list) + .flatten() + } + + fn iter<'a>(&'a self) -> Self::Attributes<'a> { + self.into_iter().map(move |(attr, _)| match attr { + Cow::Borrowed(attr) => *attr, + Cow::Owned(attr) => attr, + }) + } +} + pub(crate) trait NestedAttributesExt { /// Returns `true` if the attribute list contains a specific `word` fn has_word(self, word: Symbol) -> bool where - Self: std::marker::Sized, + Self: Sized, { ::get_word_attr(self, word).is_some() } @@ -1014,7 +1009,7 @@ pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { /// A link that has not yet been rendered. /// /// This link will be turned into a rendered link by [`Item::links`]. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) struct ItemLink { /// The original link written in the markdown pub(crate) link: Box, @@ -1213,9 +1208,9 @@ impl Lifetime { #[derive(Clone, Debug)] pub(crate) enum WherePredicate { - BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, + BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec }, - EqPredicate { lhs: Box, rhs: Box, bound_params: Vec }, + EqPredicate { lhs: Box, rhs: Box, bound_params: Vec }, } impl WherePredicate { @@ -1227,7 +1222,7 @@ impl WherePredicate { } } - pub(crate) fn get_bound_params(&self) -> Option<&[Lifetime]> { + pub(crate) fn get_bound_params(&self) -> Option<&[GenericParamDef]> { match self { Self::BoundPredicate { bound_params, .. } | Self::EqPredicate { bound_params, .. } => { Some(bound_params) @@ -1241,7 +1236,7 @@ impl WherePredicate { pub(crate) enum GenericParamDefKind { Lifetime { outlives: Vec }, Type { did: DefId, bounds: Vec, default: Option>, synthetic: bool }, - Const { did: DefId, ty: Box, default: Option> }, + Const { ty: Box, default: Option> }, } impl GenericParamDefKind { @@ -1471,27 +1466,68 @@ impl Type { result } - /// Check if two types are "potentially the same". + pub(crate) fn is_borrowed_ref(&self) -> bool { + matches!(self, Type::BorrowedRef { .. }) + } + + /// Check if two types are "the same" for documentation purposes. + /// /// This is different from `Eq`, because it knows that things like /// `Placeholder` are possible matches for everything. - pub(crate) fn is_same(&self, other: &Self, cache: &Cache) -> bool { - match (self, other) { + /// + /// This relation is not commutative when generics are involved: + /// + /// ```ignore(private) + /// # // see types/tests.rs:is_same_generic for the real test + /// use rustdoc::format::cache::Cache; + /// use rustdoc::clean::types::{Type, PrimitiveType}; + /// let cache = Cache::new(false); + /// let generic = Type::Generic(rustc_span::symbol::sym::Any); + /// let unit = Type::Primitive(PrimitiveType::Unit); + /// assert!(!generic.is_same(&unit, &cache)); + /// assert!(unit.is_same(&generic, &cache)); + /// ``` + /// + /// An owned type is also the same as its borrowed variants (this is commutative), + /// but `&T` is not the same as `&mut T`. + pub(crate) fn is_doc_subtype_of(&self, other: &Self, cache: &Cache) -> bool { + // Strip the references so that it can compare the actual types, unless both are references. + // If both are references, leave them alone and compare the mutabilities later. + let (self_cleared, other_cleared) = if !self.is_borrowed_ref() || !other.is_borrowed_ref() { + (self.without_borrowed_ref(), other.without_borrowed_ref()) + } else { + (self, other) + }; + match (self_cleared, other_cleared) { // Recursive cases. (Type::Tuple(a), Type::Tuple(b)) => { - a.len() == b.len() && a.iter().zip(b).all(|(a, b)| a.is_same(b, cache)) + a.len() == b.len() && a.iter().zip(b).all(|(a, b)| a.is_doc_subtype_of(b, cache)) } - (Type::Slice(a), Type::Slice(b)) => a.is_same(b, cache), - (Type::Array(a, al), Type::Array(b, bl)) => al == bl && a.is_same(b, cache), + (Type::Slice(a), Type::Slice(b)) => a.is_doc_subtype_of(b, cache), + (Type::Array(a, al), Type::Array(b, bl)) => al == bl && a.is_doc_subtype_of(b, cache), (Type::RawPointer(mutability, type_), Type::RawPointer(b_mutability, b_type_)) => { - mutability == b_mutability && type_.is_same(b_type_, cache) + mutability == b_mutability && type_.is_doc_subtype_of(b_type_, cache) } ( Type::BorrowedRef { mutability, type_, .. }, Type::BorrowedRef { mutability: b_mutability, type_: b_type_, .. }, - ) => mutability == b_mutability && type_.is_same(b_type_, cache), - // Placeholders and generics are equal to all other types. + ) => mutability == b_mutability && type_.is_doc_subtype_of(b_type_, cache), + // Placeholders are equal to all other types. (Type::Infer, _) | (_, Type::Infer) => true, - (Type::Generic(_), _) | (_, Type::Generic(_)) => true, + // Generics match everything on the right, but not on the left. + // If both sides are generic, this returns true. + (_, Type::Generic(_)) => true, + (Type::Generic(_), _) => false, + // Paths account for both the path itself and its generics. + (Type::Path { path: a }, Type::Path { path: b }) => { + a.def_id() == b.def_id() + && a.generics() + .zip(b.generics()) + .map(|(ag, bg)| { + ag.iter().zip(bg.iter()).all(|(at, bt)| at.is_doc_subtype_of(bt, cache)) + }) + .unwrap_or(true) + } // Other cases, such as primitives, just use recursion. (a, b) => a .def_id(cache) @@ -1782,13 +1818,17 @@ impl PrimitiveType { } } - /// Returns the DefId of the module with `doc(primitive)` for this primitive type. + /// Returns the DefId of the module with `rustc_doc_primitive` for this primitive type. /// Panics if there is no such module. /// - /// This gives precedence to primitives defined in the current crate, and deprioritizes primitives defined in `core`, - /// but otherwise, if multiple crates define the same primitive, there is no guarantee of which will be picked. - /// In particular, if a crate depends on both `std` and another crate that also defines `doc(primitive)`, then - /// it's entirely random whether `std` or the other crate is picked. (no_std crates are usually fine unless multiple dependencies define a primitive.) + /// This gives precedence to primitives defined in the current crate, and deprioritizes + /// primitives defined in `core`, + /// but otherwise, if multiple crates define the same primitive, there is no guarantee of which + /// will be picked. + /// + /// In particular, if a crate depends on both `std` and another crate that also defines + /// `rustc_doc_primitive`, then it's entirely random whether `std` or the other crate is picked. + /// (no_std crates are usually fine unless multiple dependencies define a primitive.) pub(crate) fn primitive_locations(tcx: TyCtxt<'_>) -> &FxHashMap { static PRIMITIVE_LOCATIONS: OnceCell> = OnceCell::new(); PRIMITIVE_LOCATIONS.get_or_init(|| { @@ -1978,7 +2018,7 @@ impl Variant { #[derive(Clone, Debug)] pub(crate) struct Discriminant { - // In the case of cross crate re-exports, we don't have the nessesary information + // In the case of cross crate re-exports, we don't have the necessary information // to reconstruct the expression of the discriminant, only the value. pub(super) expr: Option, pub(super) value: DefId, diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs index 20627c2cf..d8c91a968 100644 --- a/src/librustdoc/clean/types/tests.rs +++ b/src/librustdoc/clean/types/tests.rs @@ -10,7 +10,7 @@ use rustc_span::symbol::Symbol; fn create_doc_fragment(s: &str) -> Vec { vec![DocFragment { span: DUMMY_SP, - parent_module: None, + item_id: None, doc: Symbol::intern(s), kind: DocFragmentKind::SugaredDoc, indent: 0, @@ -69,3 +69,14 @@ fn should_not_trim() { run_test("\t line1 \n\t line2", "line1 \nline2"); run_test(" \tline1 \n \tline2", "line1 \nline2"); } + +#[test] +fn is_same_generic() { + use crate::clean::types::{PrimitiveType, Type}; + use crate::formats::cache::Cache; + let cache = Cache::new(false); + let generic = Type::Generic(rustc_span::symbol::sym::Any); + let unit = Type::Primitive(PrimitiveType::Unit); + assert!(!generic.is_doc_subtype_of(&unit, &cache)); + assert!(unit.is_doc_subtype_of(&generic, &cache)); +} diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index c9c1c2c45..cca50df0d 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -17,7 +17,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::mir; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use std::fmt::Write as _; use std::mem; @@ -195,12 +195,12 @@ pub(crate) fn build_deref_target_impls( if let Some(prim) = target.primitive_type() { let _prof_timer = cx.tcx.sess.prof.generic_activity("build_primitive_inherent_impls"); for did in prim.impls(tcx).filter(|did| !did.is_local()) { - inline::build_impl(cx, None, did, None, ret); + inline::build_impl(cx, did, None, ret); } } else if let Type::Path { path } = target { let did = path.def_id(); if !did.is_local() { - inline::build_impls(cx, None, did, None, ret); + inline::build_impls(cx, did, None, ret); } } } -- cgit v1.2.3