diff options
Diffstat (limited to 'src/librustdoc')
67 files changed, 1946 insertions, 1087 deletions
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 92bae5516..a06f31a93 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -46,7 +46,7 @@ where let tcx = self.cx.tcx; let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty])); if !self.cx.generated_synthetics.insert((ty, trait_def_id)) { - debug!("get_auto_trait_impl_for({:?}): already generated, aborting", trait_ref); + debug!("get_auto_trait_impl_for({trait_ref:?}): already generated, aborting"); return None; } @@ -137,10 +137,10 @@ where pub(crate) fn get_auto_trait_impls(&mut self, item_def_id: DefId) -> Vec<Item> { let tcx = self.cx.tcx; let param_env = tcx.param_env(item_def_id); - let ty = tcx.type_of(item_def_id).subst_identity(); + let ty = tcx.type_of(item_def_id).instantiate_identity(); let f = auto_trait::AutoTraitFinder::new(tcx); - debug!("get_auto_trait_impls({:?})", ty); + debug!("get_auto_trait_impls({ty:?})"); let auto_traits: Vec<_> = self.cx.auto_traits.to_vec(); let mut auto_traits: Vec<Item> = auto_traits .into_iter() @@ -163,9 +163,9 @@ where fn get_lifetime(region: Region<'_>, names_map: &FxHashMap<Symbol, Lifetime>) -> Lifetime { region_name(region) .map(|name| { - names_map.get(&name).unwrap_or_else(|| { - panic!("Missing lifetime with name {:?} for {:?}", name.as_str(), region) - }) + names_map + .get(&name) + .unwrap_or_else(|| panic!("Missing lifetime with name {name:?} for {region:?}")) }) .unwrap_or(&Lifetime::statik()) .clone() @@ -372,7 +372,7 @@ where let output = output.as_ref().cloned().map(Box::new); if old_output.is_some() && old_output != output { - panic!("Output mismatch for {:?} {:?} {:?}", ty, old_output, output); + panic!("Output mismatch for {ty:?} {old_output:?} {output:?}"); } let new_params = GenericArgs::Parenthesized { inputs: old_input, output }; @@ -462,7 +462,7 @@ where ); let mut generic_params = raw_generics.params; - debug!("param_env_to_generics({:?}): generic_params={:?}", item_def_id, generic_params); + debug!("param_env_to_generics({item_def_id:?}): generic_params={generic_params:?}"); let mut has_sized = FxHashSet::default(); let mut ty_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default(); @@ -623,7 +623,7 @@ where // loop ty_to_traits.entry(ty.clone()).or_default().insert(trait_.clone()); } - _ => panic!("Unexpected LHS {:?} for {:?}", lhs, item_def_id), + _ => panic!("Unexpected LHS {lhs:?} for {item_def_id:?}"), } } }; @@ -710,7 +710,7 @@ where /// involved (impls rarely have more than a few bounds) means that it /// shouldn't matter in practice. fn unstable_debug_sort<T: Debug>(&self, vec: &mut [T]) { - vec.sort_by_cached_key(|x| format!("{:?}", x)) + vec.sort_by_cached_key(|x| format!("{x:?}")) } fn is_fn_trait(&self, path: &Path) -> bool { diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index a36041588..dad2aa406 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -17,7 +17,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { let param_env = cx.tcx.param_env(item_def_id); let ty = cx.tcx.type_of(item_def_id); - trace!("get_blanket_impls({:?})", ty); + trace!("get_blanket_impls({ty:?})"); let mut impls = Vec::new(); for trait_def_id in cx.tcx.all_traits() { if !cx.cache.effective_visibilities.is_reachable(cx.tcx, trait_def_id) @@ -38,18 +38,22 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { continue; } let infcx = cx.tcx.infer_ctxt().build(); - let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id); - let impl_ty = ty.subst(infcx.tcx, substs); - let param_env = EarlyBinder::bind(param_env).subst(infcx.tcx, substs); + let args = infcx.fresh_args_for_item(DUMMY_SP, item_def_id); + let impl_ty = ty.instantiate(infcx.tcx, args); + let param_env = EarlyBinder::bind(param_env).instantiate(infcx.tcx, args); - let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); - let impl_trait_ref = trait_ref.subst(infcx.tcx, impl_substs); + let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = trait_ref.instantiate(infcx.tcx, impl_args); // Require the type the impl is implemented on to match // our type, and ignore the impl if there was a mismatch. - let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq(DefineOpaqueTypes::No, impl_trait_ref.self_ty(), impl_ty) else { - continue - }; + 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; // FIXME(eddyb) ignoring `obligations` might cause false positives. drop(obligations); @@ -63,12 +67,12 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { let predicates = cx .tcx .predicates_of(impl_def_id) - .instantiate(cx.tcx, impl_substs) + .instantiate(cx.tcx, impl_args) .predicates .into_iter() .chain(Some(ty::Binder::dummy(impl_trait_ref).to_predicate(infcx.tcx))); for predicate in predicates { - debug!("testing predicate {:?}", predicate); + debug!("testing predicate {predicate:?}"); let obligation = traits::Obligation::new( infcx.tcx, traits::ObligationCause::dummy(), @@ -104,11 +108,11 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { // the post-inference `trait_ref`, as it's more accurate. trait_: Some(clean_trait_ref_with_bindings( cx, - ty::Binder::dummy(trait_ref.subst_identity()), + ty::Binder::dummy(trait_ref.instantiate_identity()), ThinVec::new(), )), for_: clean_middle_ty( - ty::Binder::dummy(ty.subst_identity()), + ty::Binder::dummy(ty.instantiate_identity()), cx, None, None, @@ -117,11 +121,12 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .tcx .associated_items(impl_def_id) .in_definition_order() - .map(|x| clean_middle_assoc_item(x, cx)) + .filter(|item| !item.is_impl_trait_in_trait()) + .map(|item| clean_middle_assoc_item(item, cx)) .collect::<Vec<_>>(), polarity: ty::ImplPolarity::Positive, kind: ImplKind::Blanket(Box::new(clean_middle_ty( - ty::Binder::dummy(trait_ref.subst_identity().self_ty()), + ty::Binder::dummy(trait_ref.instantiate_identity().self_ty()), cx, None, None, diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 5177cffe6..ab5aec12f 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -434,9 +434,9 @@ impl<'a> fmt::Display for Display<'a> { } if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) { if self.1.is_html() { - write!(fmt, "<code>{}</code>", feat)?; + write!(fmt, "<code>{feat}</code>")?; } else { - write!(fmt, "`{}`", feat)?; + write!(fmt, "`{feat}`")?; } } else { write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?; @@ -471,9 +471,9 @@ impl<'a> fmt::Display for Display<'a> { } if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) { if self.1.is_html() { - write!(fmt, "<code>{}</code>", feat)?; + write!(fmt, "<code>{feat}</code>")?; } else { - write!(fmt, "`{}`", feat)?; + write!(fmt, "`{feat}`")?; } } else { write_with_opt_paren(fmt, !sub_cfg.is_simple(), Display(sub_cfg, self.1))?; @@ -519,8 +519,11 @@ impl<'a> fmt::Display for Display<'a> { "asmjs" => "JavaScript", "loongarch64" => "LoongArch LA64", "m68k" => "M68k", + "csky" => "CSKY", "mips" => "MIPS", + "mips32r6" => "MIPS Release 6", "mips64" => "MIPS-64", + "mips64r6" => "MIPS-64 Release 6", "msp430" => "MSP430", "powerpc" => "PowerPC", "powerpc64" => "PowerPC-64", @@ -549,21 +552,21 @@ impl<'a> fmt::Display for Display<'a> { "sgx" => "SGX", _ => "", }, - (sym::target_endian, Some(endian)) => return write!(fmt, "{}-endian", endian), - (sym::target_pointer_width, Some(bits)) => return write!(fmt, "{}-bit", bits), + (sym::target_endian, Some(endian)) => return write!(fmt, "{endian}-endian"), + (sym::target_pointer_width, Some(bits)) => return write!(fmt, "{bits}-bit"), (sym::target_feature, Some(feat)) => match self.1 { Format::LongHtml => { - return write!(fmt, "target feature <code>{}</code>", feat); + return write!(fmt, "target feature <code>{feat}</code>"); } - Format::LongPlain => return write!(fmt, "target feature `{}`", feat), - Format::ShortHtml => return write!(fmt, "<code>{}</code>", feat), + Format::LongPlain => return write!(fmt, "target feature `{feat}`"), + Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"), }, (sym::feature, Some(feat)) => match self.1 { Format::LongHtml => { - return write!(fmt, "crate feature <code>{}</code>", feat); + return write!(fmt, "crate feature <code>{feat}</code>"); } - Format::LongPlain => return write!(fmt, "crate feature `{}`", feat), - Format::ShortHtml => return write!(fmt, "<code>{}</code>", feat), + Format::LongPlain => return write!(fmt, "crate feature `{feat}`"), + Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"), }, _ => "", }; @@ -578,12 +581,12 @@ impl<'a> fmt::Display for Display<'a> { Escape(v.as_str()) ) } else { - write!(fmt, r#"`{}="{}"`"#, name, v) + write!(fmt, r#"`{name}="{v}"`"#) } } else if self.1.is_html() { write!(fmt, "<code>{}</code>", Escape(name.as_str())) } else { - write!(fmt, "`{}`", name) + write!(fmt, "`{name}`") } } } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 870cfa930..cac211307 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -9,9 +9,10 @@ use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId}; +use rustc_hir::def_id::{DefId, DefIdSet, LocalModDefId}; use rustc_hir::Mutability; use rustc_metadata::creader::{CStore, LoadedMacro}; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; @@ -49,7 +50,7 @@ pub(crate) fn try_inline( } let mut ret = Vec::new(); - debug!("attrs={:?}", attrs); + debug!("attrs={attrs:?}"); let attrs_without_docs = attrs.map(|(attrs, def_id)| { (attrs.into_iter().filter(|a| a.doc_str().is_none()).cloned().collect::<Vec<_>>(), def_id) @@ -78,7 +79,7 @@ pub(crate) fn try_inline( build_impls(cx, did, attrs_without_docs, &mut ret); clean::UnionItem(build_union(cx, did)) } - Res::Def(DefKind::TyAlias, did) => { + Res::Def(DefKind::TyAlias { .. }, did) => { record_extern_fqn(cx, did, ItemType::Typedef); build_impls(cx, did, attrs_without_docs, &mut ret); clean::TypedefItem(build_type_alias(cx, did)) @@ -137,9 +138,10 @@ pub(crate) fn try_inline( pub(crate) fn try_inline_glob( cx: &mut DocContext<'_>, res: Res, - current_mod: LocalDefId, + current_mod: LocalModDefId, visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, + import: &hir::Item<'_>, ) -> Option<Vec<clean::Item>> { let did = res.opt_def_id()?; if did.is_local() { @@ -152,12 +154,20 @@ pub(crate) fn try_inline_glob( // reexported by the glob, e.g. because they are shadowed by something else. let reexports = cx .tcx - .module_children_local(current_mod) + .module_children_local(current_mod.to_local_def_id()) .iter() .filter(|child| !child.reexport_chain.is_empty()) .filter_map(|child| child.res.opt_def_id()) .collect(); - let mut items = build_module_items(cx, did, visited, inlined_names, Some(&reexports)); + let attrs = cx.tcx.hir().attrs(import.hir_id()); + let mut items = build_module_items( + cx, + did, + visited, + inlined_names, + Some(&reexports), + Some((attrs, Some(import.owner_id.def_id.to_def_id()))), + ); items.retain(|item| { if let Some(name) = item.name { // If an item with the same type and name already exists, @@ -190,7 +200,7 @@ pub(crate) fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemT let fqn = if let ItemType::Macro = kind { // Check to see if it is a macro 2.0 or built-in macro if matches!( - CStore::from_tcx(cx.tcx).load_macro_untracked(did, cx.sess()), + CStore::from_tcx(cx.tcx).load_macro_untracked(did, cx.tcx), LoadedMacro::MacroDef(def, _) if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) @@ -215,6 +225,7 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean .tcx .associated_items(did) .in_definition_order() + .filter(|item| !item.is_impl_trait_in_trait()) .map(|item| clean_middle_assoc_item(item, cx)) .collect(); @@ -226,7 +237,7 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean } fn build_external_function<'tcx>(cx: &mut DocContext<'tcx>, did: DefId) -> Box<clean::Function> { - let sig = cx.tcx.fn_sig(did).subst_identity(); + let sig = cx.tcx.fn_sig(did).instantiate_identity(); let late_bound_regions = sig.bound_vars().into_iter().filter_map(|var| match var { ty::BoundVariableKind::Region(ty::BrNamed(_, name)) if name != kw::UnderscoreLifetime => { @@ -279,7 +290,7 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::Typedef> { let predicates = cx.tcx.explicit_predicates_of(did); let type_ = clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), + ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()), cx, Some(did), None, @@ -314,9 +325,8 @@ pub(crate) fn build_impls( // * https://github.com/rust-lang/rust/pull/99917 — where the feature got used // * https://github.com/rust-lang/rust/issues/53487 — overall tracking issue for Error if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { - use rustc_middle::ty::fast_reject::SimplifiedType::*; let type_ = - if tcx.is_trait(did) { TraitSimplifiedType(did) } else { AdtSimplifiedType(did) }; + if tcx.is_trait(did) { SimplifiedType::Trait(did) } else { SimplifiedType::Adt(did) }; for &did in tcx.incoherent_impls(type_) { build_impl(cx, did, attrs, ret); } @@ -391,7 +401,7 @@ pub(crate) fn build_impl( let for_ = match &impl_item { Some(impl_) => clean_ty(impl_.self_ty, cx), None => clean_middle_ty( - ty::Binder::dummy(tcx.type_of(did).subst_identity()), + ty::Binder::dummy(tcx.type_of(did).instantiate_identity()), cx, Some(did), None, @@ -459,6 +469,7 @@ pub(crate) fn build_impl( None => ( tcx.associated_items(did) .in_definition_order() + .filter(|item| !item.is_impl_trait_in_trait()) .filter(|item| { // If this is a trait impl, filter out associated items whose corresponding item // in the associated trait is marked `doc(hidden)`. @@ -473,7 +484,7 @@ pub(crate) fn build_impl( associated_trait.def_id, ) .unwrap(); // corresponding associated item has to exist - !tcx.is_doc_hidden(trait_item.def_id) + document_hidden || !tcx.is_doc_hidden(trait_item.def_id) } else { item.visibility(tcx).is_public() } @@ -496,7 +507,7 @@ pub(crate) fn build_impl( let mut stack: Vec<&Type> = vec![&for_]; if let Some(did) = trait_.as_ref().map(|t| t.def_id()) { - if tcx.is_doc_hidden(did) { + if !document_hidden && tcx.is_doc_hidden(did) { return; } } @@ -505,7 +516,7 @@ pub(crate) fn build_impl( } while let Some(ty) = stack.pop() { - if let Some(did) = ty.def_id(&cx.cache) && tcx.is_doc_hidden(did) { + if let Some(did) = ty.def_id(&cx.cache) && !document_hidden && tcx.is_doc_hidden(did) { return; } if let Some(generics) = ty.generics() { @@ -518,7 +529,7 @@ pub(crate) fn build_impl( } let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs); - trace!("merged_attrs={:?}", merged_attrs); + trace!("merged_attrs={merged_attrs:?}"); trace!( "build_impl: impl {:?} for {:?}", @@ -547,7 +558,7 @@ pub(crate) fn build_impl( } fn build_module(cx: &mut DocContext<'_>, did: DefId, visited: &mut DefIdSet) -> clean::Module { - let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None); + let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None, None); let span = clean::Span::new(cx.tcx.def_span(did)); clean::Module { items, span } @@ -559,6 +570,7 @@ fn build_module_items( visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, allowed_def_ids: Option<&DefIdSet>, + attrs: Option<(&[ast::Attribute], Option<DefId>)>, ) -> Vec<clean::Item> { let mut items = Vec::new(); @@ -613,7 +625,7 @@ fn build_module_items( cfg: None, inline_stmt_id: None, }); - } else if let Some(i) = try_inline(cx, res, item.ident.name, None, visited) { + } else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) { items.extend(i) } } @@ -632,13 +644,18 @@ pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String { } fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { + let mut generics = + clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id)); + clean::simplify::move_bounds_to_generic_parameters(&mut generics); + clean::Constant { type_: clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), + ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), cx, Some(def_id), None, ), + generics: Box::new(generics), kind: clean::ConstantKind::Extern { def_id }, } } @@ -646,7 +663,7 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::Static { clean::Static { type_: clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), + ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()), cx, Some(did), None, @@ -663,7 +680,7 @@ fn build_macro( import_def_id: Option<DefId>, macro_kind: MacroKind, ) -> clean::ItemKind { - match CStore::from_tcx(cx.tcx).load_macro_untracked(def_id, cx.sess()) { + match CStore::from_tcx(cx.tcx).load_macro_untracked(def_id, cx.tcx) { LoadedMacro::MacroDef(item_def, _) => match macro_kind { MacroKind::Bang => { if let ast::ItemKind::MacroDef(ref def) = item_def.kind { @@ -764,7 +781,7 @@ pub(crate) fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) { cx.active_extern_traits.insert(did); } - debug!("record_extern_trait: {:?}", did); + debug!("record_extern_trait: {did:?}"); let trait_ = build_external_trait(cx, did); cx.external_traits.borrow_mut().insert(did, trait_); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8ad1ed095..c9a05460b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -24,7 +24,6 @@ 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, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -54,7 +53,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< let mut inserted = FxHashSet::default(); items.extend(doc.foreigns.iter().map(|(item, renamed)| { let item = clean_maybe_renamed_foreign_item(cx, item, *renamed); - if let Some(name) = item.name && !item.is_doc_hidden() { + if let Some(name) = item.name && (cx.render_options.document_hidden || !item.is_doc_hidden()) { inserted.insert((item.type_(), name)); } item @@ -64,7 +63,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< return None; } let item = clean_doc_module(x, cx); - if item.is_doc_hidden() { + if !cx.render_options.document_hidden && item.is_doc_hidden() { // Hidden modules are stripped at a later stage. // If a hidden module has the same name as a visible one, we want // to keep both of them around. @@ -85,12 +84,25 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< } let v = clean_maybe_renamed_item(cx, item, *renamed, *import_id); for item in &v { - if let Some(name) = item.name && !item.is_doc_hidden() { + if let Some(name) = item.name && (cx.render_options.document_hidden || !item.is_doc_hidden()) { inserted.insert((item.type_(), name)); } } v })); + items.extend(doc.inlined_foreigns.iter().flat_map(|((_, renamed), (res, local_import_id))| { + let Some(def_id) = res.opt_def_id() else { return Vec::new() }; + let name = renamed.unwrap_or_else(|| cx.tcx.item_name(def_id)); + let import = cx.tcx.hir().expect_item(*local_import_id); + match import.kind { + hir::ItemKind::Use(path, kind) => { + let hir::UsePath { segments, span, .. } = *path; + let path = hir::Path { segments, res: *res, span }; + clean_use_statement_inner(import, name, &path, kind, cx, &mut Default::default()) + } + _ => unreachable!(), + } + })); items.extend(doc.items.values().flat_map(|(item, renamed, _)| { // Now we actually lower the imports, skipping everything else. if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { @@ -120,25 +132,31 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< }); let kind = ModuleItem(Module { items, span }); - generate_item_with_correct_attrs(cx, kind, doc.def_id, doc.name, doc.import_id, doc.renamed) + generate_item_with_correct_attrs( + cx, + kind, + doc.def_id.to_def_id(), + doc.name, + doc.import_id, + doc.renamed, + ) } fn generate_item_with_correct_attrs( cx: &mut DocContext<'_>, kind: ItemKind, - local_def_id: LocalDefId, + def_id: DefId, name: Symbol, import_id: Option<LocalDefId>, renamed: Option<Symbol>, ) -> Item { - let def_id = local_def_id.to_def_id(); 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, local_def_id, is_inline); + let mut attrs = get_all_import_attributes(cx, import_id, def_id, is_inline); add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None); attrs } else { @@ -197,11 +215,11 @@ pub(crate) fn clean_trait_ref_with_bindings<'tcx>( ) -> Path { let kind = cx.tcx.def_kind(trait_ref.def_id()).into(); if !matches!(kind, ItemType::Trait | ItemType::TraitAlias) { - span_bug!(cx.tcx.def_span(trait_ref.def_id()), "`TraitRef` had unexpected kind {:?}", kind); + span_bug!(cx.tcx.def_span(trait_ref.def_id()), "`TraitRef` had unexpected kind {kind:?}"); } inline::record_extern_fqn(cx, trait_ref.def_id(), kind); let path = - external_path(cx, trait_ref.def_id(), true, bindings, trait_ref.map_bound(|tr| tr.substs)); + external_path(cx, trait_ref.def_id(), true, bindings, trait_ref.map_bound(|tr| tr.args)); debug!(?trait_ref); @@ -239,7 +257,7 @@ fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> | rbv::ResolvedArg::Free(_, node_id), ) = def { - if let Some(lt) = cx.substs.get(&node_id).and_then(|p| p.as_lt()).cloned() { + if let Some(lt) = cx.args.get(&node_id).and_then(|p| p.as_lt()).cloned() { return lt; } } @@ -250,11 +268,12 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t let def_id = cx.tcx.hir().body_owner_def_id(constant.value.body).to_def_id(); Constant { type_: clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), + ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), cx, Some(def_id), None, ), + generics: Box::new(Generics::default()), kind: ConstantKind::Anonymous { body: constant.value.body }, } } @@ -266,6 +285,7 @@ pub(crate) fn clean_middle_const<'tcx>( // FIXME: instead of storing the stringified expression, store `self` directly instead. Constant { type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None), + generics: Box::new(Generics::default()), kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() }, } } @@ -284,7 +304,7 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Life | ty::ReError(_) | ty::RePlaceholder(..) | ty::ReErased => { - debug!("cannot clean region {:?}", region); + debug!("cannot clean region {region:?}"); None } } @@ -350,9 +370,8 @@ fn clean_poly_trait_predicate<'tcx>( cx: &mut DocContext<'tcx>, ) -> Option<WherePredicate> { // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op. - if pred.skip_binder().constness == ty::BoundConstness::ConstIfConst - && Some(pred.skip_binder().def_id()) == cx.tcx.lang_items().destruct_trait() - { + // FIXME(effects) check constness + if Some(pred.skip_binder().def_id()) == cx.tcx.lang_items().destruct_trait() { return None; } @@ -442,7 +461,7 @@ fn clean_projection<'tcx>( let bounds = cx .tcx .explicit_item_bounds(ty.skip_binder().def_id) - .subst_iter_copied(cx.tcx, ty.skip_binder().substs) + .iter_instantiated_copied(cx.tcx, ty.skip_binder().args) .map(|(pred, _)| pred) .collect::<Vec<_>>(); return clean_middle_opaque_bounds(cx, bounds); @@ -481,9 +500,9 @@ fn projection_to_path_segment<'tcx>( PathSegment { name: item.name, args: GenericArgs::AngleBracketed { - args: substs_to_args( + args: ty_args_to_args( cx, - ty.map_bound(|ty| &ty.substs[generics.parent_count..]), + ty.map_bound(|ty| &ty.args[generics.parent_count..]), false, None, ) @@ -504,7 +523,7 @@ fn clean_generic_param_def<'tcx>( ty::GenericParamDefKind::Type { has_default, synthetic, .. } => { let default = if has_default { Some(clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(def.def_id).subst_identity()), + ty::Binder::dummy(cx.tcx.type_of(def.def_id).instantiate_identity()), cx, Some(def.def_id), None, @@ -538,7 +557,7 @@ fn clean_generic_param_def<'tcx>( )), default: match has_default { true => Some(Box::new( - cx.tcx.const_param_default(def.def_id).subst_identity().to_string(), + cx.tcx.const_param_default(def.def_id).instantiate_identity().to_string(), )), false => None, }, @@ -786,10 +805,10 @@ fn clean_ty_generics<'tcx>( let where_predicates = preds .predicates .iter() - .flat_map(|(p, _)| { + .flat_map(|(pred, _)| { let mut projection = None; let param_idx = (|| { - let bound_p = p.kind(); + let bound_p = pred.kind(); match bound_p.skip_binder() { ty::ClauseKind::Trait(pred) => { if let ty::Param(param) = pred.self_ty().kind() { @@ -814,33 +833,26 @@ fn clean_ty_generics<'tcx>( })(); if let Some(param_idx) = param_idx - && let Some(b) = impl_trait.get_mut(¶m_idx.into()) + && let Some(bounds) = impl_trait.get_mut(¶m_idx.into()) { - let p: WherePredicate = clean_predicate(*p, cx)?; + let pred = clean_predicate(*pred, cx)?; - b.extend( - p.get_bounds() + bounds.extend( + pred.get_bounds() .into_iter() .flatten() .cloned() - .filter(|b| !b.is_sized_bound(cx)), ); - let proj = projection.map(|p| { - ( - clean_projection(p.map_bound(|p| p.projection_ty), cx, None), - p.map_bound(|p| p.term), - ) - }); - if let Some(((_, trait_did, name), rhs)) = proj - .as_ref() - .and_then(|(lhs, rhs): &(Type, _)| Some((lhs.projection()?, rhs))) + if let Some(proj) = projection + && let lhs = clean_projection(proj.map_bound(|p| p.projection_ty), cx, None) + && let Some((_, trait_did, name)) = lhs.projection() { impl_trait_proj.entry(param_idx).or_default().push(( trait_did, name, - *rhs, - p.get_bound_params() + proj.map_bound(|p| p.term), + pred.get_bound_params() .into_iter() .flatten() .cloned() @@ -851,13 +863,32 @@ fn clean_ty_generics<'tcx>( return None; } - Some(p) + Some(pred) }) .collect::<Vec<_>>(); for (param, mut bounds) in impl_trait { + let mut has_sized = false; + bounds.retain(|b| { + if b.is_sized_bound(cx) { + has_sized = true; + false + } else { + true + } + }); + if !has_sized { + bounds.push(GenericBound::maybe_sized(cx)); + } + // Move trait bounds to the front. - bounds.sort_by_key(|b| !matches!(b, GenericBound::TraitBound(..))); + bounds.sort_by_key(|b| !b.is_trait_bound()); + + // Add back a `Sized` bound if there are no *trait* bounds remaining (incl. `?Sized`). + // Since all potential trait bounds are at the front we can just check the first bound. + if bounds.first().map_or(true, |b| !b.is_trait_bound()) { + bounds.insert(0, GenericBound::sized(cx)); + } let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() }; if let Some(proj) = impl_trait_proj.remove(&idx) { @@ -879,7 +910,7 @@ fn clean_ty_generics<'tcx>( // implicit `Sized` bound unless removed with `?Sized`. // However, in the list of where-predicates below, `Sized` appears like a // normal bound: It's either present (the type is sized) or - // absent (the type is unsized) but never *maybe* (i.e. `?Sized`). + // absent (the type might be unsized) but never *maybe* (i.e. `?Sized`). // // This is unsuitable for rendering. // Thus, as a first step remove all `Sized` bounds that should be implicit. @@ -890,8 +921,8 @@ fn clean_ty_generics<'tcx>( let mut sized_params = FxHashSet::default(); where_predicates.retain(|pred| { if let WherePredicate::BoundPredicate { ty: Generic(g), bounds, .. } = pred - && *g != kw::SelfUpper - && bounds.iter().any(|b| b.is_sized_bound(cx)) + && *g != kw::SelfUpper + && bounds.iter().any(|b| b.is_sized_bound(cx)) { sized_params.insert(*g); false @@ -1158,11 +1189,18 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext let local_did = trait_item.owner_id.to_def_id(); cx.with_param_env(local_did, |cx| { let inner = match trait_item.kind { - hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem( - clean_ty(ty, cx), - ConstantKind::Local { def_id: local_did, body: default }, - ), - hir::TraitItemKind::Const(ty, None) => TyAssocConstItem(clean_ty(ty, cx)), + hir::TraitItemKind::Const(ty, Some(default)) => { + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + AssocConstItem( + Box::new(generics), + clean_ty(ty, cx), + ConstantKind::Local { def_id: local_did, body: default }, + ) + } + hir::TraitItemKind::Const(ty, None) => { + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + TyAssocConstItem(Box::new(generics), clean_ty(ty, cx)) + } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body)); MethodItem(m, None) @@ -1207,8 +1245,9 @@ pub(crate) fn clean_impl_item<'tcx>( cx.with_param_env(local_did, |cx| { let inner = match impl_.kind { hir::ImplItemKind::Const(ty, expr) => { + let generics = clean_generics(impl_.generics, cx); let default = ConstantKind::Local { def_id: local_did, body: expr }; - AssocConstItem(clean_ty(ty, cx), default) + AssocConstItem(Box::new(generics), clean_ty(ty, cx), default) } hir::ImplItemKind::Fn(ref sig, body) => { let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); @@ -1243,24 +1282,31 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( let kind = match assoc_item.kind { ty::AssocKind::Const => { let ty = clean_middle_ty( - ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), + ty::Binder::dummy(tcx.type_of(assoc_item.def_id).instantiate_identity()), cx, Some(assoc_item.def_id), None, ); + let mut generics = Box::new(clean_ty_generics( + cx, + tcx.generics_of(assoc_item.def_id), + tcx.explicit_predicates_of(assoc_item.def_id), + )); + simplify::move_bounds_to_generic_parameters(&mut generics); + let provided = match assoc_item.container { ty::ImplContainer => true, ty::TraitContainer => tcx.defaultness(assoc_item.def_id).has_value(), }; if provided { - AssocConstItem(ty, ConstantKind::Extern { def_id: assoc_item.def_id }) + AssocConstItem(generics, ty, ConstantKind::Extern { def_id: assoc_item.def_id }) } else { - TyAssocConstItem(ty) + TyAssocConstItem(generics, ty) } } ty::AssocKind::Fn => { - let sig = tcx.fn_sig(assoc_item.def_id).subst_identity(); + let sig = tcx.fn_sig(assoc_item.def_id).instantiate_identity(); let late_bound_regions = sig.bound_vars().into_iter().filter_map(|var| match var { ty::BoundVariableKind::Region(ty::BrNamed(_, name)) @@ -1283,7 +1329,9 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( if assoc_item.fn_has_self_parameter { let self_ty = match assoc_item.container { - ty::ImplContainer => tcx.type_of(assoc_item.container_id(tcx)).subst_identity(), + ty::ImplContainer => { + tcx.type_of(assoc_item.container_id(tcx)).instantiate_identity() + } ty::TraitContainer => tcx.types.self_param, }; let self_arg_ty = sig.input(0).skip_binder(); @@ -1339,7 +1387,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( let mut predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates; if let ty::TraitContainer = assoc_item.container { let bounds = - tcx.explicit_item_bounds(assoc_item.def_id).subst_identity_iter_copied(); + tcx.explicit_item_bounds(assoc_item.def_id).instantiate_identity_iter_copied(); predicates = tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied())); } let mut generics = clean_ty_generics( @@ -1347,34 +1395,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( tcx.generics_of(assoc_item.def_id), ty::GenericPredicates { parent: None, predicates }, ); - // Move bounds that are (likely) directly attached to the parameters of the - // (generic) associated type from the where clause to the respective parameter. - // There is no guarantee that this is what the user actually wrote but we have - // no way of knowing. - let mut where_predicates = ThinVec::new(); - for mut pred in generics.where_predicates { - if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred - && let Some(GenericParamDef { - kind: GenericParamDefKind::Type { bounds: param_bounds, .. }, - .. - }) = generics.params.iter_mut().find(|param| ¶m.name == arg) - { - param_bounds.append(bounds); - } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred - && let Some(GenericParamDef { - kind: GenericParamDefKind::Lifetime { outlives: param_bounds }, - .. - }) = generics.params.iter_mut().find(|param| ¶m.name == arg) - { - param_bounds.extend(bounds.drain(..).map(|bound| match bound { - GenericBound::Outlives(lifetime) => lifetime, - _ => unreachable!(), - })); - } else { - where_predicates.push(pred); - } - } - generics.where_predicates = where_predicates; + simplify::move_bounds_to_generic_parameters(&mut generics); if let ty::TraitContainer = assoc_item.container { // Move bounds that are (likely) directly attached to the associated type @@ -1442,7 +1463,9 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( AssocTypeItem( Box::new(Typedef { type_: clean_middle_ty( - ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), + ty::Binder::dummy( + tcx.type_of(assoc_item.def_id).instantiate_identity(), + ), cx, Some(assoc_item.def_id), None, @@ -1459,7 +1482,9 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( AssocTypeItem( Box::new(Typedef { type_: clean_middle_ty( - ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), + ty::Binder::dummy( + tcx.type_of(assoc_item.def_id).instantiate_identity(), + ), cx, Some(assoc_item.def_id), None, @@ -1478,14 +1503,127 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx) } +fn first_non_private_clean_path<'tcx>( + cx: &mut DocContext<'tcx>, + path: &hir::Path<'tcx>, + new_path_segments: &'tcx [hir::PathSegment<'tcx>], + new_path_span: rustc_span::Span, +) -> Path { + let new_hir_path = + hir::Path { segments: new_path_segments, res: path.res, span: new_path_span }; + let mut new_clean_path = clean_path(&new_hir_path, cx); + // In here we need to play with the path data one last time to provide it the + // missing `args` and `res` of the final `Path` we get, which, since it comes + // from a re-export, doesn't have the generics that were originally there, so + // we add them by hand. + if let Some(path_last) = path.segments.last().as_ref() + && let Some(new_path_last) = new_clean_path.segments[..].last_mut() + && let Some(path_last_args) = path_last.args.as_ref() + && path_last.args.is_some() + { + assert!(new_path_last.args.is_empty()); + new_path_last.args = clean_generic_args(path_last_args, cx); + } + new_clean_path +} + +/// The goal of this function is to return the first `Path` which is not private (ie not private +/// or `doc(hidden)`). If it's not possible, it'll return the "end type". +/// +/// If the path is not a re-export or is public, it'll return `None`. +fn first_non_private<'tcx>( + cx: &mut DocContext<'tcx>, + hir_id: hir::HirId, + path: &hir::Path<'tcx>, +) -> Option<Path> { + let target_def_id = path.res.opt_def_id()?; + let (parent_def_id, ident) = match &path.segments[..] { + [] => return None, + // Relative paths are available in the same scope as the owner. + [leaf] => (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident), + // So are self paths. + [parent, leaf] if parent.ident.name == kw::SelfLower => { + (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident) + } + // Crate paths are not. We start from the crate root. + [parent, leaf] if matches!(parent.ident.name, kw::Crate | kw::PathRoot) => { + (LOCAL_CRATE.as_def_id().as_local()?, leaf.ident) + } + [parent, leaf] if parent.ident.name == kw::Super => { + let parent_mod = cx.tcx.parent_module(hir_id); + if let Some(super_parent) = cx.tcx.opt_local_parent(parent_mod.to_local_def_id()) { + (super_parent, leaf.ident) + } else { + // If we can't find the parent of the parent, then the parent is already the crate. + (LOCAL_CRATE.as_def_id().as_local()?, leaf.ident) + } + } + // Absolute paths are not. We start from the parent of the item. + [.., parent, leaf] => (parent.res.opt_def_id()?.as_local()?, leaf.ident), + }; + let hir = cx.tcx.hir(); + // First we try to get the `DefId` of the item. + for child in + cx.tcx.module_children_local(parent_def_id).iter().filter(move |c| c.ident == ident) + { + if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = child.res { + continue; + } + + if let Some(def_id) = child.res.opt_def_id() && target_def_id == def_id { + let mut last_path_res = None; + 'reexps: for reexp in child.reexport_chain.iter() { + if let Some(use_def_id) = reexp.id() && + let Some(local_use_def_id) = use_def_id.as_local() && + let Some(hir::Node::Item(item)) = hir.find_by_def_id(local_use_def_id) && + !item.ident.name.is_empty() && + let hir::ItemKind::Use(path, _) = item.kind + { + for res in &path.res { + if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { + continue; + } + if (cx.render_options.document_hidden || + !cx.tcx.is_doc_hidden(use_def_id)) && + // We never check for "cx.render_options.document_private" + // because if a re-export is not fully public, it's never + // documented. + cx.tcx.local_visibility(local_use_def_id).is_public() { + break 'reexps; + } + last_path_res = Some((path, res)); + continue 'reexps; + } + } + } + if !child.reexport_chain.is_empty() { + // So in here, we use the data we gathered from iterating the reexports. If + // `last_path_res` is set, it can mean two things: + // + // 1. We found a public reexport. + // 2. We didn't find a public reexport so it's the "end type" path. + if let Some((new_path, _)) = last_path_res { + return Some(first_non_private_clean_path(cx, path, new_path.segments, new_path.span)); + } + // If `last_path_res` is `None`, it can mean two things: + // + // 1. The re-export is public, no need to change anything, just use the path as is. + // 2. Nothing was found, so let's just return the original path. + return None; + } + } + } + None +} + fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { - let hir::Ty { hir_id: _, span, ref kind } = *hir_ty; + let hir::Ty { hir_id, span, ref kind } = *hir_ty; let hir::TyKind::Path(qpath) = kind else { unreachable!() }; match qpath { hir::QPath::Resolved(None, path) => { if let Res::Def(DefKind::TyParam, did) = path.res { - if let Some(new_ty) = cx.substs.get(&did).and_then(|p| p.as_ty()).cloned() { + if let Some(new_ty) = cx.args.get(&did).and_then(|p| p.as_ty()).cloned() { return new_ty; } if let Some(bounds) = cx.impl_trait_bounds.remove(&did.into()) { @@ -1496,7 +1634,12 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type if let Some(expanded) = maybe_expand_private_type_alias(cx, path) { expanded } else { - let path = clean_path(path, cx); + // First we check if it's a private re-export. + let path = if let Some(path) = first_non_private(cx, hir_id, &path) { + path + } else { + clean_path(path, cx) + }; resolve_type(cx, path) } } @@ -1563,7 +1706,7 @@ fn maybe_expand_private_type_alias<'tcx>( cx: &mut DocContext<'tcx>, path: &hir::Path<'tcx>, ) -> Option<Type> { - let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None }; + let Res::Def(DefKind::TyAlias { .. }, def_id) = path.res else { return None }; // Substitute private type aliases let def_id = def_id.as_local()?; let alias = if !cx.cache.effective_visibilities.is_exported(cx.tcx, def_id.to_def_id()) @@ -1576,7 +1719,7 @@ fn maybe_expand_private_type_alias<'tcx>( let hir::ItemKind::TyAlias(ty, generics) = alias else { return None }; let provided_params = &path.segments.last().expect("segments were empty"); - let mut substs = DefIdMap::default(); + let mut args = DefIdMap::default(); let generic_args = provided_params.args(); let mut indices: hir::GenericParamCount = Default::default(); @@ -1600,7 +1743,7 @@ fn maybe_expand_private_type_alias<'tcx>( } else { Lifetime::elided() }; - substs.insert(param.def_id.to_def_id(), SubstParam::Lifetime(cleaned)); + args.insert(param.def_id.to_def_id(), SubstParam::Lifetime(cleaned)); } indices.lifetimes += 1; } @@ -1617,10 +1760,9 @@ fn maybe_expand_private_type_alias<'tcx>( _ => None, }); if let Some(ty) = type_ { - substs.insert(param.def_id.to_def_id(), SubstParam::Type(clean_ty(ty, cx))); + args.insert(param.def_id.to_def_id(), SubstParam::Type(clean_ty(ty, cx))); } else if let Some(default) = *default { - substs - .insert(param.def_id.to_def_id(), SubstParam::Type(clean_ty(default, cx))); + args.insert(param.def_id.to_def_id(), SubstParam::Type(clean_ty(default, cx))); } indices.types += 1; } @@ -1637,7 +1779,7 @@ fn maybe_expand_private_type_alias<'tcx>( _ => None, }); if let Some(ct) = const_ { - substs.insert( + args.insert( param.def_id.to_def_id(), SubstParam::Constant(clean_const(ct, cx)), ); @@ -1648,7 +1790,7 @@ fn maybe_expand_private_type_alias<'tcx>( } } - Some(cx.enter_alias(substs, def_id.to_def_id(), |cx| clean_ty(ty, cx))) + Some(cx.enter_alias(args, def_id.to_def_id(), |cx| clean_ty(&ty, cx))) } pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { @@ -1725,11 +1867,11 @@ fn normalize<'tcx>( .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)); match normalized { Ok(normalized_value) => { - debug!("normalized {:?} to {:?}", ty, normalized_value); + debug!("normalized {ty:?} to {normalized_value:?}"); Some(normalized_value) } Err(err) => { - debug!("failed to normalize {:?}: {:?}", ty, err); + debug!("failed to normalize {ty:?}: {err:?}"); None } } @@ -1922,7 +2064,7 @@ pub(crate) fn clean_middle_ty<'tcx>( abi: sig.abi(), })) } - ty::Adt(def, substs) => { + ty::Adt(def, args) => { let did = def.did(); let kind = match def.adt_kind() { AdtKind::Struct => ItemType::Struct, @@ -1930,7 +2072,7 @@ pub(crate) fn clean_middle_ty<'tcx>( AdtKind::Enum => ItemType::Enum, }; inline::record_extern_fqn(cx, did, kind); - let path = external_path(cx, did, false, ThinVec::new(), bound_ty.rebind(substs)); + let path = external_path(cx, did, false, ThinVec::new(), bound_ty.rebind(args)); Type::Path { path } } ty::Foreign(did) => { @@ -1940,7 +2082,7 @@ pub(crate) fn clean_middle_ty<'tcx>( did, false, ThinVec::new(), - ty::Binder::dummy(InternalSubsts::empty()), + ty::Binder::dummy(ty::GenericArgs::empty()), ); Type::Path { path } } @@ -1953,10 +2095,10 @@ pub(crate) fn clean_middle_ty<'tcx>( .principal_def_id() .or_else(|| dids.next()) .unwrap_or_else(|| panic!("found trait object `{bound_ty:?}` with no traits?")); - let substs = match obj.principal() { - Some(principal) => principal.map_bound(|p| p.substs), - // marker traits have no substs. - _ => ty::Binder::dummy(InternalSubsts::empty()), + let args = match obj.principal() { + Some(principal) => principal.map_bound(|p| p.args), + // marker traits have no args. + _ => ty::Binder::dummy(ty::GenericArgs::empty()), }; inline::record_extern_fqn(cx, did, ItemType::Trait); @@ -1965,7 +2107,7 @@ pub(crate) fn clean_middle_ty<'tcx>( let mut bounds = dids .map(|did| { - let empty = ty::Binder::dummy(InternalSubsts::empty()); + let empty = ty::Binder::dummy(ty::GenericArgs::empty()); let path = external_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); PolyTrait { trait_: path, generic_params: Vec::new() } @@ -1979,7 +2121,7 @@ pub(crate) fn clean_middle_ty<'tcx>( pb.map_bound(|pb| { pb // HACK(compiler-errors): Doesn't actually matter what self - // type we put here, because we're only using the GAT's substs. + // type we put here, because we're only using the GAT's args. .with_self_ty(cx.tcx, cx.tcx.types.self_param) .projection_ty }), @@ -2005,7 +2147,7 @@ pub(crate) fn clean_middle_ty<'tcx>( .collect(); let late_bound_regions = late_bound_regions.into_iter().collect(); - let path = external_path(cx, did, false, bindings, substs); + let path = external_path(cx, did, false, bindings, args); bounds.insert(0, PolyTrait { trait_: path, generic_params: late_bound_regions }); DynTrait(bounds, lifetime) @@ -2026,9 +2168,9 @@ pub(crate) fn clean_middle_ty<'tcx>( assoc: PathSegment { name: cx.tcx.associated_item(alias_ty.skip_binder().def_id).name, args: GenericArgs::AngleBracketed { - args: substs_to_args( + args: ty_args_to_args( cx, - alias_ty.map_bound(|ty| ty.substs.as_slice()), + alias_ty.map_bound(|ty| ty.args.as_slice()), true, None, ) @@ -2051,11 +2193,11 @@ pub(crate) fn clean_middle_ty<'tcx>( data.def_id, false, ThinVec::new(), - bound_ty.rebind(data.substs), + bound_ty.rebind(data.args), ); Type::Path { path } } else { - let ty = cx.tcx.type_of(data.def_id).subst(cx.tcx, data.substs); + let ty = cx.tcx.type_of(data.def_id).instantiate(cx.tcx, data.args); clean_middle_ty(bound_ty.rebind(ty), cx, None, None) } } @@ -2068,11 +2210,10 @@ pub(crate) fn clean_middle_ty<'tcx>( } } - ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { // If it's already in the same alias, don't get an infinite loop. if cx.current_type_aliases.contains_key(&def_id) { - let path = - external_path(cx, def_id, false, ThinVec::new(), bound_ty.rebind(substs)); + let path = external_path(cx, def_id, false, ThinVec::new(), bound_ty.rebind(args)); Type::Path { path } } else { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; @@ -2081,7 +2222,7 @@ pub(crate) fn clean_middle_ty<'tcx>( let bounds = cx .tcx .explicit_item_bounds(def_id) - .subst_iter_copied(cx.tcx, substs) + .iter_instantiated_copied(cx.tcx, args) .map(|(bound, _)| bound) .collect::<Vec<_>>(); let ty = clean_middle_opaque_bounds(cx, bounds); @@ -2110,7 +2251,6 @@ fn clean_middle_opaque_bounds<'tcx>( cx: &mut DocContext<'tcx>, bounds: Vec<ty::Clause<'tcx>>, ) -> Type { - let mut regions = vec![]; let mut has_sized = false; let mut bounds = bounds .iter() @@ -2119,10 +2259,7 @@ fn clean_middle_opaque_bounds<'tcx>( let trait_ref = match bound_predicate.skip_binder() { ty::ClauseKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { - if let Some(r) = clean_middle_region(reg) { - regions.push(GenericBound::Outlives(r)); - } - return None; + return clean_middle_region(reg).map(GenericBound::Outlives); } _ => return None, }; @@ -2158,10 +2295,20 @@ fn clean_middle_opaque_bounds<'tcx>( Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) }) .collect::<Vec<_>>(); - bounds.extend(regions); - if !has_sized && !bounds.is_empty() { - bounds.insert(0, GenericBound::maybe_sized(cx)); + + if !has_sized { + bounds.push(GenericBound::maybe_sized(cx)); + } + + // Move trait bounds to the front. + bounds.sort_by_key(|b| !b.is_trait_bound()); + + // Add back a `Sized` bound if there are no *trait* bounds remaining (incl. `?Sized`). + // Since all potential trait bounds are at the front we can just check the first bound. + if bounds.first().map_or(true, |b| !b.is_trait_bound()) { + bounds.insert(0, GenericBound::sized(cx)); } + ImplTrait(bounds) } @@ -2174,7 +2321,7 @@ pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext field.did, field.name, clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(field.did).subst_identity()), + ty::Binder::dummy(cx.tcx.type_of(field.did).instantiate_identity()), cx, Some(field.did), None, @@ -2305,10 +2452,10 @@ fn clean_bare_fn_ty<'tcx>( pub(crate) fn reexport_chain<'tcx>( tcx: TyCtxt<'tcx>, import_def_id: LocalDefId, - target_def_id: LocalDefId, + target_def_id: DefId, ) -> &'tcx [Reexport] { for child in tcx.module_children_local(tcx.local_parent(import_def_id)) { - if child.res.opt_def_id() == Some(target_def_id.to_def_id()) + if child.res.opt_def_id() == Some(target_def_id) && child.reexport_chain.first().and_then(|r| r.id()) == Some(import_def_id.to_def_id()) { return &child.reexport_chain; @@ -2321,7 +2468,7 @@ pub(crate) fn reexport_chain<'tcx>( fn get_all_import_attributes<'hir>( cx: &mut DocContext<'hir>, import_def_id: LocalDefId, - target_def_id: LocalDefId, + target_def_id: DefId, is_inline: bool, ) -> Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)> { let mut attrs = Vec::new(); @@ -2336,7 +2483,7 @@ fn get_all_import_attributes<'hir>( attrs = import_attrs.iter().map(|attr| (Cow::Borrowed(attr), Some(def_id))).collect(); first = false; // We don't add attributes of an intermediate re-export if it has `#[doc(hidden)]`. - } else if !cx.tcx.is_doc_hidden(def_id) { + } else if cx.render_options.document_hidden || !cx.tcx.is_doc_hidden(def_id) { add_without_unwanted_attributes(&mut attrs, import_attrs, is_inline, Some(def_id)); } } @@ -2344,19 +2491,19 @@ fn get_all_import_attributes<'hir>( } fn filter_tokens_from_list( - args_tokens: TokenStream, + args_tokens: &TokenStream, should_retain: impl Fn(&TokenTree) -> bool, ) -> Vec<TokenTree> { let mut tokens = Vec::with_capacity(args_tokens.len()); let mut skip_next_comma = false; - for token in args_tokens.into_trees() { + for token in args_tokens.trees() { match token { TokenTree::Token(Token { kind: TokenKind::Comma, .. }, _) if skip_next_comma => { skip_next_comma = false; } - token if should_retain(&token) => { + token if should_retain(token) => { skip_next_comma = false; - tokens.push(token); + tokens.push(token.clone()); } _ => { skip_next_comma = true; @@ -2414,7 +2561,7 @@ fn add_without_unwanted_attributes<'hir>( match normal.item.args { ast::AttrArgs::Delimited(ref mut args) => { let tokens = - filter_tokens_from_list(args.tokens.clone(), |token| { + filter_tokens_from_list(&args.tokens, |token| { !matches!( token, TokenTree::Token( @@ -2458,8 +2605,9 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::Static(ty, mutability, body_id) => { StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(body_id) }) } - ItemKind::Const(ty, body_id) => ConstantItem(Constant { + ItemKind::Const(ty, generics, body_id) => ConstantItem(Constant { type_: clean_ty(ty, cx), + generics: Box::new(clean_generics(generics, cx)), kind: ConstantKind::Local { body: body_id, def_id }, }), ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { @@ -2538,7 +2686,7 @@ fn clean_maybe_renamed_item<'tcx>( vec![generate_item_with_correct_attrs( cx, kind, - item.owner_id.def_id, + item.owner_id.def_id.to_def_id(), name, import_id, renamed, @@ -2574,8 +2722,8 @@ fn clean_impl<'tcx>( let for_ = clean_ty(impl_.self_ty, cx); let type_alias = for_.def_id(&cx.cache).and_then(|alias_def_id: DefId| match tcx.def_kind(alias_def_id) { - DefKind::TyAlias => Some(clean_middle_ty( - ty::Binder::dummy(tcx.type_of(def_id).subst_identity()), + DefKind::TyAlias { .. } => Some(clean_middle_ty( + ty::Binder::dummy(tcx.type_of(def_id).instantiate_identity()), cx, Some(def_id.to_def_id()), None, @@ -2640,15 +2788,12 @@ fn clean_extern_crate<'tcx>( } } - // FIXME: using `from_def_id_and_kind` breaks `rustdoc/masked` for some reason - vec![Item { - name: Some(name), - attrs: Box::new(Attributes::from_ast(attrs)), - item_id: crate_def_id.into(), - kind: Box::new(ExternCrateItem { src: orig_name }), - cfg: attrs.cfg(cx.tcx, &cx.cache.hidden_cfg), - inline_stmt_id: Some(krate_owner_def_id), - }] + vec![Item::from_def_id_and_parts( + krate_owner_def_id, + Some(name), + ExternCrateItem { src: orig_name }, + cx, + )] } fn clean_use_statement<'tcx>( @@ -2662,9 +2807,6 @@ fn clean_use_statement<'tcx>( let mut items = Vec::new(); let hir::UsePath { segments, ref res, span } = *path; for &res in res { - if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { - continue; - } let path = hir::Path { segments, res, span }; items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names)); } @@ -2679,6 +2821,9 @@ fn clean_use_statement_inner<'tcx>( cx: &mut DocContext<'tcx>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, ) -> Vec<Item> { + if should_ignore_res(path.res) { + return Vec::new(); + } // We need this comparison because some imports (for std types for example) // are "inserted" as well but directly by the compiler and they should not be // taken into account. @@ -2691,11 +2836,12 @@ fn clean_use_statement_inner<'tcx>( let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline); let pub_underscore = visibility.is_public() && name == kw::Underscore; let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id); + let import_def_id = import.owner_id.def_id.to_def_id(); // The parent of the module in which this import resides. This // is the same as `current_mod` if that's already the top // level module. - let parent_mod = cx.tcx.parent_module_from_def_id(current_mod); + let parent_mod = cx.tcx.parent_module_from_def_id(current_mod.to_local_def_id()); // This checks if the import can be seen from a higher level module. // In other words, it checks if the visibility is the equivalent of @@ -2741,9 +2887,14 @@ fn clean_use_statement_inner<'tcx>( let inner = if kind == hir::UseKind::Glob { if !denied { let mut visited = DefIdSet::default(); - if let Some(items) = - inline::try_inline_glob(cx, path.res, current_mod, &mut visited, inlined_names) - { + if let Some(items) = inline::try_inline_glob( + cx, + path.res, + current_mod, + &mut visited, + inlined_names, + import, + ) { return items; } } @@ -2759,7 +2910,6 @@ fn clean_use_statement_inner<'tcx>( denied = true; } if !denied { - let import_def_id = import.owner_id.to_def_id(); if let Some(mut items) = inline::try_inline( cx, path.res, @@ -2779,7 +2929,7 @@ fn clean_use_statement_inner<'tcx>( Import::new_simple(name, resolve_use_source(cx, path), true) }; - vec![Item::from_def_id_and_parts(import.owner_id.to_def_id(), None, ImportItem(inner), cx)] + vec![Item::from_def_id_and_parts(import_def_id, None, ImportItem(inner), cx)] } fn clean_maybe_renamed_foreign_item<'tcx>( diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index ef38ca3c1..66d10f236 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -76,17 +76,13 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String }; // Reparse a single token tree. - let mut reparsed_trees = match parser.parse_all_token_trees() { - Ok(reparsed_trees) => reparsed_trees, - Err(diagnostic) => { - diagnostic.cancel(); - return None; - } - }; - if reparsed_trees.len() != 1 { + if parser.token == token::Eof { + return None; + } + let reparsed_tree = parser.parse_token_tree(); + if parser.token != token::Eof { return None; } - let reparsed_tree = reparsed_trees.pop().unwrap(); // Compare against the original tree. if reparsed_tree.eq_unspanned(matcher) { Some(snippet) } else { None } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 65b1b72ad..7b8f20326 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -47,7 +47,9 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> ThinVec<WP // Look for equality predicates on associated types that can be merged into // general bound predicates. equalities.retain(|(lhs, rhs, bound_params)| { - let Some((ty, trait_did, name)) = lhs.projection() else { return true; }; + let Some((ty, trait_did, name)) = lhs.projection() else { + return true; + }; let Some((bounds, _)) = tybounds.get_mut(ty) else { return true }; merge_bounds(cx, bounds, bound_params.clone(), trait_did, name, rhs) }); @@ -136,3 +138,38 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) }) .any(|did| trait_is_same_or_supertrait(cx, did, trait_)) } + +/// Move bounds that are (likely) directly attached to generic parameters from the where-clause to +/// the respective parameter. +/// +/// There is no guarantee that this is what the user actually wrote but we have no way of knowing. +// FIXME(fmease): It'd make a lot of sense to just incorporate this logic into `clean_ty_generics` +// making every of its users benefit from it. +pub(crate) fn move_bounds_to_generic_parameters(generics: &mut clean::Generics) { + use clean::types::*; + + let mut where_predicates = ThinVec::new(); + for mut pred in generics.where_predicates.drain(..) { + if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred + && let Some(GenericParamDef { + kind: GenericParamDefKind::Type { bounds: param_bounds, .. }, + .. + }) = generics.params.iter_mut().find(|param| ¶m.name == arg) + { + param_bounds.append(bounds); + } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred + && let Some(GenericParamDef { + kind: GenericParamDefKind::Lifetime { outlives: param_bounds }, + .. + }) = generics.params.iter_mut().find(|param| ¶m.name == arg) + { + param_bounds.extend(bounds.drain(..).map(|bound| match bound { + GenericBound::Outlives(lifetime) => lifetime, + _ => unreachable!(), + })); + } else { + where_predicates.push(pred); + } + } + generics.where_predicates = where_predicates; +} diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 26139d527..49bde1d31 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -78,7 +78,7 @@ impl ItemId { #[track_caller] pub(crate) fn expect_def_id(self) -> DefId { self.as_def_id() - .unwrap_or_else(|| panic!("ItemId::expect_def_id: `{:?}` isn't a DefId", self)) + .unwrap_or_else(|| panic!("ItemId::expect_def_id: `{self:?}` isn't a DefId")) } #[inline] @@ -352,7 +352,7 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool { match tcx.def_kind(parent) { DefKind::Struct | DefKind::Union => false, DefKind::Variant => true, - parent_kind => panic!("unexpected parent kind: {:?}", parent_kind), + parent_kind => panic!("unexpected parent kind: {parent_kind:?}"), } } @@ -436,7 +436,7 @@ impl Item { attrs: Box<Attributes>, cfg: Option<Arc<Cfg>>, ) -> Item { - trace!("name={:?}, def_id={:?} cfg={:?}", name, def_id, cfg); + trace!("name={name:?}, def_id={def_id:?} cfg={cfg:?}"); Item { item_id: def_id.into(), @@ -451,11 +451,7 @@ impl Item { pub(crate) fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> { use crate::html::format::{href, link_tooltip}; - let Some(links) = cx.cache() - .intra_doc_links - .get(&self.item_id) else { - return vec![] - }; + let Some(links) = cx.cache().intra_doc_links.get(&self.item_id) else { return vec![] }; links .iter() .filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| { @@ -484,11 +480,9 @@ 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<RenderedLink> { - let Some(links) = cache - .intra_doc_links - .get(&self.item_id) else { - return vec![]; - }; + let Some(links) = cache.intra_doc_links.get(&self.item_id) else { + return vec![]; + }; links .iter() .map(|ItemLink { link: s, link_text, .. }| RenderedLink { @@ -830,9 +824,9 @@ pub(crate) enum ItemKind { ProcMacroItem(ProcMacro), PrimitiveItem(PrimitiveType), /// A required associated constant in a trait declaration. - TyAssocConstItem(Type), + TyAssocConstItem(Box<Generics>, Type), /// An associated constant in a trait impl or a provided one in a trait declaration. - AssocConstItem(Type, ConstantKind), + AssocConstItem(Box<Generics>, Type, ConstantKind), /// A required associated type in a trait declaration. /// /// The bounds may be non-empty if there is a `where` clause. @@ -877,8 +871,8 @@ impl ItemKind { | MacroItem(_) | ProcMacroItem(_) | PrimitiveItem(_) - | TyAssocConstItem(_) - | AssocConstItem(_, _) + | TyAssocConstItem(..) + | AssocConstItem(..) | TyAssocTypeItem(..) | AssocTypeItem(..) | StrippedItem(_) @@ -1225,15 +1219,24 @@ pub(crate) enum GenericBound { } impl GenericBound { + pub(crate) fn sized(cx: &mut DocContext<'_>) -> GenericBound { + Self::sized_with(cx, hir::TraitBoundModifier::None) + } + pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound { + Self::sized_with(cx, hir::TraitBoundModifier::Maybe) + } + + fn sized_with(cx: &mut DocContext<'_>, modifier: hir::TraitBoundModifier) -> GenericBound { let did = cx.tcx.require_lang_item(LangItem::Sized, None); - let empty = ty::Binder::dummy(ty::InternalSubsts::empty()); + let empty = ty::Binder::dummy(ty::GenericArgs::empty()); let path = external_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); - GenericBound::TraitBound( - PolyTrait { trait_: path, generic_params: Vec::new() }, - hir::TraitBoundModifier::Maybe, - ) + GenericBound::TraitBound(PolyTrait { trait_: path, generic_params: Vec::new() }, modifier) + } + + pub(crate) fn is_trait_bound(&self) -> bool { + matches!(self, Self::TraitBound(..)) } pub(crate) fn is_sized_bound(&self, cx: &DocContext<'_>) -> bool { @@ -1275,7 +1278,7 @@ impl Lifetime { } } -#[derive(Clone, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<GenericParamDef> }, RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> }, @@ -1345,7 +1348,7 @@ impl GenericParamDef { } // maybe use a Generic enum and use Vec<Generic>? -#[derive(Clone, Debug, Default)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] pub(crate) struct Generics { pub(crate) params: ThinVec<GenericParamDef>, pub(crate) where_predicates: ThinVec<WherePredicate>, @@ -1782,7 +1785,6 @@ impl PrimitiveType { } pub(crate) fn simplified_types() -> &'static SimplifiedTypes { - use ty::fast_reject::SimplifiedType::*; use ty::{FloatTy, IntTy, UintTy}; use PrimitiveType::*; static CELL: OnceCell<SimplifiedTypes> = OnceCell::new(); @@ -1790,38 +1792,38 @@ impl PrimitiveType { let single = |x| iter::once(x).collect(); CELL.get_or_init(move || { map! { - Isize => single(IntSimplifiedType(IntTy::Isize)), - I8 => single(IntSimplifiedType(IntTy::I8)), - I16 => single(IntSimplifiedType(IntTy::I16)), - I32 => single(IntSimplifiedType(IntTy::I32)), - I64 => single(IntSimplifiedType(IntTy::I64)), - I128 => single(IntSimplifiedType(IntTy::I128)), - Usize => single(UintSimplifiedType(UintTy::Usize)), - U8 => single(UintSimplifiedType(UintTy::U8)), - U16 => single(UintSimplifiedType(UintTy::U16)), - U32 => single(UintSimplifiedType(UintTy::U32)), - U64 => single(UintSimplifiedType(UintTy::U64)), - U128 => single(UintSimplifiedType(UintTy::U128)), - F32 => single(FloatSimplifiedType(FloatTy::F32)), - F64 => single(FloatSimplifiedType(FloatTy::F64)), - Str => single(StrSimplifiedType), - Bool => single(BoolSimplifiedType), - Char => single(CharSimplifiedType), - Array => single(ArraySimplifiedType), - Slice => single(SliceSimplifiedType), + Isize => single(SimplifiedType::Int(IntTy::Isize)), + I8 => single(SimplifiedType::Int(IntTy::I8)), + I16 => single(SimplifiedType::Int(IntTy::I16)), + I32 => single(SimplifiedType::Int(IntTy::I32)), + I64 => single(SimplifiedType::Int(IntTy::I64)), + I128 => single(SimplifiedType::Int(IntTy::I128)), + Usize => single(SimplifiedType::Uint(UintTy::Usize)), + U8 => single(SimplifiedType::Uint(UintTy::U8)), + U16 => single(SimplifiedType::Uint(UintTy::U16)), + U32 => single(SimplifiedType::Uint(UintTy::U32)), + U64 => single(SimplifiedType::Uint(UintTy::U64)), + U128 => single(SimplifiedType::Uint(UintTy::U128)), + F32 => single(SimplifiedType::Float(FloatTy::F32)), + F64 => single(SimplifiedType::Float(FloatTy::F64)), + Str => single(SimplifiedType::Str), + Bool => single(SimplifiedType::Bool), + Char => single(SimplifiedType::Char), + Array => single(SimplifiedType::Array), + Slice => single(SimplifiedType::Slice), // FIXME: If we ever add an inherent impl for tuples // with different lengths, they won't show in rustdoc. // // Either manually update this arrayvec at this point // or start with a more complex refactoring. - Tuple => [TupleSimplifiedType(1), TupleSimplifiedType(2), TupleSimplifiedType(3)].into(), - Unit => single(TupleSimplifiedType(0)), - RawPointer => [PtrSimplifiedType(Mutability::Not), PtrSimplifiedType(Mutability::Mut)].into_iter().collect(), - Reference => [RefSimplifiedType(Mutability::Not), RefSimplifiedType(Mutability::Mut)].into_iter().collect(), + Tuple => [SimplifiedType::Tuple(1), SimplifiedType::Tuple(2), SimplifiedType::Tuple(3)].into(), + Unit => single(SimplifiedType::Tuple(0)), + RawPointer => [SimplifiedType::Ptr(Mutability::Not), SimplifiedType::Ptr(Mutability::Mut)].into_iter().collect(), + Reference => [SimplifiedType::Ref(Mutability::Not), SimplifiedType::Ref(Mutability::Mut)].into_iter().collect(), // FIXME: This will be wrong if we ever add inherent impls // for function pointers. - Fn => single(FunctionSimplifiedType(1)), - Never => single(NeverSimplifiedType), + Fn => single(SimplifiedType::Function(1)), + Never => single(SimplifiedType::Never), } }) } @@ -2210,6 +2212,17 @@ pub(crate) enum GenericArgs { Parenthesized { inputs: Box<[Type]>, output: Option<Box<Type>> }, } +impl GenericArgs { + pub(crate) fn is_empty(&self) -> bool { + match self { + GenericArgs::AngleBracketed { args, bindings } => { + args.is_empty() && bindings.is_empty() + } + GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(), + } + } +} + #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct PathSegment { pub(crate) name: Symbol, @@ -2253,6 +2266,7 @@ pub(crate) struct Static { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct Constant { pub(crate) type_: Type, + pub(crate) generics: Box<Generics>, pub(crate) kind: ConstantKind, } @@ -2502,7 +2516,8 @@ mod size_asserts { static_assert_size!(GenericParamDef, 56); static_assert_size!(Generics, 16); static_assert_size!(Item, 56); - static_assert_size!(ItemKind, 64); + // FIXME(generic_const_items): Further reduce the size. + static_assert_size!(ItemKind, 72); static_assert_size!(PathSegment, 40); static_assert_size!(Type, 32); // tidy-alphabetical-end diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs index 394954208..4907a5527 100644 --- a/src/librustdoc/clean/types/tests.rs +++ b/src/librustdoc/clean/types/tests.rs @@ -73,7 +73,7 @@ fn should_not_trim() { fn is_same_generic() { use crate::clean::types::{PrimitiveType, Type}; use crate::formats::cache::Cache; - let cache = Cache::new(false); + let cache = Cache::new(false, false); let generic = Type::Generic(rustc_span::symbol::sym::Any); let unit = Type::Primitive(PrimitiveType::Unit); assert!(!generic.is_doc_subtype_of(&unit, &cache)); diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index b786ecbe3..a86bbcc76 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -13,11 +13,10 @@ use rustc_ast as ast; use rustc_ast::tokenstream::TokenTree; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_middle::mir; use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use std::fmt::Write as _; use std::mem; @@ -39,11 +38,15 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { for it in &module.items { // `compiler_builtins` should be masked too, but we can't apply // `#[doc(masked)]` to the injected `extern crate` because it's unstable. - if it.is_extern_crate() - && (it.attrs.has_doc_flag(sym::masked) - || cx.tcx.is_compiler_builtins(it.item_id.krate())) - { + if cx.tcx.is_compiler_builtins(it.item_id.krate()) { cx.cache.masked_crates.insert(it.item_id.krate()); + } else if it.is_extern_crate() + && it.attrs.has_doc_flag(sym::masked) + && let Some(def_id) = it.item_id.as_def_id() + && let Some(local_def_id) = def_id.as_local() + && let Some(cnum) = cx.tcx.extern_mod_stmt_cnum(local_def_id) + { + cx.cache.masked_crates.insert(cnum); } } } @@ -71,7 +74,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { Crate { module, external_traits: cx.external_traits.clone() } } -pub(crate) fn substs_to_args<'tcx>( +pub(crate) fn ty_args_to_args<'tcx>( cx: &mut DocContext<'tcx>, args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, has_self: bool, @@ -114,12 +117,12 @@ fn external_generic_args<'tcx>( did: DefId, has_self: bool, bindings: ThinVec<TypeBinding>, - substs: ty::Binder<'tcx, SubstsRef<'tcx>>, + ty_args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, ) -> GenericArgs { - let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self, Some(did)); + let args = ty_args_to_args(cx, ty_args.map_bound(|args| &args[..]), has_self, Some(did)); if cx.tcx.fn_trait_kind_from_def_id(did).is_some() { - let ty = substs + let ty = ty_args .iter() .nth(if has_self { 1 } else { 0 }) .unwrap() @@ -147,7 +150,7 @@ pub(super) fn external_path<'tcx>( did: DefId, has_self: bool, bindings: ThinVec<TypeBinding>, - substs: ty::Binder<'tcx, SubstsRef<'tcx>>, + args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, ) -> Path { let def_kind = cx.tcx.def_kind(did); let name = cx.tcx.item_name(did); @@ -155,7 +158,7 @@ pub(super) fn external_path<'tcx>( res: Res::Def(def_kind, did), segments: thin_vec![PathSegment { name, - args: external_generic_args(cx, did, has_self, bindings, substs), + args: external_generic_args(cx, did, has_self, bindings, args), }], } } @@ -217,7 +220,7 @@ pub(crate) fn build_deref_target_impls( pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { use rustc_hir::*; - debug!("trying to get a name from pattern: {:?}", p); + debug!("trying to get a name from pattern: {p:?}"); Symbol::intern(&match p.kind { PatKind::Wild | PatKind::Struct(..) => return kw::Underscore, @@ -250,7 +253,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { match n.kind() { - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs: _ }) => { + ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => { let s = if let Some(def) = def.as_local() { print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(def)) } else { @@ -275,7 +278,7 @@ pub(crate) fn print_evaluated_const( underscores_and_type: bool, ) -> Option<String> { tcx.const_eval_poly(def_id).ok().and_then(|val| { - let ty = tcx.type_of(def_id).subst_identity(); + let ty = tcx.type_of(def_id).instantiate_identity(); match (val, ty.kind()) { (_, &ty::Ref(..)) => None, (ConstValue::Scalar(_), &ty::Adt(_, _)) => None, @@ -460,7 +463,7 @@ pub(crate) fn print_const_expr(tcx: TyCtxt<'_>, body: hir::BodyId) -> String { /// Given a type Path, resolve it to a Type using the TyCtxt pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { - debug!("resolve_type({:?})", path); + debug!("resolve_type({path:?})"); match path.res { Res::PrimTy(p) => Primitive(PrimitiveType::from(p)), @@ -479,12 +482,6 @@ pub(crate) fn get_auto_trait_and_blanket_impls( cx: &mut DocContext<'_>, item_def_id: DefId, ) -> impl Iterator<Item = Item> { - // FIXME: To be removed once `parallel_compiler` bugs are fixed! - // More information in <https://github.com/rust-lang/rust/pull/106930>. - if cfg!(parallel_compiler) { - return vec![].into_iter().chain(vec![].into_iter()); - } - let auto_impls = cx .sess() .prof @@ -505,16 +502,30 @@ pub(crate) fn get_auto_trait_and_blanket_impls( /// [`href()`]: crate::html::format::href pub(crate) fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId { use DefKind::*; - debug!("register_res({:?})", res); + debug!("register_res({res:?})"); let (kind, did) = match res { Res::Def( - kind @ (AssocTy | AssocFn | AssocConst | Variant | Fn | TyAlias | Enum | Trait | Struct - | Union | Mod | ForeignTy | Const | Static(_) | Macro(..) | TraitAlias), + kind @ (AssocTy + | AssocFn + | AssocConst + | Variant + | Fn + | TyAlias { .. } + | Enum + | Trait + | Struct + | Union + | Mod + | ForeignTy + | Const + | Static(_) + | Macro(..) + | TraitAlias), did, ) => (kind.into(), did), - _ => panic!("register_res: unexpected {:?}", res), + _ => panic!("register_res: unexpected {res:?}"), }; if did.is_local() { return did; @@ -592,8 +603,12 @@ pub(super) fn render_macro_arms<'a>( ) -> String { let mut out = String::new(); for matcher in matchers { - writeln!(out, " {} => {{ ... }}{}", render_macro_matcher(tcx, matcher), arm_delim) - .unwrap(); + writeln!( + out, + " {matcher} => {{ ... }}{arm_delim}", + matcher = render_macro_matcher(tcx, matcher), + ) + .unwrap(); } out } @@ -609,22 +624,54 @@ pub(super) fn display_macro_source( let matchers = def.body.tokens.chunks(4).map(|arm| &arm[0]); if def.macro_rules { - format!("macro_rules! {} {{\n{}}}", name, render_macro_arms(cx.tcx, matchers, ";")) + format!("macro_rules! {name} {{\n{arms}}}", arms = render_macro_arms(cx.tcx, matchers, ";")) } else { if matchers.len() <= 1 { format!( - "{}macro {}{} {{\n ...\n}}", - visibility_to_src_with_space(Some(vis), cx.tcx, def_id), - name, - matchers.map(|matcher| render_macro_matcher(cx.tcx, matcher)).collect::<String>(), + "{vis}macro {name}{matchers} {{\n ...\n}}", + vis = visibility_to_src_with_space(Some(vis), cx.tcx, def_id), + matchers = matchers + .map(|matcher| render_macro_matcher(cx.tcx, matcher)) + .collect::<String>(), ) } else { format!( - "{}macro {} {{\n{}}}", - visibility_to_src_with_space(Some(vis), cx.tcx, def_id), - name, - render_macro_arms(cx.tcx, matchers, ","), + "{vis}macro {name} {{\n{arms}}}", + vis = visibility_to_src_with_space(Some(vis), cx.tcx, def_id), + arms = render_macro_arms(cx.tcx, matchers, ","), ) } } } + +pub(crate) fn inherits_doc_hidden( + tcx: TyCtxt<'_>, + mut def_id: LocalDefId, + stop_at: Option<LocalDefId>, +) -> bool { + let hir = tcx.hir(); + while let Some(id) = tcx.opt_local_parent(def_id) { + if let Some(stop_at) = stop_at && id == stop_at { + return false; + } + def_id = id; + if tcx.is_doc_hidden(def_id.to_def_id()) { + return true; + } else if let Some(node) = hir.find_by_def_id(def_id) && + matches!( + node, + hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }), + ) + { + // `impl` blocks stand a bit on their own: unless they have `#[doc(hidden)]` directly + // on them, they don't inherit it from the parent context. + return false; + } + } + false +} + +#[inline] +pub(crate) fn should_ignore_res(res: Res) -> bool { + matches!(res, Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..)) +} diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 217f1a6ee..81fb13f41 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -50,7 +50,7 @@ impl TryFrom<&str> for OutputFormat { match value { "json" => Ok(OutputFormat::Json), "html" => Ok(OutputFormat::Html), - _ => Err(format!("unknown output format `{}`", value)), + _ => Err(format!("unknown output format `{value}`")), } } } @@ -383,7 +383,7 @@ impl Options { match kind.parse() { Ok(kind) => emit.push(kind), Err(()) => { - diag.err(format!("unrecognized emission type: {}", kind)); + diag.err(format!("unrecognized emission type: {kind}")); return Err(1); } } @@ -415,7 +415,7 @@ impl Options { println!("rustdoc: [check-theme] Starting tests! (Ignoring all other arguments)"); for theme_file in to_check.iter() { - print!(" - Checking \"{}\"...", theme_file); + print!(" - Checking \"{theme_file}\"..."); let (success, differences) = theme::test_theme_against(theme_file, &paths, &diag); if !differences.is_empty() || !success { println!(" FAILED"); @@ -556,30 +556,28 @@ impl Options { matches.opt_strs("theme").iter().map(|s| (PathBuf::from(&s), s.to_owned())) { if !theme_file.is_file() { - diag.struct_err(format!("invalid argument: \"{}\"", theme_s)) + diag.struct_err(format!("invalid argument: \"{theme_s}\"")) .help("arguments to --theme must be files") .emit(); return Err(1); } if theme_file.extension() != Some(OsStr::new("css")) { - diag.struct_err(format!("invalid argument: \"{}\"", theme_s)) + diag.struct_err(format!("invalid argument: \"{theme_s}\"")) .help("arguments to --theme must have a .css extension") .emit(); return Err(1); } let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag); if !success { - diag.struct_err(format!("error loading theme file: \"{}\"", theme_s)).emit(); + diag.struct_err(format!("error loading theme file: \"{theme_s}\"")).emit(); return Err(1); } else if !ret.is_empty() { diag.struct_warn(format!( - "theme file \"{}\" is missing CSS rules from the default theme", - theme_s + "theme file \"{theme_s}\" is missing CSS rules from the default theme", )) .warn("the theme may appear incorrect when loaded") .help(format!( - "to see what rules are missing, call `rustdoc --check-theme \"{}\"`", - theme_s + "to see what rules are missing, call `rustdoc --check-theme \"{theme_s}\"`", )) .emit(); } @@ -608,7 +606,7 @@ impl Options { match matches.opt_str("r").as_deref() { Some("rust") | None => {} Some(s) => { - diag.struct_err(format!("unknown input format: {}", s)).emit(); + diag.struct_err(format!("unknown input format: {s}")).emit(); return Err(1); } } @@ -628,7 +626,7 @@ impl Options { let crate_types = match parse_crate_types_from_list(matches.opt_strs("crate-type")) { Ok(types) => types, Err(e) => { - diag.struct_err(format!("unknown crate type: {}", e)).emit(); + diag.struct_err(format!("unknown crate type: {e}")).emit(); return Err(1); } }; @@ -787,7 +785,7 @@ fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Han for &flag in deprecated_flags.iter() { if matches.opt_present(flag) { - diag.struct_warn(format!("the `{}` flag is deprecated", flag)) + diag.struct_warn(format!("the `{flag}` flag is deprecated")) .note( "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ for more information", @@ -800,7 +798,7 @@ fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Han for &flag in removed_flags.iter() { if matches.opt_present(flag) { - let mut err = diag.struct_warn(format!("the `{}` flag no longer functions", flag)); + let mut err = diag.struct_warn(format!("the `{flag}` flag no longer functions")); err.note( "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ for more information", diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 9687b8b18..4c8dab61f 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -10,6 +10,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{HirId, Path}; use rustc_interface::interface; +use rustc_lint::{late_lint_mod, MissingDoc}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks}; @@ -45,7 +46,7 @@ pub(crate) struct DocContext<'tcx> { // The current set of parameter substitutions, // for expanding type aliases at the HIR level: /// Table `DefId` of type, lifetime, or const parameter -> substituted type, lifetime, or const - pub(crate) substs: DefIdMap<clean::SubstParam>, + pub(crate) args: DefIdMap<clean::SubstParam>, pub(crate) current_type_aliases: DefIdMap<usize>, /// Table synthetic type parameter for `impl Trait` in argument position -> bounds pub(crate) impl_trait_bounds: FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>, @@ -85,17 +86,17 @@ impl<'tcx> DocContext<'tcx> { /// the substitutions for a type alias' RHS. pub(crate) fn enter_alias<F, R>( &mut self, - substs: DefIdMap<clean::SubstParam>, + args: DefIdMap<clean::SubstParam>, def_id: DefId, f: F, ) -> R where F: FnOnce(&mut Self) -> R, { - let old_substs = mem::replace(&mut self.substs, substs); + let old_args = mem::replace(&mut self.args, args); *self.current_type_aliases.entry(def_id).or_insert(0) += 1; let r = f(self); - self.substs = old_substs; + self.args = old_args; if let Some(count) = self.current_type_aliases.get_mut(&def_id) { *count -= 1; if *count == 0 { @@ -136,19 +137,13 @@ pub(crate) fn new_handler( ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); Box::new( - EmitterWriter::stderr( - color_config, - source_map.map(|sm| sm as _), - None, - fallback_bundle, - short, - unstable_opts.teach, - diagnostic_width, - false, - unstable_opts.track_diagnostics, - TerminalUrl::No, - ) - .ui_testing(unstable_opts.ui_testing), + EmitterWriter::stderr(color_config, fallback_bundle) + .sm(source_map.map(|sm| sm as _)) + .short_message(short) + .teach(unstable_opts.teach) + .diagnostic_width(diagnostic_width) + .track_diagnostics(unstable_opts.track_diagnostics) + .ui_testing(unstable_opts.ui_testing), ) } ErrorOutputType::Json { pretty, json_rendered } => { @@ -173,10 +168,8 @@ pub(crate) fn new_handler( } }; - rustc_errors::Handler::with_emitter_and_flags( - emitter, - unstable_opts.diagnostic_handler_flags(true), - ) + rustc_errors::Handler::with_emitter(emitter) + .with_flags(unstable_opts.diagnostic_handler_flags(true)) } /// Parse, resolve, and typecheck the given crate. @@ -270,8 +263,9 @@ pub(crate) fn create_config( parse_sess_created: None, register_lints: Some(Box::new(crate::lint::register_lints)), override_queries: Some(|_sess, providers, _external_providers| { + // We do not register late module lints, so this only runs `MissingDoc`. // Most lints will require typechecking, so just don't run them. - providers.lint_mod = |_, _| {}; + providers.lint_mod = |tcx, module_def_id| late_lint_mod(tcx, module_def_id, MissingDoc); // hack so that `used_trait_imports` won't try to call typeck providers.used_trait_imports = |_, _| { static EMPTY_SET: LazyLock<UnordSet<LocalDefId>> = LazyLock::new(UnordSet::default); @@ -289,13 +283,14 @@ pub(crate) fn create_config( let hir = tcx.hir(); let body = hir.body(hir.body_owned_by(def_id)); - debug!("visiting body for {:?}", def_id); + debug!("visiting body for {def_id:?}"); EmitIgnoredResolutionErrors::new(tcx).visit_body(body); (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck)(tcx, def_id) }; }), make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), + ice_file: None, } } @@ -324,9 +319,7 @@ pub(crate) fn run_global_ctxt( tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module)) }); tcx.sess.abort_if_errors(); - tcx.sess.time("missing_docs", || { - rustc_lint::check_crate(tcx, rustc_lint::builtin::MissingDoc::new); - }); + tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx)); tcx.sess.time("check_mod_attrs", || { tcx.hir().for_each_module(|module| tcx.ensure().check_mod_attrs(module)) }); @@ -340,12 +333,12 @@ pub(crate) fn run_global_ctxt( param_env: ParamEnv::empty(), external_traits: Default::default(), active_extern_traits: Default::default(), - substs: Default::default(), + args: Default::default(), current_type_aliases: Default::default(), impl_trait_bounds: Default::default(), generated_synthetics: Default::default(), auto_traits, - cache: Cache::new(render_options.document_private), + cache: Cache::new(render_options.document_private, render_options.document_hidden), inlined: FxHashSet::default(), output_format, render_options, @@ -384,7 +377,7 @@ pub(crate) fn run_global_ctxt( fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler, sp: Span) { let mut msg = - diag.struct_span_warn(sp, format!("the `#![doc({})]` attribute is deprecated", name)); + diag.struct_span_warn(sp, format!("the `#![doc({name})]` attribute is deprecated")); msg.note( "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ for more information", @@ -477,7 +470,7 @@ impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> { } fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { - debug!("visiting path {:?}", path); + debug!("visiting path {path:?}"); if path.res == Res::Err { // We have less context here than in rustc_resolve, // so we can only emit the name and span. @@ -494,8 +487,7 @@ impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> { self.tcx.sess, path.span, E0433, - "failed to resolve: {}", - label + "failed to resolve: {label}", ); err.span_label(path.span, label); err.note("this error was originally ignored because you are running `rustdoc`"); diff --git a/src/librustdoc/docfs.rs b/src/librustdoc/docfs.rs index d58b8dc6a..82c1a5039 100644 --- a/src/librustdoc/docfs.rs +++ b/src/librustdoc/docfs.rs @@ -72,9 +72,9 @@ impl DocFS { let sender = self.errors.clone().expect("can't write after closing"); self.pool.execute(move || { fs::write(&path, contents).unwrap_or_else(|e| { - sender.send(format!("\"{}\": {}", path.display(), e)).unwrap_or_else(|_| { - panic!("failed to send error on \"{}\"", path.display()) - }) + sender.send(format!("\"{path}\": {e}", path = path.display())).unwrap_or_else( + |_| panic!("failed to send error on \"{}\"", path.display()), + ) }); }); } else { diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 217257316..36d5adb63 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,7 +1,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError, TerminalUrl}; +use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID}; use rustc_interface::interface; @@ -108,6 +108,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { override_queries: None, make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), + ice_file: None, }; let test_args = options.test_args.clone(); @@ -191,7 +192,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { // The allow lint level is not expected, // as if allow is specified, no message // is to be emitted. - v => unreachable!("Invalid lint level '{}'", v), + v => unreachable!("Invalid lint level '{v}'"), }) .unwrap_or("warn") .to_string(); @@ -403,7 +404,7 @@ fn run_test( compiler.stdin(Stdio::piped()); compiler.stderr(Stdio::piped()); - debug!("compiler invocation for doctest: {:?}", compiler); + debug!("compiler invocation for doctest: {compiler:?}"); let mut child = compiler.spawn().expect("Failed to spawn rustc process"); { @@ -468,7 +469,9 @@ fn run_test( // Run the code! let mut cmd; + let output_file = make_maybe_absolute_path(output_file); if let Some(tool) = runtool { + let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); cmd.args(runtool_args); cmd.arg(output_file); @@ -502,6 +505,20 @@ fn run_test( Ok(()) } +/// Converts a path intended to use as a command to absolute if it is +/// relative, and not a single component. +/// +/// This is needed to deal with relative paths interacting with +/// `Command::current_dir` in a platform-specific way. +fn make_maybe_absolute_path(path: PathBuf) -> PathBuf { + if path.components().count() == 1 { + // Look up process via PATH. + path + } else { + std::env::current_dir().map(|c| c.join(&path)).unwrap_or_else(|_| path) + } +} + /// Transforms a test into code that can be compiled into a Rust binary, and returns the number of /// lines before the test code begins as well as if the output stream supports colors or not. pub(crate) fn make_test( @@ -557,36 +574,14 @@ pub(crate) fn make_test( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, ); - supports_color = EmitterWriter::stderr( - ColorConfig::Auto, - None, - None, - fallback_bundle.clone(), - false, - false, - Some(80), - false, - false, - TerminalUrl::No, - ) - .supports_color(); + supports_color = EmitterWriter::stderr(ColorConfig::Auto, fallback_bundle.clone()) + .diagnostic_width(Some(80)) + .supports_color(); - let emitter = EmitterWriter::new( - Box::new(io::sink()), - None, - None, - fallback_bundle, - false, - false, - false, - None, - false, - false, - TerminalUrl::No, - ); + let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let handler = Handler::with_emitter(false, None, Box::new(emitter)); + let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); let mut found_main = false; @@ -653,8 +648,7 @@ pub(crate) fn make_test( (found_main, found_extern_crate, found_macro) }) }); - let Ok((already_has_main, already_has_extern_crate, found_macro)) = result - else { + let Ok((already_has_main, already_has_extern_crate, found_macro)) = result else { // If the parser panicked due to a fatal error, pass the test code through unchanged. // The error will be reported during compilation. return (s.to_owned(), 0, false); @@ -760,21 +754,9 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { false, ); - let emitter = EmitterWriter::new( - Box::new(io::sink()), - None, - None, - fallback_bundle, - false, - false, - false, - None, - false, - false, - TerminalUrl::No, - ); + let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle); - let handler = Handler::with_emitter(false, None, Box::new(emitter)); + let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) { @@ -967,7 +949,7 @@ impl Collector { if !item_path.is_empty() { item_path.push(' '); } - format!("{} - {}(line {})", filename.prefer_local(), item_path, line) + format!("{} - {item_path}(line {line})", filename.prefer_local()) } pub(crate) fn set_position(&mut self, position: Span) { @@ -1044,7 +1026,7 @@ impl Tester for Collector { path.push(&test_id); if let Err(err) = std::fs::create_dir_all(&path) { - eprintln!("Couldn't create directory for doctest executables: {}", err); + eprintln!("Couldn't create directory for doctest executables: {err}"); panic::resume_unwind(Box::new(())); } @@ -1113,7 +1095,7 @@ impl Tester for Collector { eprint!("Test executable succeeded, but it's marked `should_panic`."); } TestFailure::MissingErrorCodes(codes) => { - eprint!("Some expected error codes were not found: {:?}", codes); + eprint!("Some expected error codes were not found: {codes:?}"); } TestFailure::ExecutionError(err) => { eprint!("Couldn't run the test: {err}"); diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index 88049c4ca..f0ebb8e5a 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -37,8 +37,7 @@ impl ExternalHtml { let bc = load_external_files(before_content, diag)?; let m_bc = load_external_files(md_before_content, diag)?; let bc = format!( - "{}{}", - bc, + "{bc}{}", Markdown { content: &m_bc, links: &[], @@ -53,8 +52,7 @@ impl ExternalHtml { let ac = load_external_files(after_content, diag)?; let m_ac = load_external_files(md_after_content, diag)?; let ac = format!( - "{}{}", - ac, + "{ac}{}", Markdown { content: &m_ac, links: &[], @@ -83,7 +81,11 @@ pub(crate) fn load_string<P: AsRef<Path>>( let contents = match fs::read(file_path) { Ok(bytes) => bytes, Err(e) => { - diag.struct_err(format!("error reading `{}`: {}", file_path.display(), e)).emit(); + diag.struct_err(format!( + "error reading `{file_path}`: {e}", + file_path = file_path.display() + )) + .emit(); return Err(LoadStringError::ReadFail); } }; diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index dac762e9f..d1deda0c7 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -86,6 +86,9 @@ pub(crate) struct Cache { /// Whether to document private items. /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. pub(crate) document_private: bool, + /// Whether to document hidden items. + /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. + pub(crate) document_hidden: bool, /// Crates marked with [`#[doc(masked)]`][doc_masked]. /// @@ -137,8 +140,8 @@ struct CacheBuilder<'a, 'tcx> { } impl Cache { - pub(crate) fn new(document_private: bool) -> Self { - Cache { document_private, ..Cache::default() } + pub(crate) fn new(document_private: bool, document_hidden: bool) -> Self { + Cache { document_private, document_hidden, ..Cache::default() } } /// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was @@ -473,7 +476,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } => { dids.insert(path.def_id()); if let Some(generics) = path.generics() && - let ty::Adt(adt, _) = self.tcx.type_of(path.def_id()).subst_identity().kind() && + let ty::Adt(adt, _) = self.tcx.type_of(path.def_id()).instantiate_identity().kind() && adt.is_fundamental() { for ty in generics { if let Some(did) = ty.def_id(self.cache) { diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index 452e14918..a78893558 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -115,7 +115,7 @@ impl From<DefKind> for ItemType { DefKind::Struct => Self::Struct, DefKind::Union => Self::Union, DefKind::Trait => Self::Trait, - DefKind::TyAlias => Self::Typedef, + DefKind::TyAlias { .. } => Self::Typedef, DefKind::TraitAlias => Self::TraitAlias, DefKind::Macro(kind) => match kind { MacroKind::Bang => ItemType::Macro, @@ -136,7 +136,6 @@ impl From<DefKind> for ItemType { | DefKind::AnonConst | DefKind::InlineConst | DefKind::OpaqueTy - | DefKind::ImplTraitPlaceholder | DefKind::Field | DefKind::LifetimeParam | DefKind::GlobalAsm diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index 6f9cc0266..c49f1a4d3 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -77,8 +77,11 @@ pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>( prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string()); cx.mod_item_in(&item)?; - let (clean::StrippedItem(box clean::ModuleItem(module)) | clean::ModuleItem(module)) = *item.kind - else { unreachable!() }; + let (clean::StrippedItem(box clean::ModuleItem(module)) | clean::ModuleItem(module)) = + *item.kind + else { + unreachable!() + }; for it in module.items { debug!("Adding {:?} to worklist", it.name); work.push((cx.make_child_renderer(), it)); diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 54c0cd2ef..2f611c31a 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -18,7 +18,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty; use rustc_middle::ty::TyCtxt; @@ -109,6 +109,10 @@ impl Buffer { self.buffer } + pub(crate) fn push(&mut self, c: char) { + self.buffer.push(c); + } + pub(crate) fn push_str(&mut self, s: &str) { self.buffer.push_str(s); } @@ -228,9 +232,9 @@ impl clean::GenericParamDef { if let Some(default) = default { if f.alternate() { - write!(f, " = {:#}", default)?; + write!(f, " = {default:#}")?; } else { - write!(f, " = {}", default)?; + write!(f, " = {default}")?; } } @@ -451,9 +455,9 @@ impl clean::GenericBound { hir::TraitBoundModifier::MaybeConst => "", }; if f.alternate() { - write!(f, "{}{:#}", modifier_str, ty.print(cx)) + write!(f, "{modifier_str}{ty:#}", ty = ty.print(cx)) } else { - write!(f, "{}{}", modifier_str, ty.print(cx)) + write!(f, "{modifier_str}{ty}", ty = ty.print(cx)) } } }) @@ -599,12 +603,12 @@ fn generate_macro_def_id_path( let cstore = CStore::from_tcx(tcx); // We need this to prevent a `panic` when this function is used from intra doc links... if !cstore.has_crate_data(def_id.krate) { - debug!("No data for crate {}", crate_name); + debug!("No data for crate {crate_name}"); return Err(HrefError::NotInExternalCache); } // Check to see if it is a macro 2.0 or built-in macro. // More information in <https://rust-lang.github.io/rfcs/1584-macros.html>. - let is_macro_2 = match cstore.load_macro_untracked(def_id, tcx.sess) { + let is_macro_2 = match cstore.load_macro_untracked(def_id, tcx) { LoadedMacro::MacroDef(def, _) => { // If `ast_def.macro_rules` is `true`, then it's not a macro 2.0. matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules) @@ -631,19 +635,18 @@ fn generate_macro_def_id_path( let url = match cache.extern_locations[&def_id.krate] { ExternalLocation::Remote(ref s) => { // `ExternalLocation::Remote` always end with a `/`. - format!("{}{}", s, path.iter().map(|p| p.as_str()).join("/")) + format!("{s}{path}", path = path.iter().map(|p| p.as_str()).join("/")) } ExternalLocation::Local => { // `root_path` always end with a `/`. format!( - "{}{}/{}", - root_path.unwrap_or(""), - crate_name, - path.iter().map(|p| p.as_str()).join("/") + "{root_path}{crate_name}/{path}", + root_path = root_path.unwrap_or(""), + path = path.iter().map(|p| p.as_str()).join("/") ) } ExternalLocation::Unknown => { - debug!("crate {} not in cache when linkifying macros", crate_name); + debug!("crate {crate_name} not in cache when linkifying macros"); return Err(HrefError::NotInExternalCache); } }; @@ -662,6 +665,14 @@ pub(crate) fn href_with_root_path( // documented on their parent's page tcx.parent(did) } + DefKind::ExternCrate => { + // Link to the crate itself, not the `extern crate` item. + if let Some(local_did) = did.as_local() { + tcx.extern_mod_stmt_cnum(local_did).unwrap_or(LOCAL_CRATE).as_def_id() + } else { + did + } + } _ => did, }; let cache = cx.cache(); @@ -724,7 +735,7 @@ pub(crate) fn href_with_root_path( _ => { let prefix = shortty.as_str(); let last = fqp.last().unwrap(); - url_parts.push_fmt(format_args!("{}.{}.html", prefix, last)); + url_parts.push_fmt(format_args!("{prefix}.{last}.html")); } } Ok((url_parts.finish(), shortty, fqp.to_vec())) @@ -771,9 +782,10 @@ pub(crate) fn href_relative_parts<'fqp>( pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Context<'_>) -> String { let cache = cx.cache(); - let Some((fqp, shortty)) = cache.paths.get(&did) - .or_else(|| cache.external_paths.get(&did)) - else { return String::new() }; + let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) + else { + return String::new(); + }; let mut buf = Buffer::new(); let fqp = if *shortty == ItemType::Primitive { // primitives are documented in a crate, but not actually part of it @@ -819,9 +831,9 @@ fn resolved_path<'cx>( let path = if use_absolute { if let Ok((_, _, fqp)) = href(did, cx) { format!( - "{}::{}", - join_with_double_colon(&fqp[..fqp.len() - 1]), - anchor(did, *fqp.last().unwrap(), cx) + "{path}::{anchor}", + path = join_with_double_colon(&fqp[..fqp.len() - 1]), + anchor = anchor(did, *fqp.last().unwrap(), cx) ) } else { last.name.to_string() @@ -829,7 +841,7 @@ fn resolved_path<'cx>( } else { anchor(did, last.name, cx).to_string() }; - write!(w, "{}{}", path, last.args.print(cx))?; + write!(w, "{path}{args}", args = last.args.print(cx))?; } Ok(()) } @@ -897,7 +909,7 @@ fn primitive_link_fragment( None => {} } } - write!(f, "{}", name)?; + f.write_str(name)?; if needs_termination { write!(f, "</a>")?; } @@ -937,15 +949,11 @@ pub(crate) fn anchor<'a, 'cx: 'a>( if let Ok((url, short_ty, fqp)) = parts { write!( f, - r#"<a class="{}" href="{}" title="{} {}">{}</a>"#, - short_ty, - url, - short_ty, - join_with_double_colon(&fqp), - text.as_str() + r#"<a class="{short_ty}" href="{url}" title="{short_ty} {path}">{text}</a>"#, + path = join_with_double_colon(&fqp), ) } else { - write!(f, "{}", text) + f.write_str(text.as_str()) } }) } @@ -956,10 +964,10 @@ fn fmt_type<'cx>( use_absolute: bool, cx: &'cx Context<'_>, ) -> fmt::Result { - trace!("fmt_type(t = {:?})", t); + trace!("fmt_type(t = {t:?})"); match *t { - clean::Generic(name) => write!(f, "{}", name), + clean::Generic(name) => f.write_str(name.as_str()), clean::Type::Path { ref path } => { // Paths like `T::Output` and `Self::Output` should be rendered with all segments. let did = path.def_id(); @@ -1076,13 +1084,13 @@ fn fmt_type<'cx>( if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() { let text = if f.alternate() { - format!("*{} {:#}", m, t.print(cx)) + format!("*{m} {ty:#}", ty = t.print(cx)) } else { - format!("*{} {}", m, t.print(cx)) + format!("*{m} {ty}", ty = t.print(cx)) }; primitive_link(f, clean::PrimitiveType::RawPointer, &text, cx) } else { - primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), cx)?; + primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{m} "), cx)?; fmt::Display::fmt(&t.print(cx), f) } } @@ -1093,22 +1101,35 @@ fn fmt_type<'cx>( }; let m = mutability.print_with_space(); let amp = if f.alternate() { "&" } else { "&" }; - match **ty { + + if let clean::Generic(name) = **ty { + return primitive_link( + f, + PrimitiveType::Reference, + &format!("{amp}{lt}{m}{name}"), + cx, + ); + } + + write!(f, "{amp}{lt}{m}")?; + + let needs_parens = match **ty { clean::DynTrait(ref bounds, ref trait_lt) if bounds.len() > 1 || trait_lt.is_some() => { - write!(f, "{}{}{}(", amp, lt, m)?; - fmt_type(ty, f, use_absolute, cx)?; - write!(f, ")") - } - clean::Generic(name) => { - primitive_link(f, PrimitiveType::Reference, &format!("{amp}{lt}{m}{name}"), cx) - } - _ => { - write!(f, "{}{}{}", amp, lt, m)?; - fmt_type(ty, f, use_absolute, cx) + true } + clean::ImplTrait(ref bounds) if bounds.len() > 1 => true, + _ => false, + }; + if needs_parens { + f.write_str("(")?; + } + fmt_type(ty, f, use_absolute, cx)?; + if needs_parens { + f.write_str(")")?; } + Ok(()) } clean::ImplTrait(ref bounds) => { if f.alternate() { @@ -1423,11 +1444,20 @@ impl clean::FnDecl { clean::SelfValue => { write!(f, "self")?; } - clean::SelfBorrowed(Some(ref lt), mtbl) => { - write!(f, "{}{} {}self", amp, lt.print(), mtbl.print_with_space())?; + clean::SelfBorrowed(Some(ref lt), mutability) => { + write!( + f, + "{amp}{lifetime} {mutability}self", + lifetime = lt.print(), + mutability = mutability.print_with_space(), + )?; } - clean::SelfBorrowed(None, mtbl) => { - write!(f, "{}{}self", amp, mtbl.print_with_space())?; + clean::SelfBorrowed(None, mutability) => { + write!( + f, + "{amp}{mutability}self", + mutability = mutability.print_with_space(), + )?; } clean::SelfExplicit(ref typ) => { write!(f, "self: ")?; @@ -1501,7 +1531,7 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>( "pub(super) ".into() } else { let path = cx.tcx().def_path(vis_did); - debug!("path={:?}", path); + debug!("path={path:?}"); // modified from `resolved_path()` to work with `DefPathData` let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); let anchor = anchor(vis_did, last_name, cx); @@ -1510,12 +1540,12 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>( for seg in &path.data[..path.data.len() - 1] { let _ = write!(s, "{}::", seg.data.get_opt_name().unwrap()); } - let _ = write!(s, "{}) ", anchor); + let _ = write!(s, "{anchor}) "); s.into() } } }; - display_fn(move |f| write!(f, "{}", to_print)) + display_fn(move |f| f.write_str(&to_print)) } /// This function is the same as print_with_space, except that it renders no links. @@ -1610,7 +1640,7 @@ impl clean::Import { if name == self.source.path.last() { write!(f, "use {};", self.source.print(cx)) } else { - write!(f, "use {} as {};", self.source.print(cx), name) + write!(f, "use {source} as {name};", source = self.source.print(cx)) } } clean::ImportKind::Glob => { @@ -1639,7 +1669,7 @@ impl clean::ImportSource { if let hir::def::Res::PrimTy(p) = self.path.res { primitive_link(f, PrimitiveType::from(p), name.as_str(), cx)?; } else { - write!(f, "{}", name)?; + f.write_str(name.as_str())?; } Ok(()) } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index c94968b48..039e8cdb9 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -928,18 +928,16 @@ fn string_without_closing_tag<T: Display>( href_context: &Option<HrefContext<'_, '_>>, open_tag: bool, ) -> Option<&'static str> { - let Some(klass) = klass - else { - write!(out, "{}", text).unwrap(); + let Some(klass) = klass else { + write!(out, "{text}").unwrap(); return None; }; - let Some(def_span) = klass.get_span() - else { + let Some(def_span) = klass.get_span() else { if !open_tag { - write!(out, "{}", text).unwrap(); + write!(out, "{text}").unwrap(); return None; } - write!(out, "<span class=\"{}\">{}", klass.as_html(), text).unwrap(); + write!(out, "<span class=\"{klass}\">{text}", klass = klass.as_html()).unwrap(); return Some("</span>"); }; @@ -949,14 +947,17 @@ fn string_without_closing_tag<T: Display>( match t { "self" | "Self" => write!( &mut path, - "<span class=\"{}\">{}</span>", - Class::Self_(DUMMY_SP).as_html(), - t + "<span class=\"{klass}\">{t}</span>", + klass = Class::Self_(DUMMY_SP).as_html(), ), "crate" | "super" => { - write!(&mut path, "<span class=\"{}\">{}</span>", Class::KeyWord.as_html(), t) + write!( + &mut path, + "<span class=\"{klass}\">{t}</span>", + klass = Class::KeyWord.as_html(), + ) } - t => write!(&mut path, "{}", t), + t => write!(&mut path, "{t}"), } .expect("Failed to build source HTML path"); path @@ -988,19 +989,24 @@ fn string_without_closing_tag<T: Display>( ) .ok() .map(|(url, _, _)| url), + LinkFromSrc::Doc(def_id) => { + format::href_with_root_path(*def_id, context, Some(&href_context.root_path)) + .ok() + .map(|(doc_link, _, _)| doc_link) + } } }) { if !open_tag { // We're already inside an element which has the same klass, no need to give it // again. - write!(out, "<a href=\"{}\">{}", href, text_s).unwrap(); + write!(out, "<a href=\"{href}\">{text_s}").unwrap(); } else { let klass_s = klass.as_html(); if klass_s.is_empty() { - write!(out, "<a href=\"{}\">{}", href, text_s).unwrap(); + write!(out, "<a href=\"{href}\">{text_s}").unwrap(); } else { - write!(out, "<a class=\"{}\" href=\"{}\">{}", klass_s, href, text_s).unwrap(); + write!(out, "<a class=\"{klass_s}\" href=\"{href}\">{text_s}").unwrap(); } } return Some("</a>"); @@ -1015,7 +1021,7 @@ fn string_without_closing_tag<T: Display>( out.write_str(&text_s).unwrap(); Some("") } else { - write!(out, "<span class=\"{}\">{}", klass_s, text_s).unwrap(); + write!(out, "<span class=\"{klass_s}\">{text_s}").unwrap(); Some("</span>") } } diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index 2c93b9a09..4c0874a68 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -23,7 +23,7 @@ fn test_html_highlighting() { let html = { let mut out = Buffer::new(); write_code(&mut out, src, None, None); - format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner()) + format!("{STYLE}<pre><code>{}</code></pre>\n", out.into_inner()) }; expect_file!["fixtures/sample.html"].assert_eq(&html); }); diff --git a/src/librustdoc/html/length_limit.rs b/src/librustdoc/html/length_limit.rs index 4c8db2c67..8562e103d 100644 --- a/src/librustdoc/html/length_limit.rs +++ b/src/librustdoc/html/length_limit.rs @@ -78,8 +78,7 @@ impl HtmlWithLimit { pub(super) fn open_tag(&mut self, tag_name: &'static str) { assert!( tag_name.chars().all(|c| ('a'..='z').contains(&c)), - "tag_name contained non-alphabetic chars: {:?}", - tag_name + "tag_name contained non-alphabetic chars: {tag_name:?}", ); self.queued_tags.push(tag_name); } @@ -88,7 +87,7 @@ impl HtmlWithLimit { pub(super) fn close_tag(&mut self) { match self.unclosed_tags.pop() { // Close the most recently opened tag. - Some(tag_name) => write!(self.buf, "</{}>", tag_name).unwrap(), + Some(tag_name) => write!(self.buf, "</{tag_name}>").unwrap(), // There are valid cases where `close_tag()` is called without // there being any tags to close. For example, this occurs when // a tag is opened after the length limit is exceeded; @@ -101,7 +100,7 @@ impl HtmlWithLimit { /// Write all queued tags and add them to the `unclosed_tags` list. fn flush_queue(&mut self) { for tag_name in self.queued_tags.drain(..) { - write!(self.buf, "<{}>", tag_name).unwrap(); + write!(self.buf, "<{tag_name}>").unwrap(); self.unclosed_tags.push(tag_name); } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index fd00277e2..98cc38a10 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -50,7 +50,7 @@ use crate::html::render::small_url_encode; use crate::html::toc::TocBuilder; use pulldown_cmark::{ - html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, + html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, OffsetIter, Options, Parser, Tag, }; #[cfg(test)] @@ -246,10 +246,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { return Some(Event::Html( format!( "<div class=\"example-wrap\">\ - <pre class=\"language-{}\"><code>{}</code></pre>\ + <pre class=\"language-{lang}\"><code>{text}</code></pre>\ </div>", - lang, - Escape(&original_text), + text = Escape(&original_text), ) .into(), )); @@ -288,8 +287,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { let test_escaped = small_url_encode(test); Some(format!( - r#"<a class="test-arrow" target="_blank" href="{}?code={}{}&edition={}">Run</a>"#, - url, test_escaped, channel, edition, + "<a class=\"test-arrow\" \ + target=\"_blank\" \ + href=\"{url}?code={test_escaped}{channel}&edition={edition}\">Run</a>", )) }); @@ -308,7 +308,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { // insert newline to clearly separate it from the // previous block so we can shorten the html output let mut s = Buffer::new(); - s.push_str("\n"); + s.push('\n'); highlight::render_example_with_highlighting( &text, @@ -349,7 +349,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> { dest, title, ))) => { - debug!("saw start of shortcut link to {} with title {}", dest, title); + debug!("saw start of shortcut link to {dest} with title {title}"); // If this is a shortcut link, it was resolved by the broken_link_callback. // So the URL will already be updated properly. let link = self.links.iter().find(|&link| *link.href == **dest); @@ -370,7 +370,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> { dest, _, ))) => { - debug!("saw end of shortcut link to {}", dest); + debug!("saw end of shortcut link to {dest}"); if self.links.iter().any(|link| *link.href == **dest) { assert!(self.shortcut_link.is_some(), "saw closing link without opening tag"); self.shortcut_link = None; @@ -379,7 +379,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> { // Handle backticks in inline code blocks, but only if we're in the middle of a shortcut link. // [`fn@f`] Some(Event::Code(text)) => { - trace!("saw code {}", text); + trace!("saw code {text}"); if let Some(link) = self.shortcut_link { // NOTE: this only replaces if the code block is the *entire* text. // If only part of the link has code highlighting, the disambiguator will not be removed. @@ -394,7 +394,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> { l.href == link.href && Some(&**text) == l.original_text.get(1..l.original_text.len() - 1) }) { - debug!("replacing {} with {}", text, link.new_text); + debug!("replacing {text} with {new_text}", new_text = link.new_text); *text = CowStr::Borrowed(&link.new_text); } } @@ -402,7 +402,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> { // Replace plain text in links, but only in the middle of a shortcut link. // [fn@f] Some(Event::Text(text)) => { - trace!("saw text {}", text); + trace!("saw text {text}"); if let Some(link) = self.shortcut_link { // NOTE: same limitations as `Event::Code` if let Some(link) = self @@ -410,7 +410,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> { .iter() .find(|l| l.href == link.href && **text == *l.original_text) { - debug!("replacing {} with {}", text, link.new_text); + debug!("replacing {text} with {new_text}", new_text = link.new_text); *text = CowStr::Borrowed(&link.new_text); } } @@ -522,18 +522,16 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator let mut html_header = String::new(); html::push_html(&mut html_header, self.buf.iter().map(|(ev, _)| ev.clone())); let sec = builder.push(level as u32, html_header, id.clone()); - self.buf.push_front((Event::Html(format!("{} ", sec).into()), 0..0)); + self.buf.push_front((Event::Html(format!("{sec} ").into()), 0..0)); } let level = std::cmp::min(level as u32 + (self.heading_offset as u32), MAX_HEADER_LEVEL); - self.buf.push_back((Event::Html(format!("</a></h{}>", level).into()), 0..0)); + self.buf.push_back((Event::Html(format!("</a></h{level}>").into()), 0..0)); let start_tags = format!( "<h{level} id=\"{id}\">\ <a href=\"#{id}\">", - id = id, - level = level ); return Some((Event::Html(start_tags.into()), 0..0)); } @@ -683,14 +681,14 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> { v.sort_by(|a, b| a.1.cmp(&b.1)); let mut ret = String::from("<div class=\"footnotes\"><hr><ol>"); for (mut content, id) in v { - write!(ret, "<li id=\"fn{}\">", id).unwrap(); + write!(ret, "<li id=\"fn{id}\">").unwrap(); let mut is_paragraph = false; if let Some(&Event::End(Tag::Paragraph)) = content.last() { content.pop(); is_paragraph = true; } html::push_html(&mut ret, content.into_iter()); - write!(ret, " <a href=\"#fnref{}\">↩</a>", id).unwrap(); + write!(ret, " <a href=\"#fnref{id}\">↩</a>").unwrap(); if is_paragraph { ret.push_str("</p>"); } @@ -961,7 +959,7 @@ impl LangString { } { if let Some(extra) = extra { extra.error_invalid_codeblock_attr( - format!("unknown attribute `{}`. Did you mean `{}`?", x, flag), + format!("unknown attribute `{x}`. Did you mean `{flag}`?"), help, ); } @@ -1040,7 +1038,7 @@ impl MarkdownWithToc<'_> { html::push_html(&mut s, p); } - format!("<nav id=\"TOC\">{}</nav>{}", toc.into_toc().print(), s) + format!("<nav id=\"TOC\">{toc}</nav>{s}", toc = toc.into_toc().print()) } } @@ -1242,6 +1240,7 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin pub(crate) struct MarkdownLink { pub kind: LinkType, pub link: String, + pub display_text: Option<String>, pub range: MarkdownLinkRange, } @@ -1265,8 +1264,8 @@ impl MarkdownLinkRange { } } -pub(crate) fn markdown_links<R>( - md: &str, +pub(crate) fn markdown_links<'md, R>( + md: &'md str, preprocess_link: impl Fn(MarkdownLink) -> Option<R>, ) -> Vec<R> { if md.is_empty() { @@ -1377,32 +1376,90 @@ pub(crate) fn markdown_links<R>( MarkdownLinkRange::Destination(range.clone()) }; - Parser::new_with_broken_link_callback( + let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into())); + let mut event_iter = Parser::new_with_broken_link_callback( md, main_body_opts(), - Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))), + Some(&mut broken_link_callback), ) - .into_offset_iter() - .filter_map(|(event, span)| match event { - Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { - let range = match link_type { - // Link is pulled from the link itself. - LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => { - span_for_offset_backward(span, b'[', b']') - } - LinkType::CollapsedUnknown => span_for_offset_forward(span, b'[', b']'), - LinkType::Inline => span_for_offset_backward(span, b'(', b')'), - // Link is pulled from elsewhere in the document. - LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => { - span_for_link(&dest, span) + .into_offset_iter(); + let mut links = Vec::new(); + + while let Some((event, span)) = event_iter.next() { + match event { + Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => { + let range = match link_type { + // Link is pulled from the link itself. + LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => { + span_for_offset_backward(span, b'[', b']') + } + LinkType::CollapsedUnknown => span_for_offset_forward(span, b'[', b']'), + LinkType::Inline => span_for_offset_backward(span, b'(', b')'), + // Link is pulled from elsewhere in the document. + LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => { + span_for_link(&dest, span) + } + LinkType::Autolink | LinkType::Email => unreachable!(), + }; + + let display_text = if matches!( + link_type, + LinkType::Inline + | LinkType::ReferenceUnknown + | LinkType::Reference + | LinkType::Shortcut + | LinkType::ShortcutUnknown + ) { + collect_link_data(&mut event_iter) + } else { + None + }; + + if let Some(link) = preprocess_link(MarkdownLink { + kind: link_type, + link: dest.into_string(), + display_text, + range, + }) { + links.push(link); } - LinkType::Autolink | LinkType::Email => unreachable!(), - }; - preprocess_link(MarkdownLink { kind: link_type, range, link: dest.into_string() }) + } + _ => {} } - _ => None, - }) - .collect() + } + + links +} + +/// Collects additional data of link. +fn collect_link_data<'input, 'callback>( + event_iter: &mut OffsetIter<'input, 'callback>, +) -> Option<String> { + let mut display_text: Option<String> = None; + let mut append_text = |text: CowStr<'_>| { + if let Some(display_text) = &mut display_text { + display_text.push_str(&text); + } else { + display_text = Some(text.to_string()); + } + }; + + while let Some((event, _span)) = event_iter.next() { + match event { + Event::Text(text) => { + append_text(text); + } + Event::Code(code) => { + append_text(code); + } + Event::End(_) => { + break; + } + _ => {} + } + } + + display_text } #[derive(Debug)] diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index e05635a02..db8504d15 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -38,7 +38,7 @@ fn test_unique_id() { ]; let mut map = IdMap::new(); - let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect(); + let actual: Vec<String> = input.iter().map(|s| map.derive(s)).collect(); assert_eq!(&actual[..], expected); } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 4c4762636..d7ff248a9 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -162,7 +162,7 @@ impl<'tcx> Context<'tcx> { self.shared.tcx.sess } - pub(super) fn derive_id(&mut self, id: String) -> String { + pub(super) fn derive_id<S: AsRef<str> + ToString>(&mut self, id: S) -> String { self.id_map.derive(id) } @@ -206,15 +206,14 @@ impl<'tcx> Context<'tcx> { format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate) } else { format!( - "API documentation for the Rust `{}` {} in crate `{}`.", - it.name.as_ref().unwrap(), - tyname, - self.shared.layout.krate + "API documentation for the Rust `{name}` {tyname} in crate `{krate}`.", + name = it.name.as_ref().unwrap(), + krate = self.shared.layout.krate, ) }; let name; let tyname_s = if it.is_crate() { - name = format!("{} crate", tyname); + name = format!("{tyname} crate"); name.as_str() } else { tyname.as_str() @@ -264,7 +263,12 @@ impl<'tcx> Context<'tcx> { current_path.push_str(&item_path(ty, names.last().unwrap().as_str())); redirections.borrow_mut().insert(current_path, path); } - None => return layout::redirect(&format!("{}{}", self.root_path(), path)), + None => { + return layout::redirect(&format!( + "{root}{path}", + root = self.root_path() + )); + } } } } @@ -382,11 +386,7 @@ impl<'tcx> Context<'tcx> { let hiline = span.hi(self.sess()).line; format!( "#{}", - if loline == hiline { - loline.to_string() - } else { - format!("{}-{}", loline, hiline) - } + if loline == hiline { loline.to_string() } else { format!("{loline}-{hiline}") } ) } else { "".to_string() @@ -798,15 +798,18 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { if let Some(def_id) = item.def_id() && self.cache().inlined_items.contains(&def_id) { self.is_inside_inlined_module = true; } - } else if item.is_doc_hidden() { + } else if !self.cache().document_hidden && item.is_doc_hidden() { // We're not inside an inlined module anymore since this one cannot be re-exported. self.is_inside_inlined_module = false; } // Render sidebar-items.js used throughout this module. if !self.render_redirect_pages { - let (clean::StrippedItem(box clean::ModuleItem(ref module)) | clean::ModuleItem(ref module)) = *item.kind - else { unreachable!() }; + let (clean::StrippedItem(box clean::ModuleItem(ref module)) + | clean::ModuleItem(ref module)) = *item.kind + else { + unreachable!() + }; let items = self.build_sidebar_items(module); let js_dst = self.dst.join(&format!("sidebar-items{}.js", self.shared.resource_suffix)); let v = format!("window.SIDEBAR_ITEMS = {};", serde_json::to_string(&items).unwrap()); @@ -852,12 +855,12 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { // If the item is a macro, redirect from the old macro URL (with !) // to the new one (without). if item_type == ItemType::Macro { - let redir_name = format!("{}.{}!.html", item_type, name); + let redir_name = format!("{item_type}.{name}!.html"); if let Some(ref redirections) = self.shared.redirections { let crate_name = &self.shared.layout.krate; redirections.borrow_mut().insert( - format!("{}/{}", crate_name, redir_name), - format!("{}/{}", crate_name, file_name), + format!("{crate_name}/{redir_name}"), + format!("{crate_name}/{file_name}"), ); } else { let v = layout::redirect(file_name); diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f923f9054..ac9c180a6 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -85,7 +85,7 @@ use crate::DOC_RUST_LANG_ORG_CHANNEL; pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { crate::html::format::display_fn(move |f| { - if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) } + if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) } }) } @@ -268,7 +268,7 @@ impl AllTypes { fn append(&mut self, item_name: String, item_type: &ItemType) { let mut url: Vec<_> = item_name.split("::").skip(1).collect(); if let Some(name) = url.pop() { - let new_url = format!("{}/{}.{}.html", url.join("/"), item_type, name); + let new_url = format!("{}/{item_type}.{name}.html", url.join("/")); url.push(name); let name = url.join("::"); match *item_type { @@ -385,16 +385,17 @@ impl AllTypes { fn scrape_examples_help(shared: &SharedContext<'_>) -> String { let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned(); content.push_str(&format!( - "## More information\n\n\ - If you want more information about this feature, please read the [corresponding chapter in the Rustdoc book]({}/rustdoc/scraped-examples.html).", - DOC_RUST_LANG_ORG_CHANNEL)); + "## More information\n\n\ + If you want more information about this feature, please read the [corresponding chapter in \ + the Rustdoc book]({DOC_RUST_LANG_ORG_CHANNEL}/rustdoc/scraped-examples.html)." + )); let mut ids = IdMap::default(); format!( "<div class=\"main-heading\">\ - <h1>About scraped examples</h1>\ - </div>\ - <div>{}</div>", + <h1>About scraped examples</h1>\ + </div>\ + <div>{}</div>", Markdown { content: &content, links: &[], @@ -415,7 +416,7 @@ fn document<'a, 'cx: 'a>( heading_offset: HeadingOffset, ) -> impl fmt::Display + 'a + Captures<'cx> { if let Some(ref name) = item.name { - info!("Documenting {}", name); + info!("Documenting {name}"); } display_fn(move |f| { @@ -473,7 +474,7 @@ fn document_short<'a, 'cx: 'a>( MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content(); if has_more_content { - let link = format!(r#" <a{}>Read more</a>"#, assoc_href_attr(item, link, cx)); + let link = format!(" <a{}>Read more</a>", assoc_href_attr(item, link, cx)); if let Some(idx) = summary_html.rfind("</p>") { summary_html.insert_str(idx, &link); @@ -482,7 +483,7 @@ fn document_short<'a, 'cx: 'a>( } } - write!(f, "<div class='docblock'>{}</div>", summary_html)?; + write!(f, "<div class='docblock'>{summary_html}</div>")?; } Ok(()) }) @@ -512,14 +513,14 @@ fn document_full_inner<'a, 'cx: 'a>( ) -> impl fmt::Display + 'a + Captures<'cx> { display_fn(move |f| { if let Some(s) = item.opt_doc_value() { - debug!("Doc block: =====\n{}\n=====", s); + debug!("Doc block: =====\n{s}\n====="); if is_collapsible { write!( f, "<details class=\"toggle top-doc\" open>\ - <summary class=\"hideme\">\ + <summary class=\"hideme\">\ <span>Expand description</span>\ - </summary>{}</details>", + </summary>{}</details>", render_markdown(cx, &s, item.links(cx), heading_offset) )?; } else { @@ -564,12 +565,10 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<Strin }; debug!( - "Portability {:?} {:?} (parent: {:?}) - {:?} = {:?}", - item.name, - item.cfg, - parent, - parent.and_then(|p| p.cfg.as_ref()), - cfg + "Portability {name:?} {item_cfg:?} (parent: {parent:?}) - {parent_cfg:?} = {cfg:?}", + name = item.name, + item_cfg = item.cfg, + parent_cfg = parent.and_then(|p| p.cfg.as_ref()), ); Some(cfg?.render_long_html()) @@ -701,8 +700,8 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) let item_type = it.type_(); let href = match link { - AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{}", id)), - AssocItemLink::Anchor(None) => Some(format!("#{}.{}", item_type, name)), + AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{id}")), + AssocItemLink::Anchor(None) => Some(format!("#{item_type}.{name}")), AssocItemLink::GotoSource(did, provided_methods) => { // We're creating a link from the implementation of an associated item to its // declaration in the trait declaration. @@ -722,7 +721,7 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) }; match href(did.expect_def_id(), cx) { - Ok((url, ..)) => Some(format!("{}#{}.{}", url, item_type, name)), + Ok((url, ..)) => Some(format!("{url}#{item_type}.{name}")), // The link is broken since it points to an external crate that wasn't documented. // Do not create any link in such case. This is better than falling back to a // dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item @@ -735,37 +734,39 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) // In this scenario, the actual `id` of this impl item would be // `#{item_type}.{name}-{n}` for some number `n` (a disambiguator). Err(HrefError::DocumentationNotBuilt) => None, - Err(_) => Some(format!("#{}.{}", item_type, name)), + Err(_) => Some(format!("#{item_type}.{name}")), } } }; // If there is no `href` for the reason explained above, simply do not render it which is valid: // https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements - href.map(|href| format!(" href=\"{}\"", href)).unwrap_or_default() + href.map(|href| format!(" href=\"{href}\"")).unwrap_or_default() } fn assoc_const( w: &mut Buffer, it: &clean::Item, + generics: &clean::Generics, ty: &clean::Type, default: Option<&clean::ConstantKind>, link: AssocItemLink<'_>, - extra: &str, + indent: usize, cx: &Context<'_>, ) { let tcx = cx.tcx(); write!( w, - "{extra}{vis}const <a{href} class=\"constant\">{name}</a>: {ty}", - extra = extra, + "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}", + indent = " ".repeat(indent), vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), href = assoc_href_attr(it, link, cx), name = it.name.as_ref().unwrap(), + generics = generics.print(cx), ty = ty.print(cx), ); if let Some(default) = default { - write!(w, " = "); + w.write_str(" = "); // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the // hood which adds noisy underscores and a type suffix to number literals. @@ -774,6 +775,7 @@ fn assoc_const( // Find a way to print constants here without all that jazz. write!(w, "{}", Escape(&default.value(tcx).unwrap_or_else(|| default.expr(tcx)))); } + write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline)); } fn assoc_type( @@ -820,6 +822,7 @@ fn assoc_method( let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item"); let name = meth.name.as_ref().unwrap(); let vis = visibility_print_with_space(meth.visibility(tcx), meth.item_id, cx).to_string(); + let defaultness = print_default_space(meth.is_default()); // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove // this condition. let constness = match render_mode { @@ -830,7 +833,6 @@ fn assoc_method( }; let asyncness = header.asyncness.print_with_space(); let unsafety = header.unsafety.print_with_space(); - let defaultness = print_default_space(meth.is_default()); let abi = print_abi_with_space(header.abi).to_string(); let href = assoc_href_attr(meth, link, cx); @@ -838,10 +840,10 @@ fn assoc_method( let generics_len = format!("{:#}", g.print(cx)).len(); let mut header_len = "fn ".len() + vis.len() + + defaultness.len() + constness.len() + asyncness.len() + unsafety.len() - + defaultness.len() + abi.len() + name.as_str().len() + generics_len; @@ -860,14 +862,14 @@ fn assoc_method( w.reserve(header_len + "<a href=\"\" class=\"fn\">{".len() + "</a>".len()); write!( w, - "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn \ + "{indent}{vis}{defaultness}{constness}{asyncness}{unsafety}{abi}fn \ <a{href} class=\"fn\">{name}</a>{generics}{decl}{notable_traits}{where_clause}", indent = indent_str, vis = vis, + defaultness = defaultness, constness = constness, asyncness = asyncness, unsafety = unsafety, - defaultness = defaultness, abi = abi, href = href, name = name, @@ -907,39 +909,41 @@ fn render_stability_since_raw_with_extra( if let Some(ver) = stable_version { stability.push_str(ver.as_str()); - title.push_str(&format!("Stable since Rust version {}", ver)); + title.push_str(&format!("Stable since Rust version {ver}")); } let const_title_and_stability = match const_stability { Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) if Some(since) != containing_const_ver => { - Some((format!("const since {}", since), format!("const: {}", since))) + Some((format!("const since {since}"), format!("const: {since}"))) } Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => { let unstable = if let Some(n) = issue { format!( - r#"<a href="https://github.com/rust-lang/rust/issues/{}" title="Tracking issue for {}">unstable</a>"#, - n, feature + "<a \ + href=\"https://github.com/rust-lang/rust/issues/{n}\" \ + title=\"Tracking issue for {feature}\"\ + >unstable</a>" ) } else { String::from("unstable") }; - Some((String::from("const unstable"), format!("const: {}", unstable))) + Some((String::from("const unstable"), format!("const: {unstable}"))) } _ => None, }; if let Some((const_title, const_stability)) = const_title_and_stability { if !title.is_empty() { - title.push_str(&format!(", {}", const_title)); + title.push_str(&format!(", {const_title}")); } else { title.push_str(&const_title); } if !stability.is_empty() { - stability.push_str(&format!(" ({})", const_stability)); + stability.push_str(&format!(" ({const_stability})")); } else { stability.push_str(&const_stability); } @@ -986,19 +990,22 @@ fn render_assoc_item( clean::MethodItem(m, _) => { assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode) } - kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => assoc_const( - w, - item, - ty, - match kind { - clean::TyAssocConstItem(_) => None, - clean::AssocConstItem(_, default) => Some(default), - _ => unreachable!(), - }, - link, - if parent == ItemType::Trait { " " } else { "" }, - cx, - ), + kind @ (clean::TyAssocConstItem(generics, ty) | clean::AssocConstItem(generics, ty, _)) => { + assoc_const( + w, + item, + generics, + ty, + match kind { + clean::TyAssocConstItem(..) => None, + clean::AssocConstItem(.., default) => Some(default), + _ => unreachable!(), + }, + link, + if parent == ItemType::Trait { 4 } else { 0 }, + cx, + ) + } clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type( w, item, @@ -1032,7 +1039,7 @@ fn render_attributes_in_pre<'a, 'b: 'a>( ) -> impl fmt::Display + Captures<'a> + Captures<'b> { crate::html::format::display_fn(move |f| { for a in it.attributes(tcx, false) { - writeln!(f, "{}{}", prefix, a)?; + writeln!(f, "{prefix}{a}")?; } Ok(()) }) @@ -1085,7 +1092,7 @@ pub(crate) fn render_all_impls( let impls = impls.into_inner(); if !impls.is_empty() { write_impl_section_heading(&mut w, "Trait Implementations", "trait-implementations"); - write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls).unwrap(); + write!(w, "<div id=\"trait-implementations-list\">{impls}</div>").unwrap(); } if !synthetic.is_empty() { @@ -1144,9 +1151,7 @@ fn render_assoc_items_inner( AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { let id = cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); - if let Some(def_id) = type_.def_id(cx.cache()) { - cx.deref_id_map.insert(def_id, id.clone()); - } + let derived_id = cx.derive_id(&id); write_impl_section_heading( &mut tmp_buf, &format!( @@ -1156,11 +1161,10 @@ fn render_assoc_items_inner( ), &id, ); - ( - RenderMode::ForDeref { mut_: deref_mut_ }, - cx.derive_id(id), - r#" class="impl-items""#, - ) + if let Some(def_id) = type_.def_id(cx.cache()) { + cx.deref_id_map.insert(def_id, id); + } + (RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#) } }; let mut impls_buf = Buffer::html(); @@ -1183,10 +1187,13 @@ fn render_assoc_items_inner( ); } if !impls_buf.is_empty() { - write!(w, "{}", tmp_buf.into_inner()).unwrap(); - write!(w, "<div id=\"{id}\"{class_html}>").unwrap(); - write!(w, "{}", impls_buf.into_inner()).unwrap(); - w.write_str("</div>").unwrap(); + write!( + w, + "{}<div id=\"{id}\"{class_html}>{}</div>", + tmp_buf.into_inner(), + impls_buf.into_inner() + ) + .unwrap(); } } @@ -1236,7 +1243,10 @@ fn render_deref_methods( _ => None, }) .expect("Expected associated type binding"); - debug!("Render deref methods for {:#?}, target {:#?}", impl_.inner_impl().for_, target); + debug!( + "Render deref methods for {for_:#?}, target {target:#?}", + for_ = impl_.inner_impl().for_ + ); let what = AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; if let Some(did) = target.def_id(cache) { @@ -1386,7 +1396,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { } } if out.is_empty() { - write!(&mut out, "</code></pre>",); + out.write_str("</code></pre>"); } (format!("{:#}", ty.print(cx)), out.into_inner()) @@ -1532,25 +1542,25 @@ fn render_impl( let toggled = !doc_buffer.is_empty(); if toggled { let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; - write!(w, "<details class=\"toggle{}\" open><summary>", method_toggle_class); + write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>"); } match &*item.kind { clean::MethodItem(..) | clean::TyMethodItem(_) => { // Only render when the method is not static or we allow static methods if render_method_item { - let id = cx.derive_id(format!("{}.{}", item_type, name)); + let id = cx.derive_id(format!("{item_type}.{name}")); let source_id = trait_ .and_then(|trait_| { trait_.items.iter().find(|item| { item.name.map(|n| n.as_str().eq(name.as_str())).unwrap_or(false) }) }) - .map(|item| format!("{}.{}", item.type_(), name)); - write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,); + .map(|item| format!("{}.{name}", item.type_())); + write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"); render_rightside(w, cx, item, containing_item, render_mode); if trait_.is_some() { // Anchors are only used on trait impls. - write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id); + write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>"); } w.write_str("<h4 class=\"code-header\">"); render_assoc_item( @@ -1561,43 +1571,43 @@ fn render_impl( cx, render_mode, ); - w.write_str("</h4>"); - w.write_str("</section>"); + w.write_str("</h4></section>"); } } - kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => { - let source_id = format!("{}.{}", item_type, name); - let id = cx.derive_id(source_id.clone()); - write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class); + kind @ (clean::TyAssocConstItem(generics, ty) + | clean::AssocConstItem(generics, ty, _)) => { + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"); render_rightside(w, cx, item, containing_item, render_mode); if trait_.is_some() { // Anchors are only used on trait impls. - write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id); + write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>"); } w.write_str("<h4 class=\"code-header\">"); assoc_const( w, item, + generics, ty, match kind { - clean::TyAssocConstItem(_) => None, - clean::AssocConstItem(_, default) => Some(default), + clean::TyAssocConstItem(..) => None, + clean::AssocConstItem(.., default) => Some(default), _ => unreachable!(), }, link.anchor(if trait_.is_some() { &source_id } else { &id }), - "", + 0, cx, ); - w.write_str("</h4>"); - w.write_str("</section>"); + w.write_str("</h4></section>"); } clean::TyAssocTypeItem(generics, bounds) => { - let source_id = format!("{}.{}", item_type, name); - let id = cx.derive_id(source_id.clone()); - write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class); + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"); if trait_.is_some() { // Anchors are only used on trait impls. - write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id); + write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>"); } w.write_str("<h4 class=\"code-header\">"); assoc_type( @@ -1610,16 +1620,15 @@ fn render_impl( 0, cx, ); - w.write_str("</h4>"); - w.write_str("</section>"); + w.write_str("</h4></section>"); } clean::AssocTypeItem(tydef, _bounds) => { - let source_id = format!("{}.{}", item_type, name); - let id = cx.derive_id(source_id.clone()); - write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class); + let source_id = format!("{item_type}.{name}"); + let id = cx.derive_id(&source_id); + write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">"); if trait_.is_some() { // Anchors are only used on trait impls. - write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id); + write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>"); } w.write_str("<h4 class=\"code-header\">"); assoc_type( @@ -1632,8 +1641,7 @@ fn render_impl( 0, cx, ); - w.write_str("</h4>"); - w.write_str("</section>"); + w.write_str("</h4></section>"); } clean::StrippedItem(..) => return, _ => panic!("can't make docs for trait item with name {:?}", item.name), @@ -1678,11 +1686,11 @@ fn render_impl( rendering_params: ImplRenderingParameters, ) { for trait_item in &t.items { - // Skip over any default trait items that are impossible to call + // Skip over any default trait items that are impossible to reference // (e.g. if it has a `Self: Sized` bound on an unsized type). if let Some(impl_def_id) = parent.item_id.as_def_id() && let Some(trait_item_def_id) = trait_item.item_id.as_def_id() - && cx.tcx().is_impossible_method((impl_def_id, trait_item_def_id)) + && cx.tcx().is_impossible_associated_item((impl_def_id, trait_item_def_id)) { continue; } @@ -1736,10 +1744,10 @@ fn render_impl( close_tags.insert_str(0, "</details>"); write!( w, - "<details class=\"toggle implementors-toggle\"{}>", + "<details class=\"toggle implementors-toggle\"{}>\ + <summary>", if rendering_params.toggle_open_by_default { " open" } else { "" } ); - write!(w, "<summary>") } render_impl_summary( w, @@ -1752,15 +1760,15 @@ fn render_impl( aliases, ); if toggled { - write!(w, "</summary>") + w.write_str("</summary>"); } if let Some(ref dox) = i.impl_item.opt_doc_value() { if trait_.is_none() && i.inner_impl().items.is_empty() { w.write_str( "<div class=\"item-info\">\ - <div class=\"stab empty-impl\">This impl block contains no items.</div>\ - </div>", + <div class=\"stab empty-impl\">This impl block contains no items.</div>\ + </div>", ); } write!( @@ -1819,11 +1827,11 @@ fn render_rightside( const_stable_since, if has_src_ref { "" } else { " rightside" }, ); - if let Some(l) = src_href { + if let Some(link) = src_href { if has_stability { - write!(rightside, " · <a class=\"srclink\" href=\"{}\">source</a>", l) + write!(rightside, " · <a class=\"src\" href=\"{link}\">source</a>") } else { - write!(rightside, "<a class=\"srclink rightside\" href=\"{}\">source</a>", l) + write!(rightside, "<a class=\"src rightside\" href=\"{link}\">source</a>") } } if has_stability && has_src_ref { @@ -1852,10 +1860,13 @@ pub(crate) fn render_impl_summary( } else { format!(" data-aliases=\"{}\"", aliases.join(",")) }; - write!(w, "<section id=\"{}\" class=\"impl\"{}>", id, aliases); + write!(w, "<section id=\"{id}\" class=\"impl\"{aliases}>"); render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal); - write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id); - write!(w, "<h3 class=\"code-header\">"); + write!( + w, + "<a href=\"#{id}\" class=\"anchor\">§</a>\ + <h3 class=\"code-header\">" + ); if let Some(use_absolute) = use_absolute { write!(w, "{}", inner_impl.print(use_absolute, cx)); @@ -1880,15 +1891,16 @@ pub(crate) fn render_impl_summary( } else { write!(w, "{}", inner_impl.print(false, cx)); } - write!(w, "</h3>"); + w.write_str("</h3>"); let is_trait = inner_impl.trait_.is_some(); if is_trait { if let Some(portability) = portability(&i.impl_item, Some(parent)) { write!( w, - "<span class=\"item-info\"><div class=\"stab portability\">{}</div></span>", - portability + "<span class=\"item-info\">\ + <div class=\"stab portability\">{portability}</div>\ + </span>", ); } } @@ -1941,7 +1953,7 @@ pub(crate) fn small_url_encode(s: String) -> String { // consistent with itself when encoding them. st += "+"; } else { - write!(st, "%{:02X}", b).unwrap(); + write!(st, "%{b:02X}").unwrap(); } // Invariant: if the current byte is not at the start of a multi-byte character, // we need to get down here so that when the next turn of the loop comes around, @@ -1988,7 +2000,9 @@ pub(crate) fn get_filtered_impls_for_reference<'a>( ) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) { let def_id = it.item_id.expect_def_id(); // If the reference primitive is somehow not defined, exit early. - let Some(v) = shared.cache.impls.get(&def_id) else { return (Vec::new(), Vec::new(), Vec::new()) }; + let Some(v) = shared.cache.impls.get(&def_id) else { + return (Vec::new(), Vec::new(), Vec::new()); + }; // Since there is no "direct implementation" on the reference primitive type, we filter out // every implementation which isn't a trait implementation. let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some()); @@ -2254,7 +2268,7 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c format!("lines {}-{}", line_lo + 1, line_hi + 1), ) }; - let url = format!("{}{}#{}", cx.root_path(), call_data.url, anchor); + let url = format!("{}{}#{anchor}", cx.root_path(), call_data.url); (url, title) }; @@ -2264,7 +2278,7 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c Ok(contents) => contents, Err(err) => { let span = item.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner()); - tcx.sess.span_err(span, format!("failed to read file {}: {}", path.display(), err)); + tcx.sess.span_err(span, format!("failed to read file {}: {err}", path.display())); return false; } }; @@ -2323,7 +2337,7 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c .unwrap(); if line_ranges.len() > 1 { - write!(w, r#"<button class="prev">≺</button> <button class="next">≻</button>"#) + w.write_str(r#"<button class="prev">≺</button> <button class="next">≻</button>"#) .unwrap(); } @@ -2359,7 +2373,7 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c highlight::DecorationInfo(decoration_info), sources::SourceContext::Embedded { offset: line_min, needs_expansion }, ); - write!(w, "</div></div>").unwrap(); + w.write_str("</div></div>").unwrap(); true }; @@ -2426,8 +2440,10 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c // For the remaining examples, generate a <ul> containing links to the source files. if it.peek().is_some() { - write!(w, r#"<div class="example-links">Additional examples can be found in:<br><ul>"#) - .unwrap(); + w.write_str( + r#"<div class="example-links">Additional examples can be found in:<br><ul>"#, + ) + .unwrap(); it.for_each(|(_, call_data)| { let (url, _) = link_to_loc(call_data, &call_data.locations[0]); write!( @@ -2438,11 +2454,11 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c ) .unwrap(); }); - write!(w, "</ul></div>").unwrap(); + w.write_str("</ul></div>").unwrap(); } - write!(w, "</div></details>").unwrap(); + w.write_str("</div></details>").unwrap(); } - write!(w, "</div>").unwrap(); + w.write_str("</div>").unwrap(); } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 383e3c170..6cab34986 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -310,9 +310,8 @@ fn toggle_open(mut w: impl fmt::Write, text: impl fmt::Display) { w, "<details class=\"toggle type-contents-toggle\">\ <summary class=\"hideme\">\ - <span>Show {}</span>\ + <span>Show {text}</span>\ </summary>", - text ) .unwrap(); } @@ -412,7 +411,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: ) }); - debug!("{:?}", indices); + debug!("{indices:?}"); let mut last_section = None; for &idx in &indices { @@ -431,9 +430,8 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: w, "<h2 id=\"{id}\" class=\"small-section-header\">\ <a href=\"#{id}\">{name}</a>\ - </h2>{}", - ITEM_TABLE_OPEN, - id = cx.derive_id(my_section.id().to_owned()), + </h2>{ITEM_TABLE_OPEN}", + id = cx.derive_id(my_section.id()), name = my_section.name(), ); } @@ -485,7 +483,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: w.write_str(ITEM_TABLE_ROW_OPEN); let id = match import.kind { clean::ImportKind::Simple(s) => { - format!(" id=\"{}\"", cx.derive_id(format!("reexport.{}", s))) + format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}"))) } clean::ImportKind::Glob => String::new(), }; @@ -583,10 +581,8 @@ fn extra_info_tags<'a, 'tcx: 'a>( display_fn(move |f| { write!( f, - r#"<span class="stab {}" title="{}">{}</span>"#, - class, - Escape(title), - contents + r#"<span class="stab {class}" title="{title}">{contents}</span>"#, + title = Escape(title), ) }) } @@ -614,7 +610,12 @@ fn extra_info_tags<'a, 'tcx: 'a>( (cfg, _) => cfg.as_deref().cloned(), }; - debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg); + debug!( + "Portability name={name:?} {cfg:?} - {parent_cfg:?} = {cfg:?}", + name = item.name, + cfg = item.cfg, + parent_cfg = parent.cfg + ); if let Some(ref cfg) = cfg { write!( f, @@ -689,14 +690,13 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: wrap_item(w, |mut w| { write!( w, - "{attrs}{}{}{}trait {}{}{}", - visibility_print_with_space(it.visibility(tcx), it.item_id, cx), - t.unsafety(tcx).print_with_space(), - if t.is_auto(tcx) { "auto " } else { "" }, - it.name.unwrap(), - t.generics.print(cx), - bounds, + "{attrs}{vis}{unsafety}{is_auto}trait {name}{generics}{bounds}", attrs = render_attributes_in_pre(it, "", tcx), + vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), + unsafety = t.unsafety(tcx).print_with_space(), + is_auto = if t.is_auto(tcx) { "auto " } else { "" }, + name = it.name.unwrap(), + generics = t.generics.print(cx), ); if !t.generics.where_predicates.is_empty() { @@ -742,11 +742,10 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: toggle_open( &mut w, format_args!( - "{} associated constant{} and {} method{}", - count_consts, - pluralize(count_consts), - count_methods, - pluralize(count_methods), + "{count_consts} associated constant{plural_const} and \ + {count_methods} method{plural_method}", + plural_const = pluralize(count_consts), + plural_method = pluralize(count_methods), ), ); } @@ -768,7 +767,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } if !toggle && should_hide_fields(count_methods) { toggle = true; - toggle_open(&mut w, format_args!("{} methods", count_methods)); + toggle_open(&mut w, format_args!("{count_methods} methods")); } if count_consts != 0 && count_methods != 0 { w.write_str("\n"); @@ -837,9 +836,9 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: fn trait_item(w: &mut Buffer, cx: &mut Context<'_>, m: &clean::Item, t: &clean::Item) { let name = m.name.unwrap(); - info!("Documenting {} on {:?}", name, t.name); + info!("Documenting {name} on {ty_name:?}", ty_name = t.name); let item_type = m.type_(); - let id = cx.derive_id(format!("{}.{}", item_type, name)); + let id = cx.derive_id(format!("{item_type}.{name}")); let mut content = Buffer::empty_from(w); write!(&mut content, "{}", document(cx, m, Some(t), HeadingOffset::H5)); let toggled = !content.is_empty(); @@ -847,7 +846,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>"); } - write!(w, "<section id=\"{}\" class=\"method\">", id); + write!(w, "<section id=\"{id}\" class=\"method\">"); render_rightside(w, cx, m, t, RenderMode::Normal); write!(w, "<h4 class=\"code-header\">"); render_assoc_item( @@ -1170,12 +1169,12 @@ fn item_trait_alias( wrap_item(w, |w| { write!( w, - "{attrs}trait {}{}{} = {};", - it.name.unwrap(), - t.generics.print(cx), - print_where_clause(&t.generics, cx, 0, Ending::Newline), - bounds(&t.bounds, true, cx), + "{attrs}trait {name}{generics}{where_b} = {bounds};", attrs = render_attributes_in_pre(it, "", cx.tcx()), + name = it.name.unwrap(), + generics = t.generics.print(cx), + where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline), + bounds = bounds(&t.bounds, true, cx), ) .unwrap(); }); @@ -1198,12 +1197,12 @@ fn item_opaque_ty( wrap_item(w, |w| { write!( w, - "{attrs}type {}{}{where_clause} = impl {bounds};", - it.name.unwrap(), - t.generics.print(cx), + "{attrs}type {name}{generics}{where_clause} = impl {bounds};", + attrs = render_attributes_in_pre(it, "", cx.tcx()), + name = it.name.unwrap(), + generics = t.generics.print(cx), where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), bounds = bounds(&t.bounds, false, cx), - attrs = render_attributes_in_pre(it, "", cx.tcx()), ) .unwrap(); }); @@ -1223,13 +1222,13 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea wrap_item(w, |w| { write!( w, - "{attrs}{}type {}{}{where_clause} = {type_};", - visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), - it.name.unwrap(), - t.generics.print(cx), + "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", + attrs = render_attributes_in_pre(it, "", cx.tcx()), + vis = visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), + name = it.name.unwrap(), + generics = t.generics.print(cx), where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), type_ = t.type_.print(cx), - attrs = render_attributes_in_pre(it, "", cx.tcx()), ); }); } @@ -1354,7 +1353,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w.write_str("{\n"); let toggle = should_hide_fields(count_variants); if toggle { - toggle_open(&mut w, format_args!("{} variants", count_variants)); + toggle_open(&mut w, format_args!("{count_variants} variants")); } for v in e.variants() { w.write_str(" "); @@ -1362,7 +1361,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: match *v.kind { // FIXME(#101337): Show discriminant clean::VariantItem(ref var) => match var.kind { - clean::VariantKind::CLike => write!(w, "{}", name), + clean::VariantKind::CLike => w.write_str(name.as_str()), clean::VariantKind::Tuple(ref s) => { write!(w, "{name}({})", print_tuple_struct_fields(cx, s),); } @@ -1418,7 +1417,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() }; if let clean::VariantKind::Tuple(ref s) = variant_data.kind { - write!(w, "({})", print_tuple_struct_fields(cx, s),); + write!(w, "({})", print_tuple_struct_fields(cx, s)); } w.write_str("</h3></section>"); @@ -1543,10 +1542,12 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle write!( w, - "{vis}const {name}: {typ}", + "{vis}const {name}{generics}: {typ}{where_clause}", vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), name = it.name.unwrap(), + generics = c.generics.print(cx), typ = c.type_.print(cx), + where_clause = print_where_clause(&c.generics, cx, 0, Ending::NoNewline), ); // FIXME: The code below now prints @@ -1615,7 +1616,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean for (index, (field, ty)) in fields.enumerate() { let field_name = field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string()); - let id = cx.derive_id(format!("{}.{}", ItemType::StructField, field_name)); + let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField)); write!( w, "<span id=\"{id}\" class=\"{item_type} small-section-header\">\ @@ -1720,7 +1721,7 @@ pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { pub(super) fn item_path(ty: ItemType, name: &str) -> String { match ty { ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)), - _ => format!("{}.{}.html", ty, name), + _ => format!("{ty}.{name}.html"), } } @@ -1843,7 +1844,7 @@ fn render_union<'a, 'cx: 'a>( fields.iter().filter(|field| matches!(*field.kind, clean::StructFieldItem(..))).count(); let toggle = should_hide_fields(count_fields); if toggle { - toggle_open(&mut f, format_args!("{} fields", count_fields)); + toggle_open(&mut f, format_args!("{count_fields} fields")); } for field in fields { @@ -1906,26 +1907,25 @@ fn render_struct( let has_visible_fields = count_fields > 0; let toggle = should_hide_fields(count_fields); if toggle { - toggle_open(&mut w, format_args!("{} fields", count_fields)); + toggle_open(&mut w, format_args!("{count_fields} fields")); } for field in fields { if let clean::StructFieldItem(ref ty) = *field.kind { write!( w, - "\n{} {}{}: {},", - tab, - visibility_print_with_space(field.visibility(tcx), field.item_id, cx), - field.name.unwrap(), - ty.print(cx), + "\n{tab} {vis}{name}: {ty},", + vis = visibility_print_with_space(field.visibility(tcx), field.item_id, cx), + name = field.name.unwrap(), + ty = ty.print(cx), ); } } if has_visible_fields { if it.has_stripped_entries().unwrap() { - write!(w, "\n{} /* private fields */", tab); + write!(w, "\n{tab} /* private fields */"); } - write!(w, "\n{}", tab); + write!(w, "\n{tab}"); } else if it.has_stripped_entries().unwrap() { write!(w, " /* private fields */ "); } diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 455b4e9ae..f3da61056 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -330,7 +330,7 @@ fn sidebar_deref_methods<'a>( ) { let c = cx.cache(); - debug!("found Deref: {:?}", impl_); + debug!("found Deref: {impl_:?}"); if let Some((target, real_target)) = impl_.inner_impl().items.iter().find_map(|item| match *item.kind { clean::AssocTypeItem(box ref t, _) => Some(match *t { @@ -340,7 +340,7 @@ fn sidebar_deref_methods<'a>( _ => None, }) { - debug!("found target, real_target: {:?} {:?}", target, real_target); + debug!("found target, real_target: {target:?} {real_target:?}"); if let Some(did) = target.def_id(c) && let Some(type_did) = impl_.inner_impl().for_.def_id(c) && // `impl Deref<Target = S> for S` @@ -357,7 +357,7 @@ fn sidebar_deref_methods<'a>( }) .and_then(|did| c.impls.get(&did)); if let Some(impls) = inner_impl { - debug!("found inner_impl: {:?}", impls); + debug!("found inner_impl: {impls:?}"); let mut ret = impls .iter() .filter(|i| i.inner_impl().trait_.is_none()) @@ -510,10 +510,10 @@ fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String { return url; } let mut add = 1; - while !used_links.insert(format!("{}-{}", url, add)) { + while !used_links.insert(format!("{url}-{add}")) { add += 1; } - format!("{}-{}", url, add) + format!("{url}-{add}") } fn get_methods<'a>( @@ -529,7 +529,7 @@ fn get_methods<'a>( Some(ref name) if !name.is_empty() && item.is_method() => { if !for_deref || super::should_render_item(item, deref_mut, tcx) { Some(Link::new( - get_next_url(used_links, format!("{}.{}", ItemType::Method, name)), + get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)), name.as_str(), )) } else { @@ -549,7 +549,7 @@ fn get_associated_constants<'a>( .iter() .filter_map(|item| match item.name { Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new( - get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)), + get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)), name.as_str(), )), _ => None, diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index eb9262f47..5f130f187 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -1,11 +1,11 @@ -use crate::clean::{self, PrimitiveType}; +use crate::clean::{self, rustc_span, PrimitiveType}; use crate::html::sources; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ExprKind, HirId, Mod, Node}; +use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; @@ -25,6 +25,7 @@ pub(crate) enum LinkFromSrc { Local(clean::Span), External(DefId), Primitive(PrimitiveType), + Doc(DefId), } /// This function will do at most two things: @@ -65,24 +66,43 @@ struct SpanMapVisitor<'tcx> { impl<'tcx> SpanMapVisitor<'tcx> { /// This function is where we handle `hir::Path` elements and add them into the "span map". fn handle_path(&mut self, path: &rustc_hir::Path<'_>) { - let info = match path.res { + match path.res { // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`. // Would be nice to support them too alongside the other `DefKind` // (such as primitive types!). - Res::Def(kind, def_id) if kind != DefKind::TyParam => Some(def_id), - Res::Local(_) => None, + Res::Def(kind, def_id) if kind != DefKind::TyParam => { + let link = if def_id.as_local().is_some() { + LinkFromSrc::Local(rustc_span(def_id, self.tcx)) + } else { + LinkFromSrc::External(def_id) + }; + self.matches.insert(path.span, link); + } + Res::Local(_) => { + if let Some(span) = self.tcx.hir().res_span(path.res) { + self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span))); + } + } Res::PrimTy(p) => { // FIXME: Doesn't handle "path-like" primitives like arrays or tuples. self.matches.insert(path.span, LinkFromSrc::Primitive(PrimitiveType::from(p))); - return; } - Res::Err => return, - _ => return, - }; - if let Some(span) = self.tcx.hir().res_span(path.res) { - self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span))); - } else if let Some(def_id) = info { - self.matches.insert(path.span, LinkFromSrc::External(def_id)); + Res::Err => {} + _ => {} + } + } + + /// Used to generate links on items' definition to go to their documentation page. + pub(crate) fn extract_info_from_hir_id(&mut self, hir_id: HirId) { + if let Some(Node::Item(item)) = self.tcx.hir().find(hir_id) { + if let Some(span) = self.tcx.def_ident_span(item.owner_id) { + let cspan = clean::Span::new(span); + // If the span isn't from the current crate, we ignore it. + if cspan.inner().is_dummy() || cspan.cnum(self.tcx.sess) != LOCAL_CRATE { + return; + } + self.matches.insert(span, LinkFromSrc::Doc(item.owner_id.to_def_id())); + } } } @@ -117,10 +137,13 @@ impl<'tcx> SpanMapVisitor<'tcx> { _ => return true, }; let link_from_src = match data.macro_def_id { - Some(macro_def_id) if macro_def_id.is_local() => { - LinkFromSrc::Local(clean::Span::new(data.def_site)) + Some(macro_def_id) => { + if macro_def_id.is_local() { + LinkFromSrc::Local(clean::Span::new(data.def_site)) + } else { + LinkFromSrc::External(macro_def_id) + } } - Some(macro_def_id) => LinkFromSrc::External(macro_def_id), None => return true, }; let new_span = data.call_site; @@ -160,6 +183,9 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)), ); } + } else { + // If it's a "mod foo {}", we want to look to its documentation page. + self.extract_info_from_hir_id(id); } intravisit::walk_mod(self, m, id); } @@ -176,13 +202,12 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { .tcx .typeck_body(hir.maybe_body_owned_by(body_id).expect("a body which isn't a body")); if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) { - self.matches.insert( - segment.ident.span, - match hir.span_if_local(def_id) { - Some(span) => LinkFromSrc::Local(clean::Span::new(span)), - None => LinkFromSrc::External(def_id), - }, - ); + let link = if def_id.as_local().is_some() { + LinkFromSrc::Local(rustc_span(def_id, self.tcx)) + } else { + LinkFromSrc::External(def_id) + }; + self.matches.insert(segment.ident.span, link); } } else if self.handle_macro(expr.span) { // We don't want to go deeper into the macro. @@ -190,4 +215,28 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { } intravisit::walk_expr(self, expr); } + + fn visit_item(&mut self, item: &'tcx Item<'tcx>) { + match item.kind { + ItemKind::Static(_, _, _) + | ItemKind::Const(_, _, _) + | ItemKind::Fn(_, _, _) + | ItemKind::Macro(_, _) + | ItemKind::TyAlias(_, _) + | ItemKind::Enum(_, _) + | ItemKind::Struct(_, _) + | ItemKind::Union(_, _) + | ItemKind::Trait(_, _, _, _, _) + | ItemKind::TraitAlias(_, _) => self.extract_info_from_hir_id(item.hir_id()), + ItemKind::Impl(_) + | ItemKind::Use(_, _) + | ItemKind::ExternCrate(_) + | ItemKind::ForeignMod { .. } + | ItemKind::GlobalAsm(_) + | ItemKind::OpaqueTy(_) + // We already have "visit_mod" above so no need to check it here. + | ItemKind::Mod(_) => {} + } + intravisit::walk_item(self, item); + } } diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs index 0bc32ea5a..377daaeb9 100644 --- a/src/librustdoc/html/render/type_layout.rs +++ b/src/librustdoc/html/render/type_layout.rs @@ -39,7 +39,7 @@ pub(crate) fn document_type_layout<'a, 'cx: 'a>( let tcx = cx.tcx(); let param_env = tcx.param_env(ty_def_id); - let ty = tcx.type_of(ty_def_id).subst_identity(); + let ty = tcx.type_of(ty_def_id).instantiate_identity(); let type_layout = tcx.layout_of(param_env.and(ty)); let variants = diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 54749e9a3..e824651e7 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -73,7 +73,7 @@ pub(super) fn write_shared( } let bytes = try_err!(fs::read(&entry.path), &entry.path); - let filename = format!("{}{}.{}", theme, cx.shared.resource_suffix, extension); + let filename = format!("{theme}{suffix}.{extension}", suffix = cx.shared.resource_suffix); cx.shared.fs.write(cx.dst.join(filename), bytes)?; } @@ -112,7 +112,7 @@ pub(super) fn write_shared( let mut krates = Vec::new(); if path.exists() { - let prefix = format!("\"{}\"", krate); + let prefix = format!("\"{krate}\""); for line in BufReader::new(File::open(path)?).lines() { let line = line?; if !line.starts_with('"') { @@ -157,7 +157,7 @@ pub(super) fn write_shared( let mut krates = Vec::new(); if path.exists() { - let prefix = format!("\"{}\"", krate); + let prefix = format!("\"{krate}\""); for line in BufReader::new(File::open(path)?).lines() { let line = line?; if !line.starts_with('"') { @@ -213,10 +213,10 @@ pub(super) fn write_shared( let dirs = if subs.is_empty() && files.is_empty() { String::new() } else { - format!(",[{}]", subs) + format!(",[{subs}]") }; let files = files.join(","); - let files = if files.is_empty() { String::new() } else { format!(",[{}]", files) }; + let files = if files.is_empty() { String::new() } else { format!(",[{files}]") }; format!( "[\"{name}\"{dirs}{files}]", name = self.elem.to_str().expect("invalid osstring conversion"), @@ -270,7 +270,7 @@ pub(super) fn write_shared( hierarchy.add_path(source); } let hierarchy = Rc::try_unwrap(hierarchy).unwrap(); - let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix)); + let dst = cx.dst.join(&format!("src-files{}.js", cx.shared.resource_suffix)); let make_sources = || { let (mut all_sources, _krates) = try_err!(collect_json(&dst, krate.name(cx.tcx()).as_str()), &dst); @@ -286,12 +286,12 @@ pub(super) fn write_shared( .replace("\\\"", "\\\\\"") )); all_sources.sort(); - let mut v = String::from("var sourcesIndex = JSON.parse('{\\\n"); + let mut v = String::from("var srcIndex = JSON.parse('{\\\n"); v.push_str(&all_sources.join(",\\\n")); - v.push_str("\\\n}');\ncreateSourceSidebar();\n"); + v.push_str("\\\n}');\ncreateSrcSidebar();\n"); Ok(v.into_bytes()) }; - write_invocation_specific("source-files.js", &make_sources)?; + write_invocation_specific("src-files.js", &make_sources)?; } // Update the search index and crate list. @@ -319,8 +319,8 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; })?; write_invocation_specific("crates.js", &|| { - let krates = krates.iter().map(|k| format!("\"{}\"", k)).join(","); - Ok(format!("window.ALL_CRATES = [{}];", krates).into_bytes()) + let krates = krates.iter().map(|k| format!("\"{k}\"")).join(","); + Ok(format!("window.ALL_CRATES = [{krates}];").into_bytes()) })?; if options.enable_index_page { @@ -349,9 +349,8 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; .iter() .map(|s| { format!( - "<li><a href=\"{}index.html\">{}</a></li>", - ensure_trailing_slash(s), - s + "<li><a href=\"{trailing_slash}index.html\">{s}</a></li>", + trailing_slash = ensure_trailing_slash(s), ) }) .collect::<String>() @@ -444,7 +443,7 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; mydst.push(part.to_string()); } cx.shared.ensure_dir(&mydst)?; - mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1])); + mydst.push(&format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1])); let (mut all_implementors, _) = try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst); diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index a26fa3749..c4a1ebbec 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -146,9 +146,8 @@ impl DocVisitor for SourceCollector<'_, '_> { self.cx.shared.tcx.sess.span_err( span, format!( - "failed to render source code for `{}`: {}", - filename.prefer_local(), - e, + "failed to render source code for `{filename}`: {e}", + filename = filename.prefer_local(), ), ); false @@ -227,7 +226,7 @@ impl SourceCollector<'_, '_> { let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped()); let page = layout::Page { title: &title, - css_class: "source", + css_class: "src", root_path: &root_path, static_root_path: shared.static_root_path.as_deref(), description: &desc, diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 54e8b6561..93aa11a58 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -19,7 +19,7 @@ nav.sub { display: none; } -.source .sidebar { +.src .sidebar { display: none; } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index b7f455259..b1de8c152 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -194,7 +194,7 @@ h1, h2, h3, h4, h5, h6, .item-name > a, .out-of-band, span.since, -a.srclink, +a.src, #help-button > a, summary.hideme, .scraped-example-list, @@ -206,7 +206,7 @@ ul.all-items { #toggle-all-docs, a.anchor, .small-section-header a, -#source-sidebar a, +#src-sidebar a, .rust a, .sidebar h2 a, .sidebar h3 a, @@ -315,7 +315,7 @@ main { min-width: 0; /* avoid growing beyond the size limit */ } -.source main { +.src main { padding: 15px; } @@ -350,10 +350,10 @@ pre.item-decl { contain: initial; } -.source .content pre { +.src .content pre { padding: 20px; } -.rustdoc.source .example-wrap pre.src-line-numbers { +.rustdoc.src .example-wrap pre.src-line-numbers { padding: 20px 0 20px 4px; } @@ -392,7 +392,7 @@ img { left: 0; } -.rustdoc.source .sidebar { +.rustdoc.src .sidebar { flex-basis: 50px; border-right: 1px solid; overflow-x: hidden; @@ -402,7 +402,7 @@ img { } .sidebar, .mobile-topbar, .sidebar-menu-toggle, -#src-sidebar-toggle, #source-sidebar { +#src-sidebar-toggle, #src-sidebar { background-color: var(--sidebar-background-color); } @@ -410,16 +410,16 @@ img { background-color: var(--sidebar-background-color-hover); } -.source .sidebar > *:not(#src-sidebar-toggle) { +.src .sidebar > *:not(#src-sidebar-toggle) { visibility: hidden; } -.source-sidebar-expanded .source .sidebar { +.src-sidebar-expanded .src .sidebar { overflow-y: auto; flex-basis: 300px; } -.source-sidebar-expanded .source .sidebar > *:not(#src-sidebar-toggle) { +.src-sidebar-expanded .src .sidebar > *:not(#src-sidebar-toggle) { visibility: visible; } @@ -544,7 +544,7 @@ ul.block, .block li { flex-grow: 1; } -.rustdoc:not(.source) .example-wrap pre { +.rustdoc:not(.src) .example-wrap pre { overflow: auto hidden; } @@ -619,7 +619,7 @@ ul.block, .block li { } .docblock code, .docblock-short code, -pre, .rustdoc.source .example-wrap { +pre, .rustdoc.src .example-wrap { background-color: var(--code-block-background-color); } @@ -676,7 +676,7 @@ nav.sub { height: 34px; flex-grow: 1; } -.source nav.sub { +.src nav.sub { margin: 0 0 15px 0; } @@ -776,7 +776,6 @@ table, } #crate-search { min-width: 115px; - /* keep these two in sync with "@-moz-document url-prefix()" below */ padding: 0 23px 0 4px; /* prevents the <select> from overflowing the containing div in case it's shrunk */ max-width: 100%; @@ -798,14 +797,6 @@ table, #crate-search:hover, #crate-search:focus { border-color: var(--crate-search-hover-border); } -/* cancel stylistic differences in padding in firefox -for "appearance: none"-style (or equivalent) <select>s */ -@-moz-document url-prefix() { - #crate-search { - padding-left: 0px; /* == 4px - 4px */ - padding-right: 19px; /* == 23px - 4px */ - } -} /* pseudo-element for holding the dropdown-arrow image; needs to be a separate thing so that we can apply CSS-filters to change the arrow color in themes */ #crate-search-div::after { @@ -888,7 +879,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ justify-content: start; flex: 3; } -.search-results .result-name span.alias { +.search-results .result-name .alias { color: var(--search-results-alias-color); } .search-results .result-name .grey { @@ -904,6 +895,9 @@ so that we can apply CSS-filters to change the arrow color in themes */ max-width: calc(100% - var(--search-typename-width)); display: inline-block; } +.search-results .result-name .path > * { + display: inline; +} .popover { position: absolute; @@ -1074,7 +1068,7 @@ pre.rust .doccomment { color: var(--code-highlight-doc-comment-color); } -.rustdoc.source .example-wrap pre.rust a { +.rustdoc.src .example-wrap pre.rust a { background: var(--codeblock-link-background); } @@ -1301,22 +1295,22 @@ a.tooltip:hover::after { align-items: stretch; z-index: 10; } -#source-sidebar { +#src-sidebar { width: 100%; overflow: auto; } -#source-sidebar > .title { +#src-sidebar > .title { font-size: 1.5rem; text-align: center; border-bottom: 1px solid var(--border-color); margin-bottom: 6px; } -#source-sidebar div.files > a:hover, details.dir-entry summary:hover, -#source-sidebar div.files > a:focus, details.dir-entry summary:focus { - background-color: var(--source-sidebar-background-hover); +#src-sidebar div.files > a:hover, details.dir-entry summary:hover, +#src-sidebar div.files > a:focus, details.dir-entry summary:focus { + background-color: var(--src-sidebar-background-hover); } -#source-sidebar div.files > a.selected { - background-color: var(--source-sidebar-background-selected); +#src-sidebar div.files > a.selected { + background-color: var(--src-sidebar-background-selected); } #src-sidebar-toggle > button { font-size: inherit; @@ -1562,7 +1556,7 @@ However, it's not needed with smaller screen width because the doc/code block is /* WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY If you update this line, then you also need to update the line with the same warning -in source-script.js +in src-script.js */ @media (max-width: 700px) { /* When linking to an item with an `id` (for instance, by clicking a link in the sidebar, @@ -1619,8 +1613,8 @@ in source-script.js /* The source view uses a different design for the sidebar toggle, and doesn't have a topbar, so don't bump down the main content or the sidebar. */ - .source main, - .rustdoc.source .sidebar { + .src main, + .rustdoc.src .sidebar { top: 0; padding: 0; height: 100vh; @@ -1628,8 +1622,8 @@ in source-script.js } .sidebar.shown, - .source-sidebar-expanded .source .sidebar, - .rustdoc:not(.source) .sidebar:focus-within { + .src-sidebar-expanded .src .sidebar, + .rustdoc:not(.src) .sidebar:focus-within { left: 0; } @@ -1709,7 +1703,7 @@ in source-script.js border-left: 0; } - .source-sidebar-expanded #src-sidebar-toggle { + .src-sidebar-expanded #src-sidebar-toggle { left: unset; top: unset; width: unset; @@ -1749,7 +1743,7 @@ in source-script.js display: inline; } - .source-sidebar-expanded .source .sidebar { + .src-sidebar-expanded .src .sidebar { max-width: 100vw; width: 100vw; } @@ -1769,7 +1763,7 @@ in source-script.js margin-left: 34px; } - .source nav.sub { + .src nav.sub { margin: 0; padding: var(--nav-sub-mobile-padding); } @@ -1792,7 +1786,7 @@ in source-script.js } @media print { - nav.sidebar, nav.sub, .out-of-band, a.srclink, #copy-path, + nav.sidebar, nav.sub, .out-of-band, a.src, #copy-path, details.toggle[open] > summary::before, details.toggle > summary::before, details.toggle.top-doc > summary { display: none; diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 7145baad2..d8dae51eb 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -89,8 +89,8 @@ Original by Dempfi (https://github.com/dempfi/ayu) --crate-search-div-hover-filter: invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%); --crate-search-hover-border: #e0e0e0; - --source-sidebar-background-selected: #14191f; - --source-sidebar-background-hover: #14191f; + --src-sidebar-background-selected: #14191f; + --src-sidebar-background-hover: #14191f; --table-alt-row-background-color: #191f26; --codeblock-link-background: #333; --scrape-example-toggle-line-background: #999; @@ -107,7 +107,7 @@ Original by Dempfi (https://github.com/dempfi/ayu) h1, h2, h3, h4, h1 a, .sidebar h2 a, .sidebar h3 a, -#source-sidebar > .title { +#src-sidebar > .title { color: #fff; } h4 { @@ -124,15 +124,15 @@ h4 { .docblock pre > code, pre, pre > code, .item-info code, -.rustdoc.source .example-wrap { +.rustdoc.src .example-wrap { color: #e6e1cf; } .sidebar .current, .sidebar a:hover, -#source-sidebar div.files > a:hover, details.dir-entry summary:hover, -#source-sidebar div.files > a:focus, details.dir-entry summary:focus, -#source-sidebar div.files > a.selected { +#src-sidebar div.files > a:hover, details.dir-entry summary:hover, +#src-sidebar div.files > a:focus, details.dir-entry summary:focus, +#src-sidebar div.files > a.selected { color: #ffb44c; } diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 3c1186a56..2b3029887 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -68,7 +68,7 @@ --test-arrow-color: #dedede; --test-arrow-background-color: rgba(78, 139, 202, 0.2); --test-arrow-hover-color: #dedede; - --test-arrow-hover-background-color: #4e8bca; + --test-arrow-hover-background-color: rgb(78, 139, 202); --target-background-color: #494a3d; --target-border-color: #bb7410; --kbd-color: #000; @@ -84,8 +84,8 @@ --crate-search-div-hover-filter: invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%); --crate-search-hover-border: #2196f3; - --source-sidebar-background-selected: #333; - --source-sidebar-background-hover: #444; + --src-sidebar-background-selected: #333; + --src-sidebar-background-hover: #444; --table-alt-row-background-color: #2A2A2A; --codeblock-link-background: #333; --scrape-example-toggle-line-background: #999; diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index f8c287137..56fd8cbef 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -68,7 +68,7 @@ --test-arrow-color: #f5f5f5; --test-arrow-background-color: rgba(78, 139, 202, 0.2); --test-arrow-hover-color: #f5f5f5; - --test-arrow-hover-background-color: #4e8bca; + --test-arrow-hover-background-color: rgb(78, 139, 202); --target-background-color: #fdffd3; --target-border-color: #ad7c37; --kbd-color: #000; @@ -81,8 +81,8 @@ --crate-search-div-hover-filter: invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%); --crate-search-hover-border: #717171; - --source-sidebar-background-selected: #fff; - --source-sidebar-background-hover: #e0e0e0; + --src-sidebar-background-selected: #fff; + --src-sidebar-background-hover: #e0e0e0; --table-alt-row-background-color: #F5F5F5; --codeblock-link-background: #eee; --scrape-example-toggle-line-background: #ccc; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 51d8e81ca..42088e735 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2108,29 +2108,22 @@ function initSearch(rawSearchIndex) { const resultName = document.createElement("div"); resultName.className = "result-name"; - if (item.is_alias) { - const alias = document.createElement("span"); - alias.className = "alias"; - - const bold = document.createElement("b"); - bold.innerText = item.alias; - alias.appendChild(bold); - - alias.insertAdjacentHTML( - "beforeend", - "<i class=\"grey\"> - see </i>"); + resultName.insertAdjacentHTML( + "beforeend", + `<span class="typename">${typeName}</span>`); + link.appendChild(resultName); - resultName.appendChild(alias); + let alias = " "; + if (item.is_alias) { + alias = ` <div class="alias">\ +<b>${item.alias}</b><i class="grey"> - see </i>\ +</div>`; } - resultName.insertAdjacentHTML( "beforeend", - `\ -<span class="typename">${typeName}</span>\ -<div class="path">\ - ${item.displayPath}<span class="${type}">${name}</span>\ + `<div class="path">${alias}\ +${item.displayPath}<span class="${type}">${name}</span>\ </div>`); - link.appendChild(resultName); const description = document.createElement("div"); description.className = "desc"; diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/src-script.js index 6eb991360..679c2341f 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/src-script.js @@ -1,5 +1,5 @@ // From rust: -/* global sourcesIndex */ +/* global srcIndex */ // Local js definitions: /* global addClass, getCurrentValue, onEachLazy, removeClass, browserSupportsHistoryApi */ @@ -74,11 +74,11 @@ function createDirEntry(elem, parent, fullPath, hasFoundFile) { function toggleSidebar() { const child = this.parentNode.children[0]; if (child.innerText === ">") { - addClass(document.documentElement, "source-sidebar-expanded"); + addClass(document.documentElement, "src-sidebar-expanded"); child.innerText = "<"; updateLocalStorage("source-sidebar-show", "true"); } else { - removeClass(document.documentElement, "source-sidebar-expanded"); + removeClass(document.documentElement, "src-sidebar-expanded"); child.innerText = ">"; updateLocalStorage("source-sidebar-show", "false"); } @@ -101,16 +101,16 @@ function createSidebarToggle() { return sidebarToggle; } -// This function is called from "source-files.js", generated in `html/render/write_shared.rs`. +// This function is called from "src-files.js", generated in `html/render/write_shared.rs`. // eslint-disable-next-line no-unused-vars -function createSourceSidebar() { +function createSrcSidebar() { const container = document.querySelector("nav.sidebar"); const sidebarToggle = createSidebarToggle(); container.insertBefore(sidebarToggle, container.firstChild); const sidebar = document.createElement("div"); - sidebar.id = "source-sidebar"; + sidebar.id = "src-sidebar"; let hasFoundFile = false; @@ -118,9 +118,9 @@ function createSourceSidebar() { title.className = "title"; title.innerText = "Files"; sidebar.appendChild(title); - Object.keys(sourcesIndex).forEach(key => { - sourcesIndex[key][NAME_OFFSET] = key; - hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "", hasFoundFile); + Object.keys(srcIndex).forEach(key => { + srcIndex[key][NAME_OFFSET] = key; + hasFoundFile = createDirEntry(srcIndex[key], sidebar, "", hasFoundFile); }); container.appendChild(sidebar); @@ -133,7 +133,7 @@ function createSourceSidebar() { const lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/; -function highlightSourceLines(match) { +function highlightSrcLines(match) { if (typeof match === "undefined") { match = window.location.hash.match(lineNumbersRegex); } @@ -172,7 +172,7 @@ function highlightSourceLines(match) { } } -const handleSourceHighlight = (function() { +const handleSrcHighlight = (function() { let prev_line_id = 0; const set_fragment = name => { @@ -180,7 +180,7 @@ const handleSourceHighlight = (function() { y = window.scrollY; if (browserSupportsHistoryApi()) { history.replaceState(null, null, "#" + name); - highlightSourceLines(); + highlightSrcLines(); } else { location.replace("#" + name); } @@ -221,15 +221,15 @@ const handleSourceHighlight = (function() { window.addEventListener("hashchange", () => { const match = window.location.hash.match(lineNumbersRegex); if (match) { - return highlightSourceLines(match); + return highlightSrcLines(match); } }); onEachLazy(document.getElementsByClassName("src-line-numbers"), el => { - el.addEventListener("click", handleSourceHighlight); + el.addEventListener("click", handleSrcHighlight); }); -highlightSourceLines(); +highlightSrcLines(); -window.createSourceSidebar = createSourceSidebar; +window.createSrcSidebar = createSrcSidebar; })(); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 71961f6f2..af3ca42a6 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -185,7 +185,7 @@ updateTheme(); if (getSettingValue("source-sidebar-show") === "true") { // At this point in page load, `document.body` is not available yet. // Set a class on the `<html>` element instead. - addClass(document.documentElement, "source-sidebar-expanded"); + addClass(document.documentElement, "src-sidebar-expanded"); } // If we navigate away (for example to a settings page), and then use the back or diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 767b974cc..a27aa2b58 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -53,7 +53,7 @@ pub(crate) fn suffix_path(filename: &str, suffix: &str) -> PathBuf { // which would result in `style.min-suffix.css` which isn't what we // want. let (base, ext) = filename.split_once('.').unwrap(); - let filename = format!("{}{}.{}", base, suffix, ext); + let filename = format!("{base}{suffix}.{ext}"); filename.into() } @@ -97,7 +97,7 @@ static_files! { main_js => "static/js/main.js", search_js => "static/js/search.js", settings_js => "static/js/settings.js", - source_script_js => "static/js/source-script.js", + src_script_js => "static/js/src-script.js", storage_js => "static/js/storage.js", scrape_examples_js => "static/js/scrape-examples.js", wheel_svg => "static/images/wheel.svg", diff --git a/src/librustdoc/html/templates/STYLE.md b/src/librustdoc/html/templates/STYLE.md index 0281b1c47..38aac2a60 100644 --- a/src/librustdoc/html/templates/STYLE.md +++ b/src/librustdoc/html/templates/STYLE.md @@ -32,7 +32,7 @@ Askama templates support quite sophisticated control flow. To keep our templates simple and understandable, we use only a subset: `if` and `for`. In particular we avoid [assignments in the template logic][assignments] and [Askama macros][macros]. This also may make things easier if we switch to a different -Jinja-style template system, like Askama, in the future. +Jinja-style template system in the future. [assignments]: https://djc.github.io/askama/template_syntax.html#assignments [macros]: https://djc.github.io/askama/template_syntax.html#macros diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index d4ec9c34b..60ccfe4da 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -42,9 +42,9 @@ <script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {# #} {% if page.css_class.contains("crate") %} <script defer src="{{page.root_path|safe}}crates{{page.resource_suffix}}.js"></script> {# #} - {% else if page.css_class == "source" %} - <script defer src="{{static_root_path|safe}}{{files.source_script_js}}"></script> {# #} - <script defer src="{{page.root_path|safe}}source-files{{page.resource_suffix}}.js"></script> {# #} + {% else if page.css_class == "src" %} + <script defer src="{{static_root_path|safe}}{{files.src_script_js}}"></script> {# #} + <script defer src="{{page.root_path|safe}}src-files{{page.resource_suffix}}.js"></script> {# #} {% else if !page.css_class.contains("mod") %} <script defer src="sidebar-items{{page.resource_suffix}}.js"></script> {# #} {% endif %} @@ -85,7 +85,7 @@ </div> {# #} <![endif]--> {# #} {{ layout.external_html.before_content|safe }} - {% if page.css_class != "source" %} + {% if page.css_class != "src" %} <nav class="mobile-topbar"> {# #} <button class="sidebar-menu-toggle">☰</button> {# #} <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #} @@ -99,7 +99,7 @@ </nav> {# #} {% endif %} <nav class="sidebar"> {# #} - {% if page.css_class != "source" %} + {% if page.css_class != "src" %} <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #} {% if !layout.logo.is_empty() %} <img src="{{layout.logo}}" alt="logo"> {# #} @@ -111,9 +111,9 @@ {{ sidebar|safe }} </nav> {# #} <main> {# #} - {% if page.css_class != "source" %}<div class="width-limiter">{% endif %} + {% if page.css_class != "src" %}<div class="width-limiter">{% endif %} <nav class="sub"> {# #} - {% if page.css_class == "source" %} + {% if page.css_class == "src" %} <a class="sub-logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #} {% if !layout.logo.is_empty() %} <img src="{{layout.logo}}" alt="logo"> {# #} @@ -144,7 +144,7 @@ </form> {# #} </nav> {# #} <section id="main-content" class="content">{{ content|safe }}</section> {# #} - {% if page.css_class != "source" %}</div>{% endif %} + {% if page.css_class != "src" %}</div>{% endif %} </main> {# #} {{ layout.external_html.after_content|safe }} </body> {# #} diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 68a295ae0..1d215c269 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -18,7 +18,7 @@ {% endif %} {% match src_href %} {% when Some with (href) %} - <a class="srclink" href="{{href|safe}}">source</a> · {#+ #} + <a class="src" href="{{href|safe}}">source</a> · {#+ #} {% else %} {% endmatch %} <button id="toggle-all-docs" title="collapse all docs"> {# #} diff --git a/src/librustdoc/html/templates/type_layout.html b/src/librustdoc/html/templates/type_layout.html index 20e09a548..287cbab07 100644 --- a/src/librustdoc/html/templates/type_layout.html +++ b/src/librustdoc/html/templates/type_layout.html @@ -44,6 +44,11 @@ <strong>Note:</strong> Encountered an error during type layout; {#+ #} the type was too big. {# #} </p> {# #} + {% when Err(LayoutError::ReferencesError(_)) %} + <p> {# #} + <strong>Note:</strong> Encountered an error during type layout; {#+ #} + the type references errors. {# #} + </p> {# #} {% when Err(LayoutError::NormalizationFailure(_, _)) %} <p> {# #} <strong>Note:</strong> Encountered an error during type layout; {#+ #} diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 91cd55b11..8673138f6 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -171,6 +171,7 @@ impl FromWithTcx<clean::GenericArg> for GenericArg { } impl FromWithTcx<clean::Constant> for Constant { + // FIXME(generic_const_items): Add support for generic const items. fn from_tcx(constant: clean::Constant, tcx: TyCtxt<'_>) -> Self { let expr = constant.expr(tcx); let value = constant.value(tcx); @@ -321,8 +322,12 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { impls: Vec::new(), // Added in JsonRenderer::item }) } - TyAssocConstItem(ty) => ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None }, - AssocConstItem(ty, default) => { + // FIXME(generic_const_items): Add support for generic associated consts. + TyAssocConstItem(_generics, ty) => { + ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None } + } + // FIXME(generic_const_items): Add support for generic associated consts. + AssocConstItem(_generics, ty, default) => { ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) } } TyAssocTypeItem(g, b) => ItemEnum::AssocType { diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 9392dd4d0..cd791ce00 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -139,7 +139,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { fn item(&mut self, item: clean::Item) -> Result<(), Error> { let item_type = item.type_(); let item_name = item.name; - trace!("rendering {} {:?}", item_type, item_name); + trace!("rendering {item_type} {item_name:?}"); // Flatten items that recursively store other items. We include orphaned items from // stripped modules and etc that are otherwise reachable. @@ -203,11 +203,11 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { if !can_be_ignored { assert_eq!(old_item, new_item); } - trace!("replaced {:?}\nwith {:?}", old_item, new_item); + trace!("replaced {old_item:?}\nwith {new_item:?}"); } } - trace!("done rendering {} {:?}", item_type, item_name); + trace!("done rendering {item_type} {item_name:?}"); Ok(()) } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index f28deae79..8220df5d4 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -192,8 +192,7 @@ fn init_logging(handler: &EarlyErrorHandler) { Ok("never") => false, Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(), Ok(value) => handler.early_error(format!( - "invalid log color value '{}': expected one of always, never, or auto", - value + "invalid log color value '{value}': expected one of always, never, or auto", )), Err(VarError::NotUnicode(value)) => handler.early_error(format!( "invalid log color value '{}': expected one of always, never, or auto", @@ -224,7 +223,7 @@ fn get_args(handler: &EarlyErrorHandler) -> Option<Vec<String>> { .map(|(i, arg)| { arg.into_string() .map_err(|arg| { - handler.early_warn(format!("Argument {} is not valid Unicode: {:?}", i, arg)); + handler.early_warn(format!("Argument {i} is not valid Unicode: {arg:?}")); }) .ok() }) @@ -665,11 +664,10 @@ fn usage(argv0: &str) { for option in opts() { (option.apply)(&mut options); } - println!("{}", options.usage(&format!("{} [options] <input>", argv0))); + println!("{}", options.usage(&format!("{argv0} [options] <input>"))); println!(" @path Read newline separated options from `path`\n"); println!( - "More information available at {}/rustdoc/what-is-rustdoc.html", - DOC_RUST_LANG_ORG_CHANNEL + "More information available at {DOC_RUST_LANG_ORG_CHANNEL}/rustdoc/what-is-rustdoc.html", ); } @@ -699,7 +697,7 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>( tcx.sess.struct_err(format!("couldn't generate documentation: {}", e.error)); let file = e.file.display().to_string(); if !file.is_empty() { - msg.note(format!("failed to create or modify \"{}\"", file)); + msg.note(format!("failed to create or modify \"{file}\"")); } Err(msg.emit()) } @@ -744,9 +742,6 @@ fn main_args(handler: &mut EarlyErrorHandler, at_args: &[String]) -> MainResult } }; - // Set parallel mode before error handler creation, which will create `Lock`s. - interface::set_thread_safe_mode(&options.unstable_opts); - let diag = core::new_handler( options.error_format, None, diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 749c1ff51..d45040e34 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -185,6 +185,17 @@ declare_rustdoc_lint! { "detects unescaped backticks in doc comments" } +declare_rustdoc_lint! { + /// This lint is **warned by default**. It detects explicit links that are same + /// as computed automatic links. This usually means the explicit links is removeable. + /// This is a `rustdoc` only lint, see the documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#redundant_explicit_links + REDUNDANT_EXPLICIT_LINKS, + Warn, + "detects redundant explicit links in doc comments" +} + pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, @@ -197,6 +208,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| { BARE_URLS, MISSING_CRATE_LEVEL_DOCS, UNESCAPED_BACKTICKS, + REDUNDANT_EXPLICIT_LINKS, ] }); diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 4321d4aa3..526eea304 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -43,7 +43,7 @@ pub(crate) fn render<P: AsRef<Path>>( edition: Edition, ) -> Result<(), String> { if let Err(e) = create_dir_all(&options.output) { - return Err(format!("{}: {}", options.output.display(), e)); + return Err(format!("{output}: {e}", output = options.output.display())); } let input = input.as_ref(); @@ -57,11 +57,13 @@ pub(crate) fn render<P: AsRef<Path>>( .expect("Writing to a String can't fail"); } - let input_str = read_to_string(input).map_err(|err| format!("{}: {}", input.display(), err))?; + let input_str = + read_to_string(input).map_err(|err| format!("{input}: {err}", input = input.display()))?; let playground_url = options.markdown_playground_url.or(options.playground_url); let playground = playground_url.map(|url| markdown::Playground { crate_name: None, url }); - let mut out = File::create(&output).map_err(|e| format!("{}: {}", output.display(), e))?; + let mut out = + File::create(&output).map_err(|e| format!("{output}: {e}", output = output.display()))?; let (metadata, text) = extract_leading_metadata(&input_str); if metadata.is_empty() { @@ -129,7 +131,7 @@ pub(crate) fn render<P: AsRef<Path>>( ); match err { - Err(e) => Err(format!("cannot write to `{}`: {}", output.display(), e)), + Err(e) => Err(format!("cannot write to `{output}`: {e}", output = output.display())), Ok(_) => Ok(()), } } @@ -137,7 +139,7 @@ pub(crate) fn render<P: AsRef<Path>>( /// Runs any tests/code examples in the markdown file `input`. pub(crate) fn test(options: Options) -> Result<(), String> { let input_str = read_to_string(&options.input) - .map_err(|err| format!("{}: {}", options.input.display(), err))?; + .map_err(|err| format!("{input}: {err}", input = options.input.display()))?; let mut opts = GlobalTestOptions::default(); opts.no_crate_inject = true; let mut collector = Collector::new( diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 6ead0cd96..592dd0a14 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -146,8 +146,10 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> { examples_percentage: f64, ) { println!( - "| {:<35} | {:>10} | {:>9.1}% | {:>10} | {:>9.1}% |", - name, count.with_docs, percentage, count.with_examples, examples_percentage, + "| {name:<35} | {with_docs:>10} | {percentage:>9.1}% | {with_examples:>10} | \ + {examples_percentage:>9.1}% |", + with_docs = count.with_docs, + with_examples = count.with_examples, ); } @@ -249,7 +251,7 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> { if let Some(span) = i.span(self.ctx.tcx) { let filename = span.filename(self.ctx.sess()); - debug!("counting {:?} {:?} in {:?}", i.type_(), i.name, filename); + debug!("counting {:?} {:?} in {filename:?}", i.type_(), i.name); self.items.entry(filename).or_default().count_item( has_docs, has_doc_example, diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index b6cd897d3..e732a4057 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -7,11 +7,11 @@ use super::Pass; use crate::clean; +use crate::clean::utils::inherits_doc_hidden; use crate::clean::*; use crate::core::DocContext; use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString}; use crate::visit::DocVisitor; -use crate::visit_ast::inherits_doc_hidden; use rustc_hir as hir; use rustc_middle::lint::LintLevelSource; use rustc_session::lint; @@ -92,8 +92,8 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - return false; } - if cx.tcx.is_doc_hidden(def_id.to_def_id()) - || inherits_doc_hidden(cx.tcx, def_id, None) + if (!cx.render_options.document_hidden + && (cx.tcx.is_doc_hidden(def_id.to_def_id()) || inherits_doc_hidden(cx.tcx, def_id, None))) || cx.tcx.def_span(def_id.to_def_id()).in_derive_expansion() { return false; @@ -106,8 +106,7 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - } pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { - let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id) - else { + let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id) else { // If non-local, no need to check anything. return; }; @@ -118,7 +117,7 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples { if should_have_doc_example(cx, item) { - debug!("reporting error for {:?} (hir_id={:?})", item, hir_id); + debug!("reporting error for {item:?} (hir_id={hir_id:?})"); let sp = item.attr_span(cx.tcx); cx.tcx.struct_span_lint_hir( crate::lint::MISSING_DOC_CODE_EXAMPLES, diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 0dd9e590b..7b0a7a90d 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -14,7 +14,7 @@ use rustc_hir::def::{DefKind, Namespace, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::Mutability; use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_middle::{bug, ty}; +use rustc_middle::{bug, span_bug, ty}; use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution}; use rustc_resolve::rustdoc::{strip_generics_from_path, MalformedGenerics}; use rustc_session::lint::Lint; @@ -150,7 +150,7 @@ impl TryFrom<ResolveRes> for Res { PrimTy(prim) => Ok(Res::Primitive(PrimitiveType::from_hir(prim))), // e.g. `#[derive]` ToolMod | NonMacroAttr(..) | Err => Result::Err(()), - other => bug!("unrecognized res {:?}", other), + other => bug!("unrecognized res {other:?}"), } } } @@ -224,7 +224,7 @@ impl UrlFragment { "structfield." } } - kind => bug!("unexpected associated item kind: {:?}", kind), + kind => bug!("unexpected associated item kind: {kind:?}"), }; s.push_str(kind); s.push_str(tcx.item_name(def_id).as_str()); @@ -279,7 +279,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { unresolved: path_str.into(), }; - debug!("looking for enum variant {}", path_str); + debug!("looking for enum variant {path_str}"); let mut split = path_str.rsplitn(3, "::"); let variant_field_name = split .next() @@ -298,7 +298,7 @@ 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).subst_identity().kind() { + Res::Def(DefKind::Enum, did) => match tcx.type_of(did).instantiate_identity().kind() { ty::Adt(def, _) if def.is_enum() => { 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) { @@ -402,10 +402,15 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // `doc_link_resolutions` is missing a `path_str`, that means that there are valid links // that are being missed. To fix the ICE, change // `rustc_resolve::rustdoc::attrs_to_preprocessed_links` to cache the link. - .unwrap_or_else(|| panic!("no resolution for {:?} {:?} {:?}", path_str, ns, module_id)) + .unwrap_or_else(|| { + span_bug!( + self.cx.tcx.def_span(item_id), + "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); + debug!("{path_str} resolved to {result:?} in namespace {ns:?}"); result } @@ -448,7 +453,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // If there's no `::`, it's not an associated item. // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. .ok_or_else(|| { - debug!("found no `::`, assuming {} was correctly not in scope", item_name); + debug!("found no `::`, assuming {item_name} was correctly not in scope"); UnresolvedPath { item_id, module_id, @@ -493,7 +498,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).subst_identity().kind() { + Some(match *self.cx.tcx.type_of(ty_id).instantiate_identity().kind() { ty::Bool => Res::Primitive(Bool), ty::Char => Res::Primitive(Char), ty::Int(ity) => Res::Primitive(ity.into()), @@ -587,7 +592,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .unwrap_or(Vec::new()) } } - Res::Def(DefKind::TyAlias, did) => { + Res::Def(DefKind::TyAlias { .. }, did) => { // Resolve the link on the type the alias points to. // FIXME: if the associated item is defined directly on the type alias, // it will show up on its documentation page, we should link there instead. @@ -598,10 +603,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { def_kind @ (DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::ForeignTy), did, ) => { - debug!("looking for associated item named {} for item {:?}", item_name, did); + debug!("looking for associated item named {item_name} for item {did:?}"); // Checks if item_name is a variant of the `SomeItem` enum if ns == TypeNS && def_kind == DefKind::Enum { - match tcx.type_of(did).subst_identity().kind() { + match tcx.type_of(did).instantiate_identity().kind() { ty::Adt(adt_def, _) => { for variant in adt_def.variants() { if variant.name == item_name { @@ -635,7 +640,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // To handle that properly resolve() would have to support // something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn) assoc_items = resolve_associated_trait_item( - tcx.type_of(did).subst_identity(), + tcx.type_of(did).instantiate_identity(), module_id, item_name, ns, @@ -646,7 +651,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .collect::<Vec<_>>(); } - debug!("got associated item {:?}", assoc_items); + debug!("got associated item {assoc_items:?}"); if !assoc_items.is_empty() { return assoc_items; @@ -655,7 +660,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { if ns != Namespace::ValueNS { return Vec::new(); } - debug!("looking for fields named {} for {:?}", item_name, did); + debug!("looking for fields named {item_name} for {did:?}"); // FIXME: this doesn't really belong in `associated_item` (maybe `variant_field` is better?) // NOTE: it's different from variant_field because it only resolves struct fields, // not variant fields (2 path segments, not 3). @@ -671,7 +676,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).subst_identity().kind() { + let def = match tcx.type_of(did).instantiate_identity().kind() { ty::Adt(def, _) if !def.is_enum() => def, _ => return Vec::new(), }; @@ -722,7 +727,7 @@ fn resolve_associated_trait_item<'a>( // Give precedence to inherent impls. let traits = trait_impls_for(cx, ty, module); let tcx = cx.tcx; - debug!("considering traits {:?}", traits); + debug!("considering traits {traits:?}"); let candidates = traits .iter() .flat_map(|&(impl_, trait_)| { @@ -739,7 +744,7 @@ fn resolve_associated_trait_item<'a>( }) .collect::<Vec<_>>(); // FIXME(#74563): warn about ambiguity - debug!("the candidates were {:?}", candidates); + debug!("the candidates were {candidates:?}"); candidates } @@ -785,10 +790,8 @@ fn trait_impls_for<'a>( // Check if these are the same type. let impl_type = trait_ref.skip_binder().self_ty(); trace!( - "comparing type {} with kind {:?} against type {:?}", - impl_type, - impl_type.kind(), - ty + "comparing type {impl_type} with kind {kind:?} against type {ty:?}", + kind = impl_type.kind(), ); // Fast path: if this is a primitive simple `==` will work // NOTE: the `match` is necessary; see #92662. @@ -935,7 +938,7 @@ fn preprocess_link( let path_str = match strip_generics_from_path(path_str) { Ok(path) => path, Err(err) => { - debug!("link has malformed generics: {}", path_str); + debug!("link has malformed generics: {path_str}"); return Some(Err(PreprocessingError::MalformedGenerics(err, path_str.to_owned()))); } }; @@ -963,6 +966,7 @@ fn preprocessed_markdown_links(s: &str) -> Vec<PreprocessedMarkdownLink> { } impl LinkCollector<'_, '_> { + #[instrument(level = "debug", skip_all)] fn resolve_links(&mut self, item: &Item) { if !self.cx.render_options.document_private && let Some(def_id) = item.item_id.as_def_id() @@ -981,7 +985,7 @@ impl LinkCollector<'_, '_> { if !may_have_doc_links(&doc) { continue; } - debug!("combined_docs={}", doc); + debug!("combined_docs={doc}"); // 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 item_id = item_id.unwrap_or_else(|| item.item_id.expect_def_id()); @@ -990,7 +994,7 @@ impl LinkCollector<'_, '_> { _ => find_nearest_parent_module(self.cx.tcx, item_id).unwrap(), }; for md_link in preprocessed_markdown_links(&doc) { - let link = self.resolve_link(item, item_id, module_id, &doc, &md_link); + let link = self.resolve_link(&doc, item, item_id, module_id, &md_link); if let Some(link) = link { self.cx.cache.intra_doc_links.entry(item.item_id).or_default().insert(link); } @@ -1003,13 +1007,12 @@ impl LinkCollector<'_, '_> { /// FIXME(jynelson): this is way too many arguments fn resolve_link( &mut self, + dox: &String, item: &Item, item_id: DefId, module_id: DefId, - dox: &str, - link: &PreprocessedMarkdownLink, + PreprocessedMarkdownLink(pp_link, ori_link): &PreprocessedMarkdownLink, ) -> Option<ItemLink> { - let PreprocessedMarkdownLink(pp_link, ori_link) = link; trace!("considering link '{}'", ori_link.link); let diag_info = DiagnosticInfo { @@ -1018,7 +1021,6 @@ impl LinkCollector<'_, '_> { ori_link: &ori_link.link, link_range: ori_link.range.clone(), }; - let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } = pp_link.as_ref().map_err(|err| err.report(self.cx, diag_info.clone())).ok()?; let disambiguator = *disambiguator; @@ -1036,8 +1038,24 @@ impl LinkCollector<'_, '_> { // resolutions are cached, for other links we want to report an error every // time so they are not cached. matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut), + false, )?; + if ori_link.display_text.is_some() { + self.resolve_display_text( + path_str, + ResolutionInfo { + item_id, + module_id, + dis: disambiguator, + path_str: ori_link.display_text.clone()?.into_boxed_str(), + extra_fragment: extra_fragment.clone(), + }, + &ori_link, + &diag_info, + ); + } + // Check for a primitive which might conflict with a module // Report the ambiguity and require that the user specify which one they meant. // FIXME: could there ever be a primitive not in the type namespace? @@ -1124,10 +1142,10 @@ impl LinkCollector<'_, '_> { item: &Item, diag_info: &DiagnosticInfo<'_>, ) -> Option<()> { - debug!("intra-doc link to {} resolved to {:?}", path_str, (kind, id)); + debug!("intra-doc link to {path_str} resolved to {:?}", (kind, id)); // Disallow e.g. linking to enums with `struct@` - debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator); + debug!("saw kind {kind:?} with disambiguator {disambiguator:?}"); match (kind, disambiguator) { | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const))) // NOTE: this allows 'method' to mean both normal functions and associated functions @@ -1168,7 +1186,7 @@ impl LinkCollector<'_, '_> { diag_info: &DiagnosticInfo<'_>, ) { // The resolved item did not match the disambiguator; give a better error than 'not found' - let msg = format!("incompatible link kind for `{}`", path_str); + let msg = format!("incompatible link kind for `{path_str}`"); let callback = |diag: &mut Diagnostic, sp: Option<rustc_span::Span>, link_range| { let note = format!( "this link resolved to {} {}, which is not {} {}", @@ -1217,6 +1235,9 @@ impl LinkCollector<'_, '_> { // If errors are cached then they are only reported on first occurrence // which we want in some cases but not in others. cache_errors: bool, + // If this call is intended to be recoverable, then pass true to silence. + // This is only recoverable when path is failed to resolved. + recoverable: bool, ) -> Option<(Res, Option<UrlFragment>)> { if let Some(res) = self.visited_links.get(&key) { if res.is_some() || cache_errors { @@ -1224,7 +1245,7 @@ impl LinkCollector<'_, '_> { } } - let mut candidates = self.resolve_with_disambiguator(&key, diag.clone()); + let mut candidates = self.resolve_with_disambiguator(&key, diag.clone(), recoverable); // FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether. // However I'm not sure how to check that across crates. @@ -1275,6 +1296,9 @@ impl LinkCollector<'_, '_> { &mut self, key: &ResolutionInfo, diag: DiagnosticInfo<'_>, + // If this call is intended to be recoverable, then pass true to silence. + // This is only recoverable when path is failed to resolved. + recoverable: bool, ) -> Vec<(Res, Option<DefId>)> { let disambiguator = key.dis; let path_str = &key.path_str; @@ -1304,7 +1328,9 @@ impl LinkCollector<'_, '_> { } } } - resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); + if !recoverable { + resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); + } return vec![]; } } @@ -1341,13 +1367,15 @@ impl LinkCollector<'_, '_> { .fold(0, |acc, res| if let Ok(res) = res { acc + res.len() } else { acc }); if len == 0 { - resolution_failure( - self, - diag, - path_str, - disambiguator, - candidates.into_iter().filter_map(|res| res.err()).collect(), - ); + if !recoverable { + resolution_failure( + self, + diag, + path_str, + disambiguator, + candidates.into_iter().filter_map(|res| res.err()).collect(), + ); + } return vec![]; } else if len == 1 { candidates.into_iter().filter_map(|res| res.ok()).flatten().collect::<Vec<_>>() @@ -1368,6 +1396,58 @@ impl LinkCollector<'_, '_> { } } } + + /// Resolve display text if the provided link has separated parts of links. + /// + /// For example: + /// Inline link `[display_text](dest_link)` and reference link `[display_text][reference_link]` has + /// separated parts of links. + fn resolve_display_text( + &mut self, + explicit_link: &Box<str>, + display_res_info: ResolutionInfo, + ori_link: &MarkdownLink, + diag_info: &DiagnosticInfo<'_>, + ) { + // Check if explicit resolution's path is same as resolution of original link's display text path, see + // tests/rustdoc-ui/lint/redundant_explicit_links.rs for more cases. + // + // To avoid disambiguator from panicking, we check if display text path is possible to be disambiguated + // into explicit path. + if !matches!( + ori_link.kind, + LinkType::Inline | LinkType::Reference | LinkType::ReferenceUnknown + ) { + return; + } + + // Algorithm to check if display text could possibly be the explicit link: + // + // Consider 2 links which are display text and explicit link, pick the shorter + // one as symbol and longer one as full qualified path, and tries to match symbol + // to the full qualified path's last symbol. + // + // Otherwise, check if 2 links are same, if so, skip the resolve process. + // + // Notice that this algorithm is passive, might possibly miss actual redudant cases. + let explicit_link = explicit_link.to_string(); + let display_text = ori_link.display_text.as_ref().unwrap(); + + if display_text.len() == explicit_link.len() { + // Whether they are same or not, skip the resolve process. + return; + } + + if explicit_link.ends_with(&display_text[..]) || display_text.ends_with(&explicit_link[..]) + { + self.resolve_with_disambiguator_cached( + display_res_info, + diag_info.clone(), // this struct should really be Copy, but Range is not :( + false, + true, + ); + } + } } /// Get the section of a link between the backticks, @@ -1453,7 +1533,7 @@ impl Disambiguator { "value" => NS(Namespace::ValueNS), "macro" => NS(Namespace::MacroNS), "prim" | "primitive" => Primitive, - _ => return Err((format!("unknown disambiguator `{}`", prefix), 0..idx)), + _ => return Err((format!("unknown disambiguator `{prefix}`"), 0..idx)), }; Ok(Some((d, &rest[1..], &rest[1..]))) } else { @@ -1521,7 +1601,7 @@ enum Suggestion { impl Suggestion { fn descr(&self) -> Cow<'static, str> { match self { - Self::Prefix(x) => format!("prefix with `{}@`", x).into(), + Self::Prefix(x) => format!("prefix with `{x}@`").into(), Self::Function => "add parentheses".into(), Self::Macro => "add an exclamation mark".into(), Self::RemoveDisambiguator => "remove the disambiguator".into(), @@ -1531,9 +1611,9 @@ impl Suggestion { fn as_help(&self, path_str: &str) -> String { // FIXME: if this is an implied shortcut link, it's bad style to suggest `@` match self { - Self::Prefix(prefix) => format!("{}@{}", prefix, path_str), - Self::Function => format!("{}()", path_str), - Self::Macro => format!("{}!", path_str), + Self::Prefix(prefix) => format!("{prefix}@{path_str}"), + Self::Function => format!("{path_str}()"), + Self::Macro => format!("{path_str}!"), Self::RemoveDisambiguator => path_str.into(), } } @@ -1568,7 +1648,7 @@ impl Suggestion { match self { Self::Prefix(prefix) => { // FIXME: if this is an implied shortcut link, it's bad style to suggest `@` - let mut sugg = vec![(sp.with_hi(inner_sp.lo()), format!("{}@", prefix))]; + let mut sugg = vec![(sp.with_hi(inner_sp.lo()), format!("{prefix}@"))]; if sp.hi() != inner_sp.hi() { sugg.push((inner_sp.shrink_to_hi().with_hi(sp.hi()), String::new())); } @@ -1610,10 +1690,9 @@ fn report_diagnostic( DiagnosticInfo { item, ori_link: _, dox, link_range }: &DiagnosticInfo<'_>, decorate: impl FnOnce(&mut Diagnostic, Option<rustc_span::Span>, MarkdownLinkRange), ) { - let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id) - else { + let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id) else { // If non-local, no need to check anything. - info!("ignoring warning from parent crate: {}", msg); + info!("ignoring warning from parent crate: {msg}"); return; }; @@ -1691,15 +1770,14 @@ fn resolution_failure( report_diagnostic( tcx, BROKEN_INTRA_DOC_LINKS, - format!("unresolved link to `{}`", path_str), + format!("unresolved link to `{path_str}`"), &diag_info, |diag, sp, link_range| { - let item = |res: Res| format!("the {} `{}`", res.descr(), res.name(tcx),); + let item = |res: Res| format!("the {} `{}`", res.descr(), res.name(tcx)); let assoc_item_not_allowed = |res: Res| { let name = res.name(tcx); format!( - "`{}` is {} {}, not a module or type, and cannot have associated items", - name, + "`{name}` is {} {}, not a module or type, and cannot have associated items", res.article(), res.descr() ) @@ -1745,7 +1823,7 @@ fn resolution_failure( name = start; for ns in [TypeNS, ValueNS, MacroNS] { if let Ok(v_res) = collector.resolve(start, ns, item_id, module_id) { - debug!("found partial_res={:?}", v_res); + debug!("found partial_res={v_res:?}"); if !v_res.is_empty() { *partial_res = Some(full_res(tcx, v_res[0])); *unresolved = end.into(); @@ -1766,10 +1844,10 @@ fn resolution_failure( let note = if partial_res.is_some() { // Part of the link resolved; e.g. `std::io::nonexistent` let module_name = tcx.item_name(module); - format!("no item named `{}` in module `{}`", unresolved, module_name) + format!("no item named `{unresolved}` in module `{module_name}`") } else { // None of the link resolved; e.g. `Notimported` - format!("no item named `{}` in scope", unresolved) + format!("no item named `{unresolved}` in scope") }; if let Some(span) = sp { diag.span_label(span, note); @@ -1811,7 +1889,7 @@ fn resolution_failure( Res::Primitive(_) => None, }; let is_struct_variant = |did| { - if let ty::Adt(def, _) = tcx.type_of(did).subst_identity().kind() + if let ty::Adt(def, _) = tcx.type_of(did).instantiate_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 @@ -1860,8 +1938,13 @@ fn resolution_failure( } return; } - Trait | TyAlias | ForeignTy | OpaqueTy | ImplTraitPlaceholder - | TraitAlias | TyParam | Static(_) => "associated item", + Trait + | TyAlias { .. } + | ForeignTy + | OpaqueTy + | TraitAlias + | TyParam + | Static(_) => "associated item", Impl { .. } | GlobalAsm => unreachable!("not a path"), } } else { @@ -1869,11 +1952,9 @@ fn resolution_failure( }; let name = res.name(tcx); let note = format!( - "the {} `{}` has no {} named `{}`", - res.descr(), - name, - disambiguator.map_or(path_description, |d| d.descr()), - unresolved, + "the {res} `{name}` has no {disamb_res} named `{unresolved}`", + res = res.descr(), + disamb_res = disambiguator.map_or(path_description, |d| d.descr()), ); if let Some(span) = sp { diag.span_label(span, note); @@ -1968,7 +2049,7 @@ fn report_malformed_generics( report_diagnostic( cx.tcx, BROKEN_INTRA_DOC_LINKS, - format!("unresolved link to `{}`", path_str), + format!("unresolved link to `{path_str}`"), &diag_info, |diag, sp, _link_range| { let note = match err { @@ -2020,7 +2101,7 @@ fn ambiguity_error( return false; } - let mut msg = format!("`{}` is ", path_str); + let mut msg = format!("`{path_str}` is "); match kinds.as_slice() { [res1, res2] => { msg += &format!( @@ -2084,7 +2165,7 @@ fn suggest_disambiguator( diag.span_suggestion_verbose(sp, help, suggestion_text, Applicability::MaybeIncorrect); } } else { - diag.help(format!("{}: {}", help, suggestion.as_help(path_str))); + diag.help(format!("{help}: {}", suggestion.as_help(path_str))); } } @@ -2098,8 +2179,7 @@ fn privacy_error(cx: &DocContext<'_>, diag_info: &DiagnosticInfo<'_>, path_str: } None => "<unknown>", }; - let msg = - format!("public documentation for `{}` links to private item `{}`", item_name, path_str); + let msg = format!("public documentation for `{item_name}` links to private item `{path_str}`"); report_diagnostic(cx.tcx, PRIVATE_INTRA_DOC_LINKS, msg, diag_info, |diag, sp, _link_range| { if let Some(sp) = sp { @@ -2150,6 +2230,6 @@ fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<Res> { "never" | "!" => Never, _ => return None, }; - debug!("resolved primitives {:?}", prim); + debug!("resolved primitives {prim:?}"); Some(Res::Primitive(prim)) } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index fbf827cce..ff89d4e08 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -103,11 +103,11 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> // nothing to do with the inherent impl. // // Rustdoc currently uses these `impl` block as a source of - // the `Ty`, as well as the `ParamEnv`, `SubstsRef`, and + // the `Ty`, as well as the `ParamEnv`, `GenericArgsRef`, and // `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).subst_identity(); + let ty = tcx.type_of(def_id).instantiate_identity(); match ty.kind() { ty::Slice(ty) | ty::Ref(_, ty, _) diff --git a/src/librustdoc/passes/lint.rs b/src/librustdoc/passes/lint.rs index e653207b9..c6d5b7bd3 100644 --- a/src/librustdoc/passes/lint.rs +++ b/src/librustdoc/passes/lint.rs @@ -4,6 +4,7 @@ mod bare_urls; mod check_code_block_syntax; mod html_tags; +mod redundant_explicit_links; mod unescaped_backticks; use super::Pass; @@ -29,6 +30,7 @@ impl<'a, 'tcx> DocVisitor for Linter<'a, 'tcx> { check_code_block_syntax::visit_item(self.cx, item); html_tags::visit_item(self.cx, item); unescaped_backticks::visit_item(self.cx, item); + redundant_explicit_links::visit_item(self.cx, item); self.visit_item_recur(item) } diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index e9cee92d2..97078a5cb 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -13,11 +13,10 @@ use std::mem; use std::sync::LazyLock; pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) { - let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id) - else { - // If non-local, no need to check anything. - return; - }; + let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id) else { + // If non-local, no need to check anything. + return; + }; let dox = item.doc_value(); if !dox.is_empty() { let report_diag = @@ -29,7 +28,7 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) { .span_suggestion( sp, "use an automatic link instead", - format!("<{}>", url), + format!("<{url}>"), Applicability::MachineApplicable, ) }); @@ -75,7 +74,7 @@ fn find_raw_urls( range: Range<usize>, f: &impl Fn(&DocContext<'_>, &'static str, &str, Range<usize>), ) { - trace!("looking for raw urls in {}", text); + trace!("looking for raw urls in {text}"); // For now, we only check "full" URLs (meaning, starting with "http://" or "https://"). for match_ in URL_REGEX.find_iter(text) { let url = match_.as_str(); diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index f489f5081..37e28e1fb 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -40,7 +40,7 @@ fn check_rust_syntax( let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle }; let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let handler = Handler::with_emitter(false, None, Box::new(emitter)); + let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let source = dox[code_block.code].to_owned(); let sess = ParseSess::with_span_handler(handler, sm); @@ -67,12 +67,11 @@ fn check_rust_syntax( return; } - let Some(local_id) = item.item_id.as_def_id().and_then(|x| x.as_local()) - else { - // We don't need to check the syntax for other crates so returning - // without doing anything should not be a problem. - return; - }; + let Some(local_id) = item.item_id.as_def_id().and_then(|x| x.as_local()) else { + // We don't need to check the syntax for other crates so returning + // without doing anything should not be a problem. + return; + }; let empty_block = code_block.lang_string == Default::default() && code_block.is_fenced; let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None; @@ -108,7 +107,7 @@ fn check_rust_syntax( // just give a `help` instead. lint.span_help( sp.from_inner(InnerSpan::new(0, 3)), - format!("{}: ```text", explanation), + format!("{explanation}: ```text"), ); } else if empty_block { lint.span_suggestion( @@ -119,7 +118,7 @@ fn check_rust_syntax( ); } } else if empty_block || is_ignore { - lint.help(format!("{}: ```text", explanation)); + lint.help(format!("{explanation}: ```text")); } // FIXME(#67563): Provide more context for these errors by displaying the spans inline. @@ -161,7 +160,7 @@ impl Emitter for BufferEmitter { .translate_message(&diag.message[0].0, &fluent_args) .unwrap_or_else(|e| panic!("{e}")); - buffer.messages.push(format!("error from rustc: {}", translated_main_message)); + buffer.messages.push(format!("error from rustc: {translated_main_message}")); if diag.is_error() { buffer.has_errors = true; } diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index 5273f52bc..c135c584c 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -14,7 +14,9 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { let tcx = cx.tcx; let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id) // If non-local, no need to check anything. - else { return }; + else { + return; + }; let dox = item.doc_value(); if !dox.is_empty() { let report_diag = |msg: String, range: &Range<usize>, is_open_tag: bool| { @@ -153,7 +155,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { let t = t.to_lowercase(); !ALLOWED_UNCLOSED.contains(&t.as_str()) }) { - report_diag(format!("unclosed HTML tag `{}`", tag), range, true); + report_diag(format!("unclosed HTML tag `{tag}`"), range, true); } if let Some(range) = is_in_comment { @@ -192,14 +194,14 @@ fn drop_tag( // `tags` is used as a queue, meaning that everything after `pos` is included inside it. // So `<h2><h3></h2>` will look like `["h2", "h3"]`. So when closing `h2`, we will still // have `h3`, meaning the tag wasn't closed as it should have. - f(format!("unclosed HTML tag `{}`", last_tag_name), &last_tag_span, true); + f(format!("unclosed HTML tag `{last_tag_name}`"), &last_tag_span, true); } // Remove the `tag_name` that was originally closed tags.pop(); } else { // It can happen for example in this case: `<h2></script></h2>` (the `h2` tag isn't required // but it helps for the visualization). - f(format!("unopened HTML tag `{}`", tag_name), &range, false); + f(format!("unopened HTML tag `{tag_name}`"), &range, false); } } @@ -353,7 +355,7 @@ fn extract_html_tag( if let Some(quote_pos) = quote_pos { let qr = Range { start: quote_pos, end: quote_pos }; f( - format!("unclosed quoted HTML attribute on tag `{}`", tag_name), + format!("unclosed quoted HTML attribute on tag `{tag_name}`"), &qr, false, ); @@ -366,7 +368,7 @@ fn extract_html_tag( at == "svg" || at == "math" }); if !valid { - f(format!("invalid self-closing HTML tag `{}`", tag_name), &r, false); + f(format!("invalid self-closing HTML tag `{tag_name}`"), &r, false); } } else { tags.push((tag_name, r)); diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs new file mode 100644 index 000000000..67cd2cc97 --- /dev/null +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -0,0 +1,347 @@ +use std::ops::Range; + +use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, OffsetIter, Parser, Tag}; +use rustc_ast::NodeId; +use rustc_errors::SuggestionStyle; +use rustc_hir::def::{DefKind, DocLinkResMap, Namespace, Res}; +use rustc_hir::HirId; +use rustc_lint_defs::Applicability; +use rustc_span::Symbol; + +use crate::clean::utils::find_nearest_parent_module; +use crate::clean::utils::inherits_doc_hidden; +use crate::clean::Item; +use crate::core::DocContext; +use crate::html::markdown::main_body_opts; +use crate::passes::source_span_for_markdown_range; + +#[derive(Debug)] +struct LinkData { + resolvable_link: Option<String>, + resolvable_link_range: Option<Range<usize>>, + display_link: String, +} + +pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { + let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id) else { + // If non-local, no need to check anything. + return; + }; + + let doc = item.doc_value(); + if doc.is_empty() { + return; + } + + if item.link_names(&cx.cache).is_empty() { + // If there's no link names in this item, + // then we skip resolution querying to + // avoid from panicking. + return; + } + + let Some(item_id) = item.def_id() else { + return; + }; + let Some(local_item_id) = item_id.as_local() else { + return; + }; + + let is_hidden = !cx.render_options.document_hidden + && (item.is_doc_hidden() || inherits_doc_hidden(cx.tcx, local_item_id, None)); + if is_hidden { + return; + } + let is_private = !cx.render_options.document_private + && !cx.cache.effective_visibilities.is_directly_public(cx.tcx, item_id); + if is_private { + return; + } + + check_redundant_explicit_link(cx, item, hir_id, &doc); +} + +fn check_redundant_explicit_link<'md>( + cx: &DocContext<'_>, + item: &Item, + hir_id: HirId, + doc: &'md str, +) -> Option<()> { + let mut broken_line_callback = |link: BrokenLink<'md>| Some((link.reference, "".into())); + let mut offset_iter = Parser::new_with_broken_link_callback( + &doc, + main_body_opts(), + Some(&mut broken_line_callback), + ) + .into_offset_iter(); + let item_id = item.def_id()?; + let module_id = match cx.tcx.def_kind(item_id) { + DefKind::Mod if item.inner_docs(cx.tcx) => item_id, + _ => find_nearest_parent_module(cx.tcx, item_id).unwrap(), + }; + let resolutions = cx.tcx.doc_link_resolutions(module_id); + + while let Some((event, link_range)) = offset_iter.next() { + match event { + Event::Start(Tag::Link(link_type, dest, _)) => { + let link_data = collect_link_data(&mut offset_iter); + + if let Some(resolvable_link) = link_data.resolvable_link.as_ref() { + if &link_data.display_link.replace("`", "") != resolvable_link { + // Skips if display link does not match to actual + // resolvable link, usually happens if display link + // has several segments, e.g. + // [this is just an `Option`](Option) + continue; + } + } + + let explicit_link = dest.to_string(); + let display_link = link_data.resolvable_link.clone()?; + + if explicit_link.ends_with(&display_link) || display_link.ends_with(&explicit_link) + { + match link_type { + LinkType::Inline | LinkType::ReferenceUnknown => { + check_inline_or_reference_unknown_redundancy( + cx, + item, + hir_id, + doc, + resolutions, + link_range, + dest.to_string(), + link_data, + if link_type == LinkType::Inline { + (b'(', b')') + } else { + (b'[', b']') + }, + ); + } + LinkType::Reference => { + check_reference_redundancy( + cx, + item, + hir_id, + doc, + resolutions, + link_range, + &dest, + link_data, + ); + } + _ => {} + } + } + } + _ => {} + } + } + + None +} + +/// FIXME(ChAoSUnItY): Too many arguments. +fn check_inline_or_reference_unknown_redundancy( + cx: &DocContext<'_>, + item: &Item, + hir_id: HirId, + doc: &str, + resolutions: &DocLinkResMap, + link_range: Range<usize>, + dest: String, + link_data: LinkData, + (open, close): (u8, u8), +) -> Option<()> { + let (resolvable_link, resolvable_link_range) = + (&link_data.resolvable_link?, &link_data.resolvable_link_range?); + let (dest_res, display_res) = + (find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?); + + if dest_res == display_res { + let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs) + .unwrap_or(item.attr_span(cx.tcx)); + let explicit_span = source_span_for_markdown_range( + cx.tcx, + &doc, + &offset_explicit_range(doc, link_range, open, close), + &item.attrs, + )?; + let display_span = + source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?; + + cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| { + lint.span_label(explicit_span, "explicit target is redundant") + .span_label(display_span, "because label contains path that resolves to same destination") + .note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links") + .span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways); + + lint + }); + } + + None +} + +/// FIXME(ChAoSUnItY): Too many arguments. +fn check_reference_redundancy( + cx: &DocContext<'_>, + item: &Item, + hir_id: HirId, + doc: &str, + resolutions: &DocLinkResMap, + link_range: Range<usize>, + dest: &CowStr<'_>, + link_data: LinkData, +) -> Option<()> { + let (resolvable_link, resolvable_link_range) = + (&link_data.resolvable_link?, &link_data.resolvable_link_range?); + let (dest_res, display_res) = + (find_resolution(resolutions, &dest)?, find_resolution(resolutions, resolvable_link)?); + + if dest_res == display_res { + let link_span = source_span_for_markdown_range(cx.tcx, &doc, &link_range, &item.attrs) + .unwrap_or(item.attr_span(cx.tcx)); + let explicit_span = source_span_for_markdown_range( + cx.tcx, + &doc, + &offset_explicit_range(doc, link_range.clone(), b'[', b']'), + &item.attrs, + )?; + let display_span = + source_span_for_markdown_range(cx.tcx, &doc, &resolvable_link_range, &item.attrs)?; + let def_span = source_span_for_markdown_range( + cx.tcx, + &doc, + &offset_reference_def_range(doc, dest, link_range), + &item.attrs, + )?; + + cx.tcx.struct_span_lint_hir(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| { + lint.span_label(explicit_span, "explicit target is redundant") + .span_label(display_span, "because label contains path that resolves to same destination") + .span_note(def_span, "referenced explicit link target defined here") + .note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links") + .span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways); + + lint + }); + } + + None +} + +fn find_resolution(resolutions: &DocLinkResMap, path: &str) -> Option<Res<NodeId>> { + [Namespace::TypeNS, Namespace::ValueNS, Namespace::MacroNS] + .into_iter() + .find_map(|ns| resolutions.get(&(Symbol::intern(path), ns)).copied().flatten()) +} + +/// Collects all neccessary data of link. +fn collect_link_data(offset_iter: &mut OffsetIter<'_, '_>) -> LinkData { + let mut resolvable_link = None; + let mut resolvable_link_range = None; + let mut display_link = String::new(); + + while let Some((event, range)) = offset_iter.next() { + match event { + Event::Text(code) => { + let code = code.to_string(); + display_link.push_str(&code); + resolvable_link = Some(code); + resolvable_link_range = Some(range); + } + Event::Code(code) => { + let code = code.to_string(); + display_link.push('`'); + display_link.push_str(&code); + display_link.push('`'); + resolvable_link = Some(code); + resolvable_link_range = Some(range); + } + Event::End(_) => { + break; + } + _ => {} + } + } + + LinkData { resolvable_link, resolvable_link_range, display_link } +} + +fn offset_explicit_range(md: &str, link_range: Range<usize>, open: u8, close: u8) -> Range<usize> { + let mut open_brace = !0; + let mut close_brace = !0; + for (i, b) in md.as_bytes()[link_range.clone()].iter().copied().enumerate().rev() { + let i = i + link_range.start; + if b == close { + close_brace = i; + break; + } + } + + if close_brace < link_range.start || close_brace >= link_range.end { + return link_range; + } + + let mut nesting = 1; + + for (i, b) in md.as_bytes()[link_range.start..close_brace].iter().copied().enumerate().rev() { + let i = i + link_range.start; + if b == close { + nesting += 1; + } + if b == open { + nesting -= 1; + } + if nesting == 0 { + open_brace = i; + break; + } + } + + assert!(open_brace != close_brace); + + if open_brace < link_range.start || open_brace >= link_range.end { + return link_range; + } + // do not actually include braces in the span + (open_brace + 1)..close_brace +} + +fn offset_reference_def_range( + md: &str, + dest: &CowStr<'_>, + link_range: Range<usize>, +) -> Range<usize> { + // For diagnostics, we want to underline the link's definition but `span` will point at + // where the link is used. This is a problem for reference-style links, where the definition + // is separate from the usage. + + match dest { + // `Borrowed` variant means the string (the link's destination) may come directly from + // the markdown text and we can locate the original link destination. + // NOTE: LinkReplacer also provides `Borrowed` but possibly from other sources, + // so `locate()` can fall back to use `span`. + CowStr::Borrowed(s) => { + // FIXME: remove this function once pulldown_cmark can provide spans for link definitions. + unsafe { + let s_start = dest.as_ptr(); + let s_end = s_start.add(s.len()); + let md_start = md.as_ptr(); + let md_end = md_start.add(md.len()); + if md_start <= s_start && s_end <= md_end { + let start = s_start.offset_from(md_start) as usize; + let end = s_end.offset_from(md_start) as usize; + start..end + } else { + link_range + } + } + } + + // For anything else, we can only use the provided range. + CowStr::Boxed(_) | CowStr::Inlined(_) => link_range, + } +} diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 8a33e51b3..95273a225 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -38,8 +38,9 @@ impl<'a, 'tcx> CfgPropagator<'a, 'tcx> { _ => return, }; - let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) - else { return }; + let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local()) else { + return; + }; if check_parent { let expected_parent = self.cx.tcx.opt_local_parent(def_id); diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index e2e38d3e7..81a90ed49 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -6,11 +6,11 @@ use rustc_span::symbol::sym; use std::mem; use crate::clean; +use crate::clean::utils::inherits_doc_hidden; use crate::clean::{Item, ItemIdSet}; 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", @@ -42,6 +42,7 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea cache: &cx.cache, is_json_output, document_private: cx.render_options.document_private, + document_hidden: cx.render_options.document_hidden, }; stripper.fold_crate(krate) } diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs index 4c992e948..468712ba3 100644 --- a/src/librustdoc/passes/strip_priv_imports.rs +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -13,5 +13,10 @@ pub(crate) const STRIP_PRIV_IMPORTS: Pass = Pass { pub(crate) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { let is_json_output = cx.output_format.is_json() && !cx.show_coverage; - ImportStripper { tcx: cx.tcx, is_json_output }.fold_crate(krate) + ImportStripper { + tcx: cx.tcx, + is_json_output, + document_hidden: cx.render_options.document_hidden, + } + .fold_crate(krate) } diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index bb6dccb7c..3b6f484fd 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -28,8 +28,12 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> is_json_output, tcx: cx.tcx, }; - krate = - ImportStripper { tcx: cx.tcx, is_json_output }.fold_crate(stripper.fold_crate(krate)); + krate = ImportStripper { + tcx: cx.tcx, + is_json_output, + document_hidden: cx.render_options.document_hidden, + } + .fold_crate(stripper.fold_crate(krate)); } // strip all impls referencing private items @@ -39,6 +43,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> cache: &cx.cache, is_json_output, document_private: cx.render_options.document_private, + document_hidden: cx.render_options.document_hidden, }; stripper.fold_crate(krate) } diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 90c361d9d..64a4ace5b 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -3,6 +3,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::{TyCtxt, Visibility}; use std::mem; +use crate::clean::utils::inherits_doc_hidden; use crate::clean::{self, Item, ItemId, ItemIdSet}; use crate::fold::{strip_item, DocFolder}; use crate::formats::cache::Cache; @@ -151,6 +152,7 @@ pub(crate) struct ImplStripper<'a, 'tcx> { pub(crate) cache: &'a Cache, pub(crate) is_json_output: bool, pub(crate) document_private: bool, + pub(crate) document_hidden: bool, } impl<'a> ImplStripper<'a, '_> { @@ -162,7 +164,13 @@ impl<'a> ImplStripper<'a, '_> { // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we // need to keep it. self.cache.effective_visibilities.is_exported(self.tcx, for_def_id) - && !item.is_doc_hidden() + && (self.document_hidden + || ((!item.is_doc_hidden() + && for_def_id + .as_local() + .map(|def_id| !inherits_doc_hidden(self.tcx, def_id, None)) + .unwrap_or(true)) + || self.cache.inlined_items.contains(&for_def_id))) } else { false } @@ -231,6 +239,7 @@ impl<'a> DocFolder for ImplStripper<'a, '_> { pub(crate) struct ImportStripper<'tcx> { pub(crate) tcx: TyCtxt<'tcx>, pub(crate) is_json_output: bool, + pub(crate) document_hidden: bool, } impl<'tcx> ImportStripper<'tcx> { @@ -247,8 +256,12 @@ impl<'tcx> ImportStripper<'tcx> { impl<'tcx> DocFolder for ImportStripper<'tcx> { fn fold_item(&mut self, i: Item) -> Option<Item> { match *i.kind { - clean::ImportItem(imp) if self.import_should_be_hidden(&i, &imp) => None, - clean::ImportItem(_) if i.is_doc_hidden() => None, + clean::ImportItem(imp) + if !self.document_hidden && self.import_should_be_hidden(&i, &imp) => + { + None + } + // clean::ImportItem(_) if !self.document_hidden && i.is_doc_hidden() => None, clean::ExternCrateItem { .. } | clean::ImportItem(..) if i.visibility(self.tcx) != Some(Visibility::Public) => { diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index d2fa7769b..534c6eebb 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -169,7 +169,7 @@ where }; let ident_span = path.ident.span; - (tcx.type_of(def_id).subst_identity(), call_span, ident_span) + (tcx.type_of(def_id).instantiate_identity(), call_span, ident_span) } _ => { return; @@ -255,7 +255,7 @@ where let fn_key = tcx.def_path_hash(*def_id); let fn_entries = self.calls.entry(fn_key).or_default(); - trace!("Including expr: {:?}", call_span); + trace!("Including expr: {call_span:?}"); let enclosing_item_span = source_map.span_extend_to_prev_char(enclosing_item_span, '\n', false); let location = @@ -345,7 +345,7 @@ pub(crate) fn load_call_locations( let inner = || { let mut all_calls: AllCallLocations = FxHashMap::default(); for path in with_examples { - let bytes = fs::read(&path).map_err(|e| format!("{} (for path {})", e, path))?; + let bytes = fs::read(&path).map_err(|e| format!("{e} (for path {path})"))?; let mut decoder = MemDecoder::new(&bytes, 0); let calls = AllCallLocations::decode(&mut decoder); @@ -358,7 +358,7 @@ pub(crate) fn load_call_locations( }; inner().map_err(|e: String| { - diag.err(format!("failed to load examples: {}", e)); + diag.err(format!("failed to load examples: {e}")); 1 }) } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index fcf591a93..549fd67e3 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -16,6 +16,7 @@ use rustc_span::Span; use std::mem; +use crate::clean::utils::{inherits_doc_hidden, should_ignore_res}; use crate::clean::{cfg::Cfg, reexport_chain, AttributesExt, NestedAttributesExt}; use crate::core; @@ -35,6 +36,8 @@ pub(crate) struct Module<'hir> { (LocalDefId, Option<Symbol>), (&'hir hir::Item<'hir>, Option<Symbol>, Option<LocalDefId>), >, + /// Same as for `items`. + pub(crate) inlined_foreigns: FxIndexMap<(DefId, Option<Symbol>), (Res, LocalDefId)>, pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>, } @@ -54,6 +57,7 @@ impl Module<'_> { import_id, mods: Vec::new(), items: FxIndexMap::default(), + inlined_foreigns: FxIndexMap::default(), foreigns: Vec::new(), } } @@ -70,33 +74,6 @@ fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<Symbol> { std::iter::once(crate_name).chain(relative).collect() } -pub(crate) fn inherits_doc_hidden( - tcx: TyCtxt<'_>, - mut def_id: LocalDefId, - stop_at: Option<LocalDefId>, -) -> bool { - let hir = tcx.hir(); - while let Some(id) = tcx.opt_local_parent(def_id) { - if let Some(stop_at) = stop_at && id == stop_at { - return false; - } - def_id = id; - if tcx.is_doc_hidden(def_id.to_def_id()) { - return true; - } else if let Some(node) = hir.find_by_def_id(def_id) && - matches!( - node, - hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }), - ) - { - // `impl` blocks stand a bit on their own: unless they have `#[doc(hidden)]` directly - // on them, they don't inherit it from the parent context. - return false; - } - } - false -} - pub(crate) struct RustdocVisitor<'a, 'tcx> { cx: &'a mut core::DocContext<'tcx>, view_item_stack: LocalDefIdSet, @@ -203,7 +180,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { /// 1. The items which are not glob imports/reexports. /// 2. The glob imports/reexports. fn visit_mod_contents(&mut self, def_id: LocalDefId, m: &'tcx hir::Mod<'tcx>) { - debug!("Going through module {:?}", m); + debug!("Going through module {m:?}"); // Keep track of if there were any private modules in the path. let orig_inside_public_path = self.inside_public_path; self.inside_public_path &= self.cx.tcx.local_visibility(def_id).is_public(); @@ -226,7 +203,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } } self.inside_public_path = orig_inside_public_path; - debug!("Leaving module {:?}", m); + debug!("Leaving module {m:?}"); } /// Tries to resolve the target of a `pub use` statement and inlines the @@ -262,34 +239,44 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { return false; }; + let document_hidden = self.cx.render_options.document_hidden; let use_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id)); // Don't inline `doc(hidden)` imports so they can be stripped at a later stage. let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline) - || use_attrs.lists(sym::doc).has_word(sym::hidden); + || (document_hidden && use_attrs.lists(sym::doc).has_word(sym::hidden)); if is_no_inline { return false; } - // For cross-crate impl inlining we need to know whether items are - // reachable in documentation -- a previously unreachable item can be - // made reachable by cross-crate inlining which we're checking here. - // (this is done here because we need to know this upfront). - if !ori_res_did.is_local() && !is_no_inline { - crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did); - return false; - } - + let is_hidden = !document_hidden && tcx.is_doc_hidden(ori_res_did); let Some(res_did) = ori_res_did.as_local() else { - return false; + // For cross-crate impl inlining we need to know whether items are + // reachable in documentation -- a previously unreachable item can be + // made reachable by cross-crate inlining which we're checking here. + // (this is done here because we need to know this upfront). + crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did); + if is_hidden || glob { + return false; + } + // We store inlined foreign items otherwise, it'd mean that the `use` item would be kept + // around. It's not a problem unless this `use` imports both a local AND a foreign item. + // If a local item is inlined, its `use` is not supposed to still be around in `clean`, + // which would make appear the `use` in the generated documentation like the local item + // was not inlined even though it actually was. + self.modules + .last_mut() + .unwrap() + .inlined_foreigns + .insert((ori_res_did, renamed), (res, def_id)); + return true; }; let is_private = !self.cx.cache.effective_visibilities.is_directly_public(tcx, ori_res_did); - let is_hidden = tcx.is_doc_hidden(ori_res_did); let item = tcx.hir().get_by_def_id(res_did); if !please_inline { - let inherits_hidden = inherits_doc_hidden(tcx, res_did, None); + let inherits_hidden = !document_hidden && inherits_doc_hidden(tcx, res_did, None); // Only inline if requested or if the item would otherwise be stripped. if (!is_private && !inherits_hidden) || ( is_hidden && @@ -313,7 +300,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { return false; } - let inlined = match tcx.hir().get_by_def_id(res_did) { + let inlined = match item { // Bang macros are handled a bit on their because of how they are handled by the // compiler. If they have `#[doc(hidden)]` and the re-export doesn't have // `#[doc(inline)]`, then we don't inline it. @@ -345,7 +332,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }; self.view_item_stack.remove(&res_did); if inlined { - self.cx.cache.inlined_items.insert(res_did.to_def_id()); + self.cx.cache.inlined_items.insert(ori_res_did); } inlined } @@ -359,8 +346,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { import_def_id: LocalDefId, target_def_id: LocalDefId, ) -> bool { + if self.cx.render_options.document_hidden { + return true; + } let tcx = self.cx.tcx; - let item_def_id = reexport_chain(tcx, import_def_id, target_def_id) + let item_def_id = reexport_chain(tcx, import_def_id, target_def_id.to_def_id()) .iter() .flat_map(|reexport| reexport.id()) .map(|id| id.expect_local()) @@ -404,7 +394,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { renamed: Option<Symbol>, import_id: Option<LocalDefId>, ) { - debug!("visiting item {:?}", item); + debug!("visiting item {item:?}"); if self.inside_body { // Only impls can be "seen" outside a body. For example: // @@ -450,7 +440,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { for &res in &path.res { // Struct and variant constructors and proc macro stubs always show up alongside // their definitions, we've already processed them so just discard these. - if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { + if should_ignore_res(res) { continue; } @@ -479,7 +469,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { continue; } } - self.add_to_current_mod(item, renamed, import_id); } } diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index fd4f92541..82c974661 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -33,6 +33,7 @@ pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) { tcx: cx.tcx, extern_public: &mut cx.cache.effective_visibilities.extern_public, visited_mods: Default::default(), + document_hidden: cx.render_options.document_hidden, } .visit_item(def_id) } @@ -45,6 +46,7 @@ struct LibEmbargoVisitor<'a, 'tcx> { extern_public: &'a mut DefIdSet, // Keeps track of already visited modules, in case a module re-exports its parent visited_mods: DefIdSet, + document_hidden: bool, } impl LibEmbargoVisitor<'_, '_> { @@ -63,7 +65,7 @@ impl LibEmbargoVisitor<'_, '_> { } fn visit_item(&mut self, def_id: DefId) { - if !self.tcx.is_doc_hidden(def_id) { + if self.document_hidden || !self.tcx.is_doc_hidden(def_id) { self.extern_public.insert(def_id); if self.tcx.def_kind(def_id) == DefKind::Mod { self.visit_mod(def_id); |