From 8adb0b6d6c264facc6e213ad06a25194e7591682 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Wed, 24 Nov 2021 12:21:44 -0800 Subject: [PATCH 01/13] Clean up `clean` re-exports This will allow re-exporting only certain enum variants. --- src/librustdoc/clean/mod.rs | 5 ----- src/librustdoc/clean/types.rs | 13 ++++++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5c893e44a9fc3..10648d74e930a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -43,11 +43,6 @@ use utils::*; crate use utils::{get_auto_trait_and_blanket_impls, krate, register_res}; -crate use self::types::FnRetTy::*; -crate use self::types::ItemKind::*; -crate use self::types::SelfTy::*; -crate use self::types::Type::*; -crate use self::types::Visibility::{Inherited, Public}; crate use self::types::*; crate trait Clean { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 9e088ac72ad2f..7704e66bd394d 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -34,7 +34,6 @@ use rustc_target::spec::abi::Abi; use crate::clean::cfg::Cfg; use crate::clean::external_path; use crate::clean::inline::{self, print_inlined_const}; -use crate::clean::types::Type::{QPath, ResolvedPath}; use crate::clean::utils::{is_literal_expr, print_const_expr, print_evaluated_const}; use crate::clean::Clean; use crate::core::DocContext; @@ -43,10 +42,14 @@ use crate::formats::item_type::ItemType; use crate::html::render::cache::ExternalLocation; use crate::html::render::Context; -use self::FnRetTy::*; -use self::ItemKind::*; -use self::SelfTy::*; -use self::Type::*; +crate use self::FnRetTy::*; +crate use self::ItemKind::*; +crate use self::SelfTy::*; +crate use self::Type::{ + Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, QPath, + RawPointer, ResolvedPath, Slice, Tuple, +}; +crate use self::Visibility::{Inherited, Public}; crate type ItemIdSet = FxHashSet; From d81deb33fa8e9f03006b637fd0c035dc7acc5343 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Wed, 24 Nov 2021 12:27:37 -0800 Subject: [PATCH 02/13] Stop re-exporting `Type::ResolvedPath` I would like to rename it to `Type::Path`, but then it can't be re-exported since the name would conflict with the `Path` struct. Usually enum variants are referred to using their qualified names in Rust (and parts of rustdoc already do that with `clean::Type`), so this is also more consistent with the language. --- src/librustdoc/clean/mod.rs | 7 +++---- src/librustdoc/clean/types.rs | 8 ++++---- src/librustdoc/clean/utils.rs | 7 +++---- src/librustdoc/formats/cache.rs | 8 +++++--- src/librustdoc/html/format.rs | 2 +- src/librustdoc/html/render/cache.rs | 2 +- src/librustdoc/html/render/mod.rs | 2 +- src/librustdoc/html/render/print_item.rs | 25 ++++++++++++------------ src/librustdoc/json/conversions.rs | 14 ++++++++----- 9 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 10648d74e930a..e8c55d10901e8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -41,9 +41,8 @@ use crate::visit_ast::Module as DocModule; use utils::*; -crate use utils::{get_auto_trait_and_blanket_impls, krate, register_res}; - crate use self::types::*; +crate use self::utils::{get_auto_trait_and_blanket_impls, krate, register_res}; crate trait Clean { fn clean(&self, cx: &mut DocContext<'_>) -> T; @@ -1406,12 +1405,12 @@ impl<'tcx> Clean for Ty<'tcx> { }; inline::record_extern_fqn(cx, did, kind); let path = external_path(cx, did, false, vec![], substs); - ResolvedPath { path } + Type::ResolvedPath { path } } ty::Foreign(did) => { inline::record_extern_fqn(cx, did, ItemType::ForeignType); let path = external_path(cx, did, false, vec![], InternalSubsts::empty()); - ResolvedPath { path } + Type::ResolvedPath { path } } ty::Dynamic(obj, ref reg) => { // HACK: pick the first `did` as the `did` of the trait object. Someone diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 7704e66bd394d..b5192d2c08257 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -47,7 +47,7 @@ crate use self::ItemKind::*; crate use self::SelfTy::*; crate use self::Type::{ Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, QPath, - RawPointer, ResolvedPath, Slice, Tuple, + RawPointer, Slice, Tuple, }; crate use self::Visibility::{Inherited, Public}; @@ -1488,7 +1488,7 @@ impl Type { /// Checks if this is a `T::Name` path for an associated type. crate fn is_assoc_ty(&self) -> bool { match self { - ResolvedPath { path, .. } => path.is_assoc_ty(), + Type::ResolvedPath { path, .. } => path.is_assoc_ty(), _ => false, } } @@ -1502,7 +1502,7 @@ impl Type { crate fn generics(&self) -> Option> { match self { - ResolvedPath { path, .. } => path.generics(), + Type::ResolvedPath { path, .. } => path.generics(), _ => None, } } @@ -1525,7 +1525,7 @@ impl Type { fn inner_def_id(&self, cache: Option<&Cache>) -> Option { let t: PrimitiveType = match *self { - ResolvedPath { ref path } => return Some(path.def_id()), + Type::ResolvedPath { ref path } => return Some(path.def_id()), DynTrait(ref bounds, _) => return Some(bounds[0].trait_.def_id()), Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 1141aff41f2da..40fa5b8a33e1b 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -2,8 +2,7 @@ use crate::clean::auto_trait::AutoTraitFinder; use crate::clean::blanket_impl::BlanketImplFinder; use crate::clean::{ inline, Clean, Crate, ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item, - ItemKind, Lifetime, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type, - TypeBinding, Visibility, + ItemKind, Lifetime, Path, PathSegment, Primitive, PrimitiveType, Type, TypeBinding, Visibility, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -187,7 +186,7 @@ crate fn build_deref_target_impls(cx: &mut DocContext<'_>, items: &[Item], ret: for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) { inline::build_impl(cx, None, did, None, ret); } - } else if let ResolvedPath { path } = target { + } else if let Type::ResolvedPath { path } = target { let did = path.def_id(); if !did.is_local() { inline::build_impls(cx, None, did, None, ret); @@ -362,7 +361,7 @@ crate fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => Generic(path.segments[0].name), _ => { let _ = register_res(cx, path.res); - ResolvedPath { path } + Type::ResolvedPath { path } } } } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index dd13c4809bbce..06fcdd0521287 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -401,7 +401,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { clean::ImplItem(ref i) => { self.cache.parent_is_trait_impl = i.trait_.is_some(); match i.for_ { - clean::ResolvedPath { ref path } => { + clean::Type::ResolvedPath { ref path } => { self.cache.parent_stack.push(path.def_id()); true } @@ -436,8 +436,10 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // Note: matching twice to restrict the lifetime of the `i` borrow. let mut dids = FxHashSet::default(); match i.for_ { - clean::ResolvedPath { ref path } - | clean::BorrowedRef { type_: box clean::ResolvedPath { ref path }, .. } => { + clean::Type::ResolvedPath { ref path } + | clean::BorrowedRef { + type_: box clean::Type::ResolvedPath { ref path }, .. + } => { dids.insert(path.def_id()); } clean::DynTrait(ref bounds, _) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 1ed3ba7ece056..d3603d17925da 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -762,7 +762,7 @@ fn fmt_type<'cx>( match *t { clean::Generic(name) => write!(f, "{}", name), - clean::ResolvedPath { ref path } => { + clean::Type::ResolvedPath { ref path } => { // Paths like `T::Output` and `Self::Output` should be rendered with all segments. let did = path.def_id(); resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx) diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index c114edf1e704c..ffa5ec8a3ee32 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -218,7 +218,7 @@ fn get_index_type(clean_type: &clean::Type, generics: Vec) -> Rend fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option { match *clean_type { - clean::ResolvedPath { ref path, .. } => { + clean::Type::ResolvedPath { ref path, .. } => { let path_segment = path.segments.last().unwrap(); Some(path_segment.name) } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 7d231a0d64922..42a2defa757a4 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1227,7 +1227,7 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { (mutability == Mutability::Mut, false, false) } - SelfTy::SelfExplicit(clean::ResolvedPath { path }) => { + SelfTy::SelfExplicit(clean::Type::ResolvedPath { path }) => { (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false) } SelfTy::SelfValue => (false, false, true), diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index e59b94f6b7d3d..f8a16fb9b713d 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -727,10 +727,10 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra let mut implementor_dups: FxHashMap = FxHashMap::default(); for implementor in implementors { match implementor.inner_impl().for_ { - clean::ResolvedPath { ref path } - | clean::BorrowedRef { type_: box clean::ResolvedPath { ref path }, .. } - if !path.is_assoc_ty() => - { + clean::Type::ResolvedPath { ref path } + | clean::BorrowedRef { + type_: box clean::Type::ResolvedPath { ref path }, .. + } if !path.is_assoc_ty() => { let did = path.def_id(); let &mut (prev_did, ref mut has_duplicates) = implementor_dups.entry(path.last()).or_insert((did, false)); @@ -1452,15 +1452,14 @@ fn render_implementor( ) { // If there's already another implementor that has the same abridged name, use the // full path, for example in `std::iter::ExactSizeIterator` - let use_absolute = match implementor.inner_impl().for_ { - clean::ResolvedPath { ref path, .. } - | clean::BorrowedRef { type_: box clean::ResolvedPath { ref path, .. }, .. } - if !path.is_assoc_ty() => - { - implementor_dups[&path.last()].1 - } - _ => false, - }; + let use_absolute = + match implementor.inner_impl().for_ { + clean::Type::ResolvedPath { ref path, .. } + | clean::BorrowedRef { + type_: box clean::Type::ResolvedPath { ref path, .. }, .. + } if !path.is_assoc_ty() => implementor_dups[&path.last()].1, + _ => false, + }; render_impl( w, cx, diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index a5c1eb1241096..ed8f7274591d6 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -365,7 +365,7 @@ impl FromWithTcx for GenericBound { match bound { TraitBound(clean::PolyTrait { trait_, generic_params }, modifier) => { // FIXME: should `trait_` be a clean::Path equivalent in JSON? - let trait_ = clean::ResolvedPath { path: trait_ }.into_tcx(tcx); + let trait_ = clean::Type::ResolvedPath { path: trait_ }.into_tcx(tcx); GenericBound::TraitBound { trait_, generic_params: generic_params.into_iter().map(|x| x.into_tcx(tcx)).collect(), @@ -388,9 +388,13 @@ crate fn from_trait_bound_modifier(modifier: rustc_hir::TraitBoundModifier) -> T impl FromWithTcx for Type { fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self { - use clean::Type::*; + use clean::Type::{ + Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, + QPath, RawPointer, Slice, Tuple, + }; + match ty { - ResolvedPath { path } => Type::ResolvedPath { + clean::Type::ResolvedPath { path } => Type::ResolvedPath { name: path.whole_name(), id: from_item_id(path.def_id().into()), args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))), @@ -435,7 +439,7 @@ impl FromWithTcx for Type { }, QPath { name, self_type, trait_, .. } => { // FIXME: should `trait_` be a clean::Path equivalent in JSON? - let trait_ = ResolvedPath { path: trait_ }.into_tcx(tcx); + let trait_ = clean::Type::ResolvedPath { path: trait_ }.into_tcx(tcx); Type::QualifiedPath { name: name.to_string(), self_type: Box::new((*self_type).into_tcx(tcx)), @@ -501,7 +505,7 @@ impl FromWithTcx for Impl { let provided_trait_methods = impl_.provided_trait_methods(tcx); let clean::Impl { unsafety, generics, trait_, for_, items, polarity, kind } = impl_; // FIXME: should `trait_` be a clean::Path equivalent in JSON? - let trait_ = trait_.map(|path| clean::ResolvedPath { path }.into_tcx(tcx)); + let trait_ = trait_.map(|path| clean::Type::ResolvedPath { path }.into_tcx(tcx)); // FIXME: use something like ImplKind in JSON? let (synthetic, blanket_impl) = match kind { clean::ImplKind::Normal => (false, None), From 553a84c445b39c439318f463d6a6f86fc391ed7b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 25 Nov 2021 18:18:36 -0800 Subject: [PATCH 03/13] Saner formatting for UTF8_CHAR_WIDTH table --- library/core/src/str/validations.rs | 33 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/library/core/src/str/validations.rs b/library/core/src/str/validations.rs index be9c41a491b8e..b2ea86d699aa6 100644 --- a/library/core/src/str/validations.rs +++ b/library/core/src/str/validations.rs @@ -244,22 +244,23 @@ pub(super) const fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { // https://tools.ietf.org/html/rfc3629 const UTF8_CHAR_WIDTH: &[u8; 256] = &[ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, // 0x1F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, // 0x3F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, // 0x5F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, // 0x7F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, // 0x9F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, // 0xBF - 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, // 0xDF - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEF - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xFF + // 1 2 3 4 5 6 7 8 9 A B C D E F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F ]; /// Given a first byte, determines how many bytes are in this UTF-8 character. From 1cf37189bc15d0bd4ac25281366d320e4a522b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Fri, 26 Nov 2021 10:33:16 +0000 Subject: [PATCH 04/13] Bump compiler-builtins to 0.1.53 Fixes a LLVM crash with the bpf targets --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edc227d9db7ba..48d9fdb3d03e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -678,9 +678,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.52" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6591c2442ee984e2b264638a8b5e7ae44fd47b32d28e3a08e2e9c3cdb0c2fb0" +checksum = "2467ff455350a4df7d02f1ed1449d0279605a763de5d586dcf6aa7d732508bcb" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 8f43e902a87ed..4147f7ee525c0 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -16,7 +16,7 @@ panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } libc = { version = "0.2.106", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.52" } +compiler_builtins = { version = "0.1.53" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.11", default-features = false, features = ['rustc-dep-of-std'] } From 6e0e22080424e7d8a4b4638bf4fb41e691efb26b Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 26 Nov 2021 09:55:46 -0500 Subject: [PATCH 05/13] Fix bug where submodules wouldn't be updated when running x.py from a subdirectory Previously, it would concatenate the relative path to the current subdirectory, which looked at the wrong folder. I tested this by checking out `1.56.1`, changing the current directory to `src/`, and running `../x.py build`. --- src/bootstrap/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 3b3c8a9227d95..1667dfc3f8544 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -493,7 +493,7 @@ impl Build { // NOTE: The check for the empty directory is here because when running x.py the first time, // the submodule won't be checked out. Check it out now so we can build it. - if !channel::GitInfo::new(false, relative_path).is_git() && !dir_is_empty(&absolute_path) { + if !channel::GitInfo::new(false, &absolute_path).is_git() && !dir_is_empty(&absolute_path) { return; } From 2d3d6bc5a2c3b46a4920cf550824054568e87fee Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 5 Nov 2021 22:37:33 +0800 Subject: [PATCH 06/13] Fix another ICE in rustdoc scrape_examples --- src/librustdoc/scrape_examples.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 3b39e3576e69a..a2357a6261753 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -142,16 +142,19 @@ where hir::ExprKind::Call(f, _) => { let types = tcx.typeck(ex.hir_id.owner); - match types.node_type_opt(f.hir_id) { - Some(ty) => (ty, ex.span), - None => { - return; - } + if let Some(ty) = types.node_type_opt(f.hir_id) { + (ty, ex.span) + } else { + return; } } hir::ExprKind::MethodCall(_, _, _, span) => { let types = tcx.typeck(ex.hir_id.owner); - let def_id = types.type_dependent_def_id(ex.hir_id).unwrap(); + let def_id = if let Some(def_id) = types.type_dependent_def_id(ex.hir_id) { + def_id + } else { + return; + }; (tcx.type_of(def_id), span) } _ => { From c86da9b7b2ae2c05d830fba463c12c58b7b3209e Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 6 Nov 2021 00:57:57 +0800 Subject: [PATCH 07/13] Add trace statements --- src/librustdoc/scrape_examples.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index a2357a6261753..10b6fdf87f419 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -145,6 +145,7 @@ where if let Some(ty) = types.node_type_opt(f.hir_id) { (ty, ex.span) } else { + trace!("node_type_opt({}) = None", f.hir_id); return; } } @@ -153,6 +154,7 @@ where let def_id = if let Some(def_id) = types.type_dependent_def_id(ex.hir_id) { def_id } else { + trace!("type_dependent_def_id({}) = None", ex.hir_id); return; }; (tcx.type_of(def_id), span) From 85c20698d494166e396740c352679980b63b116d Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 6 Nov 2021 01:18:32 +0800 Subject: [PATCH 08/13] Add ICE test --- src/test/rustdoc-ui/scrape-examples-ice.rs | 472 +++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 src/test/rustdoc-ui/scrape-examples-ice.rs diff --git a/src/test/rustdoc-ui/scrape-examples-ice.rs b/src/test/rustdoc-ui/scrape-examples-ice.rs new file mode 100644 index 0000000000000..df22237643402 --- /dev/null +++ b/src/test/rustdoc-ui/scrape-examples-ice.rs @@ -0,0 +1,472 @@ +// compile-flags: -Z unstable-options --scrape-examples-output-path t.calls --scrape-examples-target-crate foobar +// check-pass + +// Copyright 2013-2016 The rust-url developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! URLs use special characters to indicate the parts of the request. +//! For example, a `?` question mark marks the end of a path and the start of a query string. +//! In order for that character to exist inside a path, it needs to be encoded differently. +//! +//! Percent encoding replaces reserved characters with the `%` escape character +//! followed by a byte value as two hexadecimal digits. +//! For example, an ASCII space is replaced with `%20`. +//! +//! When encoding, the set of characters that can (and should, for readability) be left alone +//! depends on the context. +//! The `?` question mark mentioned above is not a separator when used literally +//! inside of a query string, and therefore does not need to be encoded. +//! The [`AsciiSet`] parameter of [`percent_encode`] and [`utf8_percent_encode`] +//! lets callers configure this. +//! +//! This crate deliberately does not provide many different sets. +//! Users should consider in what context the encoded string will be used, +//! read relevant specifications, and define their own set. +//! This is done by using the `add` method of an existing set. +//! +//! # Examples +//! +//! ``` +//! use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; +//! +//! /// https://url.spec.whatwg.org/#fragment-percent-encode-set +//! const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`'); +//! +//! assert_eq!(utf8_percent_encode("foo ", FRAGMENT).to_string(), "foo%20%3Cbar%3E"); +//! ``` + +#![no_std] +#[cfg(feature = "alloc")] +extern crate alloc; + +#[cfg(feature = "alloc")] +use alloc::{ + borrow::{Cow, ToOwned}, + string::String, + vec::Vec, +}; +use core::{fmt, mem, slice, str}; + +/// Represents a set of characters or bytes in the ASCII range. +/// +/// This is used in [`percent_encode`] and [`utf8_percent_encode`]. +/// This is similar to [percent-encode sets](https://url.spec.whatwg.org/#percent-encoded-bytes). +/// +/// Use the `add` method of an existing set to define a new set. For example: +/// +/// ``` +/// use percent_encoding::{AsciiSet, CONTROLS}; +/// +/// /// https://url.spec.whatwg.org/#fragment-percent-encode-set +/// const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`'); +/// ``` +pub struct AsciiSet { + mask: [Chunk; ASCII_RANGE_LEN / BITS_PER_CHUNK], +} + +type Chunk = u32; + +const ASCII_RANGE_LEN: usize = 0x80; + +const BITS_PER_CHUNK: usize = 8 * mem::size_of::(); + +impl AsciiSet { + /// Called with UTF-8 bytes rather than code points. + /// Not used for non-ASCII bytes. + const fn contains(&self, byte: u8) -> bool { + let chunk = self.mask[byte as usize / BITS_PER_CHUNK]; + let mask = 1 << (byte as usize % BITS_PER_CHUNK); + (chunk & mask) != 0 + } + + fn should_percent_encode(&self, byte: u8) -> bool { + !byte.is_ascii() || self.contains(byte) + } + + pub const fn add(&self, byte: u8) -> Self { + let mut mask = self.mask; + mask[byte as usize / BITS_PER_CHUNK] |= 1 << (byte as usize % BITS_PER_CHUNK); + AsciiSet { mask } + } + + pub const fn remove(&self, byte: u8) -> Self { + let mut mask = self.mask; + mask[byte as usize / BITS_PER_CHUNK] &= !(1 << (byte as usize % BITS_PER_CHUNK)); + AsciiSet { mask } + } +} + +/// The set of 0x00 to 0x1F (C0 controls), and 0x7F (DEL). +/// +/// Note that this includes the newline and tab characters, but not the space 0x20. +/// +/// +pub const CONTROLS: &AsciiSet = &AsciiSet { + mask: [ + !0_u32, // C0: 0x00 to 0x1F (32 bits set) + 0, + 0, + 1 << (0x7F_u32 % 32), // DEL: 0x7F (one bit set) + ], +}; + +macro_rules! static_assert { + ($( $bool: expr, )+) => { + fn _static_assert() { + $( + let _ = mem::transmute::<[u8; $bool as usize], u8>; + )+ + } + } +} + +static_assert! { + CONTROLS.contains(0x00), + CONTROLS.contains(0x1F), + !CONTROLS.contains(0x20), + !CONTROLS.contains(0x7E), + CONTROLS.contains(0x7F), +} + +/// Everything that is not an ASCII letter or digit. +/// +/// This is probably more eager than necessary in any context. +pub const NON_ALPHANUMERIC: &AsciiSet = &CONTROLS + .add(b' ') + .add(b'!') + .add(b'"') + .add(b'#') + .add(b'$') + .add(b'%') + .add(b'&') + .add(b'\'') + .add(b'(') + .add(b')') + .add(b'*') + .add(b'+') + .add(b',') + .add(b'-') + .add(b'.') + .add(b'/') + .add(b':') + .add(b';') + .add(b'<') + .add(b'=') + .add(b'>') + .add(b'?') + .add(b'@') + .add(b'[') + .add(b'\\') + .add(b']') + .add(b'^') + .add(b'_') + .add(b'`') + .add(b'{') + .add(b'|') + .add(b'}') + .add(b'~'); + +/// Return the percent-encoding of the given byte. +/// +/// This is unconditional, unlike `percent_encode()` which has an `AsciiSet` parameter. +/// +/// # Examples +/// +/// ``` +/// use percent_encoding::percent_encode_byte; +/// +/// assert_eq!("foo bar".bytes().map(percent_encode_byte).collect::(), +/// "%66%6F%6F%20%62%61%72"); +/// ``` +pub fn percent_encode_byte(byte: u8) -> &'static str { + let index = usize::from(byte) * 3; + &"\ + %00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F\ + %10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F\ + %20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F\ + %30%31%32%33%34%35%36%37%38%39%3A%3B%3C%3D%3E%3F\ + %40%41%42%43%44%45%46%47%48%49%4A%4B%4C%4D%4E%4F\ + %50%51%52%53%54%55%56%57%58%59%5A%5B%5C%5D%5E%5F\ + %60%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F\ + %70%71%72%73%74%75%76%77%78%79%7A%7B%7C%7D%7E%7F\ + %80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F\ + %90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F\ + %A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF\ + %B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF\ + %C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF\ + %D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF\ + %E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF\ + %F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF\ + "[index..index + 3] +} + +/// Percent-encode the given bytes with the given set. +/// +/// Non-ASCII bytes and bytes in `ascii_set` are encoded. +/// +/// The return type: +/// +/// * Implements `Iterator` and therefore has a `.collect::()` method, +/// * Implements `Display` and therefore has a `.to_string()` method, +/// * Implements `Into>` borrowing `input` when none of its bytes are encoded. +/// +/// # Examples +/// +/// ``` +/// use percent_encoding::{percent_encode, NON_ALPHANUMERIC}; +/// +/// assert_eq!(percent_encode(b"foo bar?", NON_ALPHANUMERIC).to_string(), "foo%20bar%3F"); +/// ``` +#[inline] +pub fn percent_encode<'a>(input: &'a [u8], ascii_set: &'static AsciiSet) -> PercentEncode<'a> { + PercentEncode { + bytes: input, + ascii_set, + } +} + +/// Percent-encode the UTF-8 encoding of the given string. +/// +/// See [`percent_encode`] regarding the return type. +/// +/// # Examples +/// +/// ``` +/// use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; +/// +/// assert_eq!(utf8_percent_encode("foo bar?", NON_ALPHANUMERIC).to_string(), "foo%20bar%3F"); +/// ``` +#[inline] +pub fn utf8_percent_encode<'a>(input: &'a str, ascii_set: &'static AsciiSet) -> PercentEncode<'a> { + percent_encode(input.as_bytes(), ascii_set) +} + +/// The return type of [`percent_encode`] and [`utf8_percent_encode`]. +#[derive(Clone)] +pub struct PercentEncode<'a> { + bytes: &'a [u8], + ascii_set: &'static AsciiSet, +} + +impl<'a> Iterator for PercentEncode<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option<&'a str> { + if let Some((&first_byte, remaining)) = self.bytes.split_first() { + if self.ascii_set.should_percent_encode(first_byte) { + self.bytes = remaining; + Some(percent_encode_byte(first_byte)) + } else { + // The unsafe blocks here are appropriate because the bytes are + // confirmed as a subset of UTF-8 in should_percent_encode. + for (i, &byte) in remaining.iter().enumerate() { + if self.ascii_set.should_percent_encode(byte) { + // 1 for first_byte + i for previous iterations of this loop + let (unchanged_slice, remaining) = self.bytes.split_at(1 + i); + self.bytes = remaining; + return Some(unsafe { str::from_utf8_unchecked(unchanged_slice) }); + } + } + let unchanged_slice = self.bytes; + self.bytes = &[][..]; + Some(unsafe { str::from_utf8_unchecked(unchanged_slice) }) + } + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.bytes.is_empty() { + (0, Some(0)) + } else { + (1, Some(self.bytes.len())) + } + } +} + +impl<'a> fmt::Display for PercentEncode<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + for c in (*self).clone() { + formatter.write_str(c)? + } + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl<'a> From> for Cow<'a, str> { + fn from(mut iter: PercentEncode<'a>) -> Self { + match iter.next() { + None => "".into(), + Some(first) => match iter.next() { + None => first.into(), + Some(second) => { + let mut string = first.to_owned(); + string.push_str(second); + string.extend(iter); + string.into() + } + }, + } + } +} + +/// Percent-decode the given string. +/// +/// +/// +/// See [`percent_decode`] regarding the return type. +#[inline] +pub fn percent_decode_str(input: &str) -> PercentDecode<'_> { + percent_decode(input.as_bytes()) +} + +/// Percent-decode the given bytes. +/// +/// +/// +/// Any sequence of `%` followed by two hexadecimal digits is decoded. +/// The return type: +/// +/// * Implements `Into>` borrowing `input` when it contains no percent-encoded sequence, +/// * Implements `Iterator` and therefore has a `.collect::>()` method, +/// * Has `decode_utf8()` and `decode_utf8_lossy()` methods. +/// +/// # Examples +/// +/// ``` +/// use percent_encoding::percent_decode; +/// +/// assert_eq!(percent_decode(b"foo%20bar%3f").decode_utf8().unwrap(), "foo bar?"); +/// ``` +#[inline] +pub fn percent_decode(input: &[u8]) -> PercentDecode<'_> { + PercentDecode { + bytes: input.iter(), + } +} + +/// The return type of [`percent_decode`]. +#[derive(Clone, Debug)] +pub struct PercentDecode<'a> { + bytes: slice::Iter<'a, u8>, +} + +fn after_percent_sign(iter: &mut slice::Iter<'_, u8>) -> Option { + let mut cloned_iter = iter.clone(); + let h = char::from(*cloned_iter.next()?).to_digit(16)?; + let l = char::from(*cloned_iter.next()?).to_digit(16)?; + *iter = cloned_iter; + Some(h as u8 * 0x10 + l as u8) +} + +impl<'a> Iterator for PercentDecode<'a> { + type Item = u8; + + fn next(&mut self) -> Option { + self.bytes.next().map(|&byte| { + if byte == b'%' { + after_percent_sign(&mut self.bytes).unwrap_or(byte) + } else { + byte + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + let bytes = self.bytes.len(); + ((bytes + 2) / 3, Some(bytes)) + } +} + +#[cfg(feature = "alloc")] +impl<'a> From> for Cow<'a, [u8]> { + fn from(iter: PercentDecode<'a>) -> Self { + match iter.if_any() { + Some(vec) => Cow::Owned(vec), + None => Cow::Borrowed(iter.bytes.as_slice()), + } + } +} + +impl<'a> PercentDecode<'a> { + /// If the percent-decoding is different from the input, return it as a new bytes vector. + #[cfg(feature = "alloc")] + fn if_any(&self) -> Option> { + let mut bytes_iter = self.bytes.clone(); + while bytes_iter.any(|&b| b == b'%') { + if let Some(decoded_byte) = after_percent_sign(&mut bytes_iter) { + let initial_bytes = self.bytes.as_slice(); + let unchanged_bytes_len = initial_bytes.len() - bytes_iter.len() - 3; + let mut decoded = initial_bytes[..unchanged_bytes_len].to_owned(); + decoded.push(decoded_byte); + decoded.extend(PercentDecode { bytes: bytes_iter }); + return Some(decoded); + } + } + // Nothing to decode + None + } + + /// Decode the result of percent-decoding as UTF-8. + /// + /// This is return `Err` when the percent-decoded bytes are not well-formed in UTF-8. + #[cfg(feature = "alloc")] + pub fn decode_utf8(self) -> Result, str::Utf8Error> { + match self.clone().into() { + Cow::Borrowed(bytes) => match str::from_utf8(bytes) { + Ok(s) => Ok(s.into()), + Err(e) => Err(e), + }, + Cow::Owned(bytes) => match String::from_utf8(bytes) { + Ok(s) => Ok(s.into()), + Err(e) => Err(e.utf8_error()), + }, + } + } + + /// Decode the result of percent-decoding as UTF-8, lossily. + /// + /// Invalid UTF-8 percent-encoded byte sequences will be replaced � U+FFFD, + /// the replacement character. + #[cfg(feature = "alloc")] + pub fn decode_utf8_lossy(self) -> Cow<'a, str> { + decode_utf8_lossy(self.clone().into()) + } +} + +#[cfg(feature = "alloc")] +fn decode_utf8_lossy(input: Cow<'_, [u8]>) -> Cow<'_, str> { + // Note: This function is duplicated in `form_urlencoded/src/query_encoding.rs`. + match input { + Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes), + Cow::Owned(bytes) => { + match String::from_utf8_lossy(&bytes) { + Cow::Borrowed(utf8) => { + // If from_utf8_lossy returns a Cow::Borrowed, then we can + // be sure our original bytes were valid UTF-8. This is because + // if the bytes were invalid UTF-8 from_utf8_lossy would have + // to allocate a new owned string to back the Cow so it could + // replace invalid bytes with a placeholder. + + // First we do a debug_assert to confirm our description above. + let raw_utf8: *const [u8]; + raw_utf8 = utf8.as_bytes(); + debug_assert!(raw_utf8 == &*bytes as *const [u8]); + + // Given we know the original input bytes are valid UTF-8, + // and we have ownership of those bytes, we re-use them and + // return a Cow::Owned here. + Cow::Owned(unsafe { String::from_utf8_unchecked(bytes) }) + } + Cow::Owned(s) => Cow::Owned(s), + } + } + } +} From 9c14d828ba395be4c60f41ec93c30fdee1394d65 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 27 Nov 2021 01:33:07 +0800 Subject: [PATCH 09/13] Reduce the ICE --- src/test/rustdoc-ui/scrape-examples-ice.rs | 470 +-------------------- 1 file changed, 1 insertion(+), 469 deletions(-) diff --git a/src/test/rustdoc-ui/scrape-examples-ice.rs b/src/test/rustdoc-ui/scrape-examples-ice.rs index df22237643402..a6138add5222c 100644 --- a/src/test/rustdoc-ui/scrape-examples-ice.rs +++ b/src/test/rustdoc-ui/scrape-examples-ice.rs @@ -1,472 +1,4 @@ // compile-flags: -Z unstable-options --scrape-examples-output-path t.calls --scrape-examples-target-crate foobar // check-pass - -// Copyright 2013-2016 The rust-url developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! URLs use special characters to indicate the parts of the request. -//! For example, a `?` question mark marks the end of a path and the start of a query string. -//! In order for that character to exist inside a path, it needs to be encoded differently. -//! -//! Percent encoding replaces reserved characters with the `%` escape character -//! followed by a byte value as two hexadecimal digits. -//! For example, an ASCII space is replaced with `%20`. -//! -//! When encoding, the set of characters that can (and should, for readability) be left alone -//! depends on the context. -//! The `?` question mark mentioned above is not a separator when used literally -//! inside of a query string, and therefore does not need to be encoded. -//! The [`AsciiSet`] parameter of [`percent_encode`] and [`utf8_percent_encode`] -//! lets callers configure this. -//! -//! This crate deliberately does not provide many different sets. -//! Users should consider in what context the encoded string will be used, -//! read relevant specifications, and define their own set. -//! This is done by using the `add` method of an existing set. -//! -//! # Examples -//! -//! ``` -//! use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; -//! -//! /// https://url.spec.whatwg.org/#fragment-percent-encode-set -//! const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`'); -//! -//! assert_eq!(utf8_percent_encode("foo ", FRAGMENT).to_string(), "foo%20%3Cbar%3E"); -//! ``` - #![no_std] -#[cfg(feature = "alloc")] -extern crate alloc; - -#[cfg(feature = "alloc")] -use alloc::{ - borrow::{Cow, ToOwned}, - string::String, - vec::Vec, -}; -use core::{fmt, mem, slice, str}; - -/// Represents a set of characters or bytes in the ASCII range. -/// -/// This is used in [`percent_encode`] and [`utf8_percent_encode`]. -/// This is similar to [percent-encode sets](https://url.spec.whatwg.org/#percent-encoded-bytes). -/// -/// Use the `add` method of an existing set to define a new set. For example: -/// -/// ``` -/// use percent_encoding::{AsciiSet, CONTROLS}; -/// -/// /// https://url.spec.whatwg.org/#fragment-percent-encode-set -/// const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`'); -/// ``` -pub struct AsciiSet { - mask: [Chunk; ASCII_RANGE_LEN / BITS_PER_CHUNK], -} - -type Chunk = u32; - -const ASCII_RANGE_LEN: usize = 0x80; - -const BITS_PER_CHUNK: usize = 8 * mem::size_of::(); - -impl AsciiSet { - /// Called with UTF-8 bytes rather than code points. - /// Not used for non-ASCII bytes. - const fn contains(&self, byte: u8) -> bool { - let chunk = self.mask[byte as usize / BITS_PER_CHUNK]; - let mask = 1 << (byte as usize % BITS_PER_CHUNK); - (chunk & mask) != 0 - } - - fn should_percent_encode(&self, byte: u8) -> bool { - !byte.is_ascii() || self.contains(byte) - } - - pub const fn add(&self, byte: u8) -> Self { - let mut mask = self.mask; - mask[byte as usize / BITS_PER_CHUNK] |= 1 << (byte as usize % BITS_PER_CHUNK); - AsciiSet { mask } - } - - pub const fn remove(&self, byte: u8) -> Self { - let mut mask = self.mask; - mask[byte as usize / BITS_PER_CHUNK] &= !(1 << (byte as usize % BITS_PER_CHUNK)); - AsciiSet { mask } - } -} - -/// The set of 0x00 to 0x1F (C0 controls), and 0x7F (DEL). -/// -/// Note that this includes the newline and tab characters, but not the space 0x20. -/// -/// -pub const CONTROLS: &AsciiSet = &AsciiSet { - mask: [ - !0_u32, // C0: 0x00 to 0x1F (32 bits set) - 0, - 0, - 1 << (0x7F_u32 % 32), // DEL: 0x7F (one bit set) - ], -}; - -macro_rules! static_assert { - ($( $bool: expr, )+) => { - fn _static_assert() { - $( - let _ = mem::transmute::<[u8; $bool as usize], u8>; - )+ - } - } -} - -static_assert! { - CONTROLS.contains(0x00), - CONTROLS.contains(0x1F), - !CONTROLS.contains(0x20), - !CONTROLS.contains(0x7E), - CONTROLS.contains(0x7F), -} - -/// Everything that is not an ASCII letter or digit. -/// -/// This is probably more eager than necessary in any context. -pub const NON_ALPHANUMERIC: &AsciiSet = &CONTROLS - .add(b' ') - .add(b'!') - .add(b'"') - .add(b'#') - .add(b'$') - .add(b'%') - .add(b'&') - .add(b'\'') - .add(b'(') - .add(b')') - .add(b'*') - .add(b'+') - .add(b',') - .add(b'-') - .add(b'.') - .add(b'/') - .add(b':') - .add(b';') - .add(b'<') - .add(b'=') - .add(b'>') - .add(b'?') - .add(b'@') - .add(b'[') - .add(b'\\') - .add(b']') - .add(b'^') - .add(b'_') - .add(b'`') - .add(b'{') - .add(b'|') - .add(b'}') - .add(b'~'); - -/// Return the percent-encoding of the given byte. -/// -/// This is unconditional, unlike `percent_encode()` which has an `AsciiSet` parameter. -/// -/// # Examples -/// -/// ``` -/// use percent_encoding::percent_encode_byte; -/// -/// assert_eq!("foo bar".bytes().map(percent_encode_byte).collect::(), -/// "%66%6F%6F%20%62%61%72"); -/// ``` -pub fn percent_encode_byte(byte: u8) -> &'static str { - let index = usize::from(byte) * 3; - &"\ - %00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F\ - %10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F\ - %20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F\ - %30%31%32%33%34%35%36%37%38%39%3A%3B%3C%3D%3E%3F\ - %40%41%42%43%44%45%46%47%48%49%4A%4B%4C%4D%4E%4F\ - %50%51%52%53%54%55%56%57%58%59%5A%5B%5C%5D%5E%5F\ - %60%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F\ - %70%71%72%73%74%75%76%77%78%79%7A%7B%7C%7D%7E%7F\ - %80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F\ - %90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F\ - %A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF\ - %B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF\ - %C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF\ - %D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF\ - %E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF\ - %F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF\ - "[index..index + 3] -} - -/// Percent-encode the given bytes with the given set. -/// -/// Non-ASCII bytes and bytes in `ascii_set` are encoded. -/// -/// The return type: -/// -/// * Implements `Iterator` and therefore has a `.collect::()` method, -/// * Implements `Display` and therefore has a `.to_string()` method, -/// * Implements `Into>` borrowing `input` when none of its bytes are encoded. -/// -/// # Examples -/// -/// ``` -/// use percent_encoding::{percent_encode, NON_ALPHANUMERIC}; -/// -/// assert_eq!(percent_encode(b"foo bar?", NON_ALPHANUMERIC).to_string(), "foo%20bar%3F"); -/// ``` -#[inline] -pub fn percent_encode<'a>(input: &'a [u8], ascii_set: &'static AsciiSet) -> PercentEncode<'a> { - PercentEncode { - bytes: input, - ascii_set, - } -} - -/// Percent-encode the UTF-8 encoding of the given string. -/// -/// See [`percent_encode`] regarding the return type. -/// -/// # Examples -/// -/// ``` -/// use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; -/// -/// assert_eq!(utf8_percent_encode("foo bar?", NON_ALPHANUMERIC).to_string(), "foo%20bar%3F"); -/// ``` -#[inline] -pub fn utf8_percent_encode<'a>(input: &'a str, ascii_set: &'static AsciiSet) -> PercentEncode<'a> { - percent_encode(input.as_bytes(), ascii_set) -} - -/// The return type of [`percent_encode`] and [`utf8_percent_encode`]. -#[derive(Clone)] -pub struct PercentEncode<'a> { - bytes: &'a [u8], - ascii_set: &'static AsciiSet, -} - -impl<'a> Iterator for PercentEncode<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option<&'a str> { - if let Some((&first_byte, remaining)) = self.bytes.split_first() { - if self.ascii_set.should_percent_encode(first_byte) { - self.bytes = remaining; - Some(percent_encode_byte(first_byte)) - } else { - // The unsafe blocks here are appropriate because the bytes are - // confirmed as a subset of UTF-8 in should_percent_encode. - for (i, &byte) in remaining.iter().enumerate() { - if self.ascii_set.should_percent_encode(byte) { - // 1 for first_byte + i for previous iterations of this loop - let (unchanged_slice, remaining) = self.bytes.split_at(1 + i); - self.bytes = remaining; - return Some(unsafe { str::from_utf8_unchecked(unchanged_slice) }); - } - } - let unchanged_slice = self.bytes; - self.bytes = &[][..]; - Some(unsafe { str::from_utf8_unchecked(unchanged_slice) }) - } - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option) { - if self.bytes.is_empty() { - (0, Some(0)) - } else { - (1, Some(self.bytes.len())) - } - } -} - -impl<'a> fmt::Display for PercentEncode<'a> { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - for c in (*self).clone() { - formatter.write_str(c)? - } - Ok(()) - } -} - -#[cfg(feature = "alloc")] -impl<'a> From> for Cow<'a, str> { - fn from(mut iter: PercentEncode<'a>) -> Self { - match iter.next() { - None => "".into(), - Some(first) => match iter.next() { - None => first.into(), - Some(second) => { - let mut string = first.to_owned(); - string.push_str(second); - string.extend(iter); - string.into() - } - }, - } - } -} - -/// Percent-decode the given string. -/// -/// -/// -/// See [`percent_decode`] regarding the return type. -#[inline] -pub fn percent_decode_str(input: &str) -> PercentDecode<'_> { - percent_decode(input.as_bytes()) -} - -/// Percent-decode the given bytes. -/// -/// -/// -/// Any sequence of `%` followed by two hexadecimal digits is decoded. -/// The return type: -/// -/// * Implements `Into>` borrowing `input` when it contains no percent-encoded sequence, -/// * Implements `Iterator` and therefore has a `.collect::>()` method, -/// * Has `decode_utf8()` and `decode_utf8_lossy()` methods. -/// -/// # Examples -/// -/// ``` -/// use percent_encoding::percent_decode; -/// -/// assert_eq!(percent_decode(b"foo%20bar%3f").decode_utf8().unwrap(), "foo bar?"); -/// ``` -#[inline] -pub fn percent_decode(input: &[u8]) -> PercentDecode<'_> { - PercentDecode { - bytes: input.iter(), - } -} - -/// The return type of [`percent_decode`]. -#[derive(Clone, Debug)] -pub struct PercentDecode<'a> { - bytes: slice::Iter<'a, u8>, -} - -fn after_percent_sign(iter: &mut slice::Iter<'_, u8>) -> Option { - let mut cloned_iter = iter.clone(); - let h = char::from(*cloned_iter.next()?).to_digit(16)?; - let l = char::from(*cloned_iter.next()?).to_digit(16)?; - *iter = cloned_iter; - Some(h as u8 * 0x10 + l as u8) -} - -impl<'a> Iterator for PercentDecode<'a> { - type Item = u8; - - fn next(&mut self) -> Option { - self.bytes.next().map(|&byte| { - if byte == b'%' { - after_percent_sign(&mut self.bytes).unwrap_or(byte) - } else { - byte - } - }) - } - - fn size_hint(&self) -> (usize, Option) { - let bytes = self.bytes.len(); - ((bytes + 2) / 3, Some(bytes)) - } -} - -#[cfg(feature = "alloc")] -impl<'a> From> for Cow<'a, [u8]> { - fn from(iter: PercentDecode<'a>) -> Self { - match iter.if_any() { - Some(vec) => Cow::Owned(vec), - None => Cow::Borrowed(iter.bytes.as_slice()), - } - } -} - -impl<'a> PercentDecode<'a> { - /// If the percent-decoding is different from the input, return it as a new bytes vector. - #[cfg(feature = "alloc")] - fn if_any(&self) -> Option> { - let mut bytes_iter = self.bytes.clone(); - while bytes_iter.any(|&b| b == b'%') { - if let Some(decoded_byte) = after_percent_sign(&mut bytes_iter) { - let initial_bytes = self.bytes.as_slice(); - let unchanged_bytes_len = initial_bytes.len() - bytes_iter.len() - 3; - let mut decoded = initial_bytes[..unchanged_bytes_len].to_owned(); - decoded.push(decoded_byte); - decoded.extend(PercentDecode { bytes: bytes_iter }); - return Some(decoded); - } - } - // Nothing to decode - None - } - - /// Decode the result of percent-decoding as UTF-8. - /// - /// This is return `Err` when the percent-decoded bytes are not well-formed in UTF-8. - #[cfg(feature = "alloc")] - pub fn decode_utf8(self) -> Result, str::Utf8Error> { - match self.clone().into() { - Cow::Borrowed(bytes) => match str::from_utf8(bytes) { - Ok(s) => Ok(s.into()), - Err(e) => Err(e), - }, - Cow::Owned(bytes) => match String::from_utf8(bytes) { - Ok(s) => Ok(s.into()), - Err(e) => Err(e.utf8_error()), - }, - } - } - - /// Decode the result of percent-decoding as UTF-8, lossily. - /// - /// Invalid UTF-8 percent-encoded byte sequences will be replaced � U+FFFD, - /// the replacement character. - #[cfg(feature = "alloc")] - pub fn decode_utf8_lossy(self) -> Cow<'a, str> { - decode_utf8_lossy(self.clone().into()) - } -} - -#[cfg(feature = "alloc")] -fn decode_utf8_lossy(input: Cow<'_, [u8]>) -> Cow<'_, str> { - // Note: This function is duplicated in `form_urlencoded/src/query_encoding.rs`. - match input { - Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes), - Cow::Owned(bytes) => { - match String::from_utf8_lossy(&bytes) { - Cow::Borrowed(utf8) => { - // If from_utf8_lossy returns a Cow::Borrowed, then we can - // be sure our original bytes were valid UTF-8. This is because - // if the bytes were invalid UTF-8 from_utf8_lossy would have - // to allocate a new owned string to back the Cow so it could - // replace invalid bytes with a placeholder. - - // First we do a debug_assert to confirm our description above. - let raw_utf8: *const [u8]; - raw_utf8 = utf8.as_bytes(); - debug_assert!(raw_utf8 == &*bytes as *const [u8]); - - // Given we know the original input bytes are valid UTF-8, - // and we have ownership of those bytes, we re-use them and - // return a Cow::Owned here. - Cow::Owned(unsafe { String::from_utf8_unchecked(bytes) }) - } - Cow::Owned(s) => Cow::Owned(s), - } - } - } -} +use core as _; From 20eda22cbf36b5e9bf9fe28c53790c30fb31c9d3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Nov 2021 15:33:38 +0100 Subject: [PATCH 10/13] Update the first heading indent rule so it is only applied on the first heading of the top doc block --- src/librustdoc/html/static/css/rustdoc.css | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index bfad510695131..479b5210f9e09 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1129,15 +1129,17 @@ h3.variant { margin-top: 3px; } -.docblock > .section-header:first-child { +.top-doc .docblock > .section-header:first-child { margin-left: 15px; - margin-top: 0; } - -.docblock > .section-header:first-child:hover > a:before { +.top-doc .docblock > .section-header:first-child:hover > a:before { left: -10px; } +.docblock > .section-header:first-child { + margin-top: 0; +} + :target > code, :target > .code-header { opacity: 1; } From 23427567b2030dfe1a8e99cd86d2602517118035 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Nov 2021 15:39:04 +0100 Subject: [PATCH 11/13] Update test for anchors and headings position --- src/test/rustdoc-gui/anchors.goml | 46 ++++++++++++++++++++++- src/test/rustdoc-gui/src/test_docs/lib.rs | 2 + 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/test/rustdoc-gui/anchors.goml b/src/test/rustdoc-gui/anchors.goml index 5f809f9aa4ed4..8910cbbbb256c 100644 --- a/src/test/rustdoc-gui/anchors.goml +++ b/src/test/rustdoc-gui/anchors.goml @@ -1,4 +1,4 @@ -// This test is to ensure that the anchors (`§`) have the expected color. +// This test is to ensure that the anchors (`§`) have the expected color and position. goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html show-text: true @@ -28,3 +28,47 @@ assert-css: ("h2#implementations a.anchor", {"color": "rgb(0, 0, 0)"}) // Same thing with the impl block title. move-cursor-to: "#impl" assert-css: ("#impl a.anchor", {"color": "rgb(0, 0, 0)"}) + +// Now we check the positions: only the first heading of the top doc comment should +// have a different position. +move-cursor-to: ".top-doc .docblock .section-header:first-child" +assert-css: ( + ".top-doc .docblock .section-header:first-child > a::before", + {"left": "-10px", "padding-right": "10px"} +) +// We also check that the heading itself has a different indent. +assert-css: (".top-doc .docblock .section-header:first-child", {"margin-left": "15px"}) + +move-cursor-to: ".top-doc .docblock .section-header:not(:first-child)" +assert-css: ( + ".top-doc .docblock .section-header:not(:first-child) > a::before", + {"left": "-25px", "padding-right": "10px"} +) +assert-css: (".top-doc .docblock .section-header:not(:first-child)", {"margin-left": "0px"}) + +// Now let's check some other docblock headings... +// First the impl block docs. +move-cursor-to: "#title-for-struct-impl-doc" +assert-css: ( + "#title-for-struct-impl-doc > a::before", + {"left": "-25px", "padding-right": "10px"} +) +assert-css: ("#title-for-struct-impl-doc", {"margin-left": "0px"}) +// Now a method docs. +move-cursor-to: "#title-for-struct-impl-item-doc" +assert-css: ( + "#title-for-struct-impl-item-doc > a::before", + {"left": "-25px", "padding-right": "10px"} +) +assert-css: ("#title-for-struct-impl-item-doc", {"margin-left": "0px"}) + +// Finally, we want to ensure that if the first element of the doc block isn't a heading, +// if there is a heading afterwards, it won't have the indent. +goto: file://|DOC_PATH|/test_docs/enum.WhoLetTheDogOut.html + +move-cursor-to: ".top-doc .docblock .section-header" +assert-css: ( + ".top-doc .docblock .section-header > a::before", + {"left": "-25px", "padding-right": "10px"} +) +assert-css: (".top-doc .docblock .section-header", {"margin-left": "0px"}) diff --git a/src/test/rustdoc-gui/src/test_docs/lib.rs b/src/test/rustdoc-gui/src/test_docs/lib.rs index 458bcc4780c6c..9b37703dded4b 100644 --- a/src/test/rustdoc-gui/src/test_docs/lib.rs +++ b/src/test/rustdoc-gui/src/test_docs/lib.rs @@ -47,6 +47,8 @@ impl AsRef for Foo { } /// Just a normal enum. +/// +/// # title! #[doc(alias = "ThisIsAnAlias")] pub enum WhoLetTheDogOut { /// Woof! From 79c718f1d5916318b5d55e6c1391a438cae1f763 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Wed, 24 Nov 2021 12:29:58 -0800 Subject: [PATCH 12/13] Rename `Type::ResolvedPath` to `Type::Path` At last! The new name is shorter, simpler, and consistent with `hir::Ty`. --- src/librustdoc/clean/mod.rs | 4 ++-- src/librustdoc/clean/types.rs | 11 ++++++----- src/librustdoc/clean/utils.rs | 4 ++-- src/librustdoc/formats/cache.rs | 10 ++++------ src/librustdoc/html/format.rs | 5 ++--- src/librustdoc/html/render/cache.rs | 4 ++-- src/librustdoc/html/render/mod.rs | 4 ++-- src/librustdoc/html/render/print_item.rs | 25 ++++++++++++------------ src/librustdoc/json/conversions.rs | 8 ++++---- 9 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index e8c55d10901e8..4e1dabd05bb48 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1405,12 +1405,12 @@ impl<'tcx> Clean for Ty<'tcx> { }; inline::record_extern_fqn(cx, did, kind); let path = external_path(cx, did, false, vec![], substs); - Type::ResolvedPath { path } + Type::Path { path } } ty::Foreign(did) => { inline::record_extern_fqn(cx, did, ItemType::ForeignType); let path = external_path(cx, did, false, vec![], InternalSubsts::empty()); - Type::ResolvedPath { path } + Type::Path { path } } ty::Dynamic(obj, ref reg) => { // HACK: pick the first `did` as the `did` of the trait object. Someone diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index b5192d2c08257..37acf68defd25 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1421,8 +1421,9 @@ crate struct PolyTrait { crate enum Type { /// A named type, which could be a trait. /// - /// This is mostly Rustdoc's version of [`hir::Path`]. It has to be different because Rustdoc's [`PathSegment`] can contain cleaned generics. - ResolvedPath { path: Path }, + /// This is mostly Rustdoc's version of [`hir::Path`]. + /// It has to be different because Rustdoc's [`PathSegment`] can contain cleaned generics. + Path { path: Path }, /// A `dyn Trait` object: `dyn for<'a> Trait<'a> + Send + 'static` DynTrait(Vec, Option), /// A type parameter. @@ -1488,7 +1489,7 @@ impl Type { /// Checks if this is a `T::Name` path for an associated type. crate fn is_assoc_ty(&self) -> bool { match self { - Type::ResolvedPath { path, .. } => path.is_assoc_ty(), + Type::Path { path, .. } => path.is_assoc_ty(), _ => false, } } @@ -1502,7 +1503,7 @@ impl Type { crate fn generics(&self) -> Option> { match self { - Type::ResolvedPath { path, .. } => path.generics(), + Type::Path { path, .. } => path.generics(), _ => None, } } @@ -1525,7 +1526,7 @@ impl Type { fn inner_def_id(&self, cache: Option<&Cache>) -> Option { let t: PrimitiveType = match *self { - Type::ResolvedPath { ref path } => return Some(path.def_id()), + Type::Path { ref path } => return Some(path.def_id()), DynTrait(ref bounds, _) => return Some(bounds[0].trait_.def_id()), Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 40fa5b8a33e1b..38f53d7e0b286 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -186,7 +186,7 @@ crate fn build_deref_target_impls(cx: &mut DocContext<'_>, items: &[Item], ret: for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) { inline::build_impl(cx, None, did, None, ret); } - } else if let Type::ResolvedPath { path } = target { + } else if let Type::Path { path } = target { let did = path.def_id(); if !did.is_local() { inline::build_impls(cx, None, did, None, ret); @@ -361,7 +361,7 @@ crate fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => Generic(path.segments[0].name), _ => { let _ = register_res(cx, path.res); - Type::ResolvedPath { path } + Type::Path { path } } } } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 06fcdd0521287..d3831450e1d87 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -27,7 +27,7 @@ use crate::html::render::IndexItem; #[derive(Default)] crate struct Cache { /// Maps a type ID to all known implementations for that type. This is only - /// recognized for intra-crate `ResolvedPath` types, and is used to print + /// recognized for intra-crate [`clean::Type::Path`]s, and is used to print /// out extra documentation on the page of an enum/struct. /// /// The values of the map are a list of implementations and documentation @@ -401,7 +401,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { clean::ImplItem(ref i) => { self.cache.parent_is_trait_impl = i.trait_.is_some(); match i.for_ { - clean::Type::ResolvedPath { ref path } => { + clean::Type::Path { ref path } => { self.cache.parent_stack.push(path.def_id()); true } @@ -436,10 +436,8 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { // Note: matching twice to restrict the lifetime of the `i` borrow. let mut dids = FxHashSet::default(); match i.for_ { - clean::Type::ResolvedPath { ref path } - | clean::BorrowedRef { - type_: box clean::Type::ResolvedPath { ref path }, .. - } => { + clean::Type::Path { ref path } + | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } => { dids.insert(path.def_id()); } clean::DynTrait(ref bounds, _) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index d3603d17925da..34742fac0e4b7 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -607,8 +607,7 @@ crate fn href_relative_parts<'a>(fqp: &'a [String], relative_to_fqp: &'a [String } } -/// Used when rendering a `ResolvedPath` structure. This invokes the `path` -/// rendering function with the necessary arguments for linking to a local path. +/// Used to render a [`clean::Path`]. fn resolved_path<'cx>( w: &mut fmt::Formatter<'_>, did: DefId, @@ -762,7 +761,7 @@ fn fmt_type<'cx>( match *t { clean::Generic(name) => write!(f, "{}", name), - clean::Type::ResolvedPath { ref path } => { + clean::Type::Path { ref path } => { // Paths like `T::Output` and `Self::Output` should be rendered with all segments. let did = path.def_id(); resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx) diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index ffa5ec8a3ee32..d12667c9e5c77 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -218,7 +218,7 @@ fn get_index_type(clean_type: &clean::Type, generics: Vec) -> Rend fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option { match *clean_type { - clean::Type::ResolvedPath { ref path, .. } => { + clean::Type::Path { ref path, .. } => { let path_segment = path.segments.last().unwrap(); Some(path_segment.name) } @@ -371,7 +371,7 @@ crate fn get_real_types<'tcx>( let mut ty_generics = Vec::new(); for bound in bound.get_bounds().unwrap_or(&[]) { if let Some(path) = bound.get_trait_path() { - let ty = Type::ResolvedPath { path }; + let ty = Type::Path { path }; get_real_types(generics, &ty, tcx, recurse + 1, &mut ty_generics, cache); } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 42a2defa757a4..20a200f0484bd 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1227,7 +1227,7 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { (mutability == Mutability::Mut, false, false) } - SelfTy::SelfExplicit(clean::Type::ResolvedPath { path }) => { + SelfTy::SelfExplicit(clean::Type::Path { path }) => { (false, Some(path.def_id()) == tcx.lang_items().owned_box(), false) } SelfTy::SelfValue => (false, false, true), @@ -2520,7 +2520,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec { } match ty { - clean::Type::ResolvedPath { path } => process_path(path.def_id()), + clean::Type::Path { path } => process_path(path.def_id()), clean::Type::Tuple(tys) => { work.extend(tys.into_iter()); } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index f8a16fb9b713d..d3738cfa3e781 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -727,10 +727,10 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra let mut implementor_dups: FxHashMap = FxHashMap::default(); for implementor in implementors { match implementor.inner_impl().for_ { - clean::Type::ResolvedPath { ref path } - | clean::BorrowedRef { - type_: box clean::Type::ResolvedPath { ref path }, .. - } if !path.is_assoc_ty() => { + clean::Type::Path { ref path } + | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } + if !path.is_assoc_ty() => + { let did = path.def_id(); let &mut (prev_did, ref mut has_duplicates) = implementor_dups.entry(path.last()).or_insert((did, false)); @@ -1452,14 +1452,15 @@ fn render_implementor( ) { // If there's already another implementor that has the same abridged name, use the // full path, for example in `std::iter::ExactSizeIterator` - let use_absolute = - match implementor.inner_impl().for_ { - clean::Type::ResolvedPath { ref path, .. } - | clean::BorrowedRef { - type_: box clean::Type::ResolvedPath { ref path, .. }, .. - } if !path.is_assoc_ty() => implementor_dups[&path.last()].1, - _ => false, - }; + let use_absolute = match implementor.inner_impl().for_ { + clean::Type::Path { ref path, .. } + | clean::BorrowedRef { type_: box clean::Type::Path { ref path, .. }, .. } + if !path.is_assoc_ty() => + { + implementor_dups[&path.last()].1 + } + _ => false, + }; render_impl( w, cx, diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index ed8f7274591d6..7fc295747f41a 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -365,7 +365,7 @@ impl FromWithTcx for GenericBound { match bound { TraitBound(clean::PolyTrait { trait_, generic_params }, modifier) => { // FIXME: should `trait_` be a clean::Path equivalent in JSON? - let trait_ = clean::Type::ResolvedPath { path: trait_ }.into_tcx(tcx); + let trait_ = clean::Type::Path { path: trait_ }.into_tcx(tcx); GenericBound::TraitBound { trait_, generic_params: generic_params.into_iter().map(|x| x.into_tcx(tcx)).collect(), @@ -394,7 +394,7 @@ impl FromWithTcx for Type { }; match ty { - clean::Type::ResolvedPath { path } => Type::ResolvedPath { + clean::Type::Path { path } => Type::ResolvedPath { name: path.whole_name(), id: from_item_id(path.def_id().into()), args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))), @@ -439,7 +439,7 @@ impl FromWithTcx for Type { }, QPath { name, self_type, trait_, .. } => { // FIXME: should `trait_` be a clean::Path equivalent in JSON? - let trait_ = clean::Type::ResolvedPath { path: trait_ }.into_tcx(tcx); + let trait_ = clean::Type::Path { path: trait_ }.into_tcx(tcx); Type::QualifiedPath { name: name.to_string(), self_type: Box::new((*self_type).into_tcx(tcx)), @@ -505,7 +505,7 @@ impl FromWithTcx for Impl { let provided_trait_methods = impl_.provided_trait_methods(tcx); let clean::Impl { unsafety, generics, trait_, for_, items, polarity, kind } = impl_; // FIXME: should `trait_` be a clean::Path equivalent in JSON? - let trait_ = trait_.map(|path| clean::Type::ResolvedPath { path }.into_tcx(tcx)); + let trait_ = trait_.map(|path| clean::Type::Path { path }.into_tcx(tcx)); // FIXME: use something like ImplKind in JSON? let (synthetic, blanket_impl) = match kind { clean::ImplKind::Normal => (false, None), From 7e4bf4bfc6a6048234d62923622636e5751b8c8f Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 26 Nov 2021 13:52:28 -0500 Subject: [PATCH 13/13] Remove `--display-doctest-warnings` This can be replicated in full with other existing features, there's no need to have a separate option for it. This also fixes a bug where `--test-args=--show-output` had no effect, and updates the documentation. --- src/doc/rustdoc/src/documentation-tests.md | 10 +++++ src/doc/rustdoc/src/unstable-features.md | 16 -------- src/librustdoc/config.rs | 6 --- src/librustdoc/doctest.rs | 31 +++++---------- src/librustdoc/doctest/tests.rs | 17 +------- src/librustdoc/markdown.rs | 8 +--- src/test/rustdoc-ui/display-output.rs | 8 +++- src/test/rustdoc-ui/display-output.stdout | 46 +++++++++++++++++++--- 8 files changed, 68 insertions(+), 74 deletions(-) diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index 70900a0bab942..6be53fb4cfed4 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -261,6 +261,16 @@ conversion, so type inference fails because the type is not unique. Please note that you must write the `(())` in one sequence without intermediate whitespace so that `rustdoc` understands you want an implicit `Result`-returning function. +## Showing warnings in doctests + +You can show warnings in doctests by running `rustdoc --test --test-args=--show-output` +(or, if you're using cargo, `cargo test --doc -- --show-output`). +By default, this will still hide `unused` warnings, since so many examples use private functions; +you can add `#![warn(unused)]` to the top of your example if you want to see unused variables or dead code warnings. +You can also use [`#![doc(test(attr(warn(unused))))]`][test-attr] in the crate root to enable warnings globally. + +[test-attr]: ./the-doc-attribute.md#testattr + ## Documenting macros Here’s an example of documenting a macro: diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 6e52127591c76..021040215437e 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -257,22 +257,6 @@ all these files are linked from every page, changing where they are can be cumbe specially cache them. This flag will rename all these files in the output to include the suffix in the filename. For example, `light.css` would become `light-suf.css` with the above command. -### `--display-doctest-warnings`: display warnings when documenting or running documentation tests - -Using this flag looks like this: - -```bash -$ rustdoc src/lib.rs -Z unstable-options --display-doctest-warnings -$ rustdoc --test src/lib.rs -Z unstable-options --display-doctest-warnings -``` - -The intent behind this flag is to allow the user to see warnings that occur within their library or -their documentation tests, which are usually suppressed. However, [due to a -bug][issue-display-warnings], this flag doesn't 100% work as intended. See the linked issue for -details. - -[issue-display-warnings]: https://github.com/rust-lang/rust/issues/41574 - ### `--extern-html-root-url`: control how rustdoc links to non-local crates Using this flag looks like this: diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 493aa56fce6ef..7c23117cce3af 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -136,9 +136,6 @@ crate struct Options { /// /// Be aware: This option can come both from the CLI and from crate attributes! crate manual_passes: Vec, - /// Whether to display warnings during doc generation or while gathering doctests. By default, - /// all non-rustdoc-specific lints are allowed when generating docs. - crate display_doctest_warnings: bool, /// Whether to run the `calculate-doc-coverage` pass, which counts the number of public items /// with and without documentation. crate show_coverage: bool, @@ -197,7 +194,6 @@ impl fmt::Debug for Options { .field("persist_doctests", &self.persist_doctests) .field("default_passes", &self.default_passes) .field("manual_passes", &self.manual_passes) - .field("display_doctest_warnings", &self.display_doctest_warnings) .field("show_coverage", &self.show_coverage) .field("crate_version", &self.crate_version) .field("render_options", &self.render_options) @@ -639,7 +635,6 @@ impl Options { let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); let playground_url = matches.opt_str("playground-url"); let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from); - let display_doctest_warnings = matches.opt_present("display-doctest-warnings"); let sort_modules_alphabetically = !matches.opt_present("sort-modules-by-appearance"); let resource_suffix = matches.opt_str("resource-suffix").unwrap_or_default(); let enable_minification = !matches.opt_present("disable-minification"); @@ -707,7 +702,6 @@ impl Options { test_args, default_passes, manual_passes, - display_doctest_warnings, show_coverage, crate_version, test_run_directory, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index de4a3732e736b..56ccdfae1d8bc 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -38,9 +38,6 @@ use crate::passes::span_of_attrs; crate struct TestOptions { /// Whether to disable the default `extern crate my_crate;` when creating doctests. crate no_crate_inject: bool, - /// Whether to emit compilation warnings when compiling doctests. Setting this will suppress - /// the default `#![allow(unused)]`. - crate display_doctest_warnings: bool, /// Additional crate-level attributes to add to doctests. crate attrs: Vec, } @@ -65,6 +62,8 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { } }); + debug!(?lint_opts); + let crate_types = if options.proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; @@ -72,7 +71,7 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { maybe_sysroot: options.maybe_sysroot.clone(), search_paths: options.libs.clone(), crate_types, - lint_opts: if !options.display_doctest_warnings { lint_opts } else { vec![] }, + lint_opts, lint_cap: Some(options.lint_cap.unwrap_or(lint::Forbid)), cg: options.codegen_options.clone(), externs: options.externs.clone(), @@ -106,7 +105,6 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { }; let test_args = options.test_args.clone(); - let display_doctest_warnings = options.display_doctest_warnings; let nocapture = options.nocapture; let externs = options.externs.clone(); let json_unused_externs = options.json_unused_externs; @@ -118,8 +116,7 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { let collector = global_ctxt.enter(|tcx| { let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); - let mut opts = scrape_test_config(crate_attrs); - opts.display_doctest_warnings |= options.display_doctest_warnings; + let opts = scrape_test_config(crate_attrs); let enable_per_target_ignores = options.enable_per_target_ignores; let mut collector = Collector::new( tcx.crate_name(LOCAL_CRATE), @@ -165,7 +162,7 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { Err(ErrorReported) => return Err(ErrorReported), }; - run_tests(test_args, nocapture, display_doctest_warnings, tests); + run_tests(test_args, nocapture, tests); // Collect and warn about unused externs, but only if we've gotten // reports for each doctest @@ -208,29 +205,19 @@ crate fn run(options: Options) -> Result<(), ErrorReported> { Ok(()) } -crate fn run_tests( - mut test_args: Vec, - nocapture: bool, - display_doctest_warnings: bool, - tests: Vec, -) { +crate fn run_tests(mut test_args: Vec, nocapture: bool, tests: Vec) { test_args.insert(0, "rustdoctest".to_string()); if nocapture { test_args.push("--nocapture".to_string()); } - test::test_main( - &test_args, - tests, - Some(test::Options::new().display_output(display_doctest_warnings)), - ); + test::test_main(&test_args, tests, None); } // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade. fn scrape_test_config(attrs: &[ast::Attribute]) -> TestOptions { use rustc_ast_pretty::pprust; - let mut opts = - TestOptions { no_crate_inject: false, display_doctest_warnings: false, attrs: Vec::new() }; + let mut opts = TestOptions { no_crate_inject: false, attrs: Vec::new() }; let test_attrs: Vec<_> = attrs .iter() @@ -510,7 +497,7 @@ crate fn make_test( let mut prog = String::new(); let mut supports_color = false; - if opts.attrs.is_empty() && !opts.display_doctest_warnings { + if opts.attrs.is_empty() { // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some // lints that are commonly triggered in doctests. The crate-level test attributes are // commonly used to make tests fail in case they trigger warnings, so having this there in diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 1851708096564..099609d0f912e 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -52,8 +52,7 @@ assert_eq!(2+2, 4); fn make_test_no_crate_inject() { // Even if you do use the crate within the test, setting `opts.no_crate_inject` will skip // adding it anyway. - let opts = - TestOptions { no_crate_inject: true, display_doctest_warnings: false, attrs: vec![] }; + let opts = TestOptions { no_crate_inject: true, attrs: vec![] }; let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -215,20 +214,6 @@ assert_eq!(2+2, 4);" assert_eq!((output, len), (expected, 1)); } -#[test] -fn make_test_display_doctest_warnings() { - // If the user is asking to display doctest warnings, suppress the default `allow(unused)`. - let mut opts = TestOptions::default(); - opts.display_doctest_warnings = true; - let input = "assert_eq!(2+2, 4);"; - let expected = "fn main() { -assert_eq!(2+2, 4); -}" - .to_string(); - let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); - assert_eq!((output, len), (expected, 1)); -} - #[test] fn make_test_issues_21299_33731() { let opts = TestOptions::default(); diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 47b24d40edc27..abb4bec5ca133 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -131,7 +131,6 @@ crate fn test(options: Options) -> Result<(), String> { .map_err(|err| format!("{}: {}", options.input.display(), err))?; let mut opts = TestOptions::default(); opts.no_crate_inject = true; - opts.display_doctest_warnings = options.display_doctest_warnings; let mut collector = Collector::new( Symbol::intern(&options.input.display().to_string()), options.clone(), @@ -146,11 +145,6 @@ crate fn test(options: Options) -> Result<(), String> { find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None); - crate::doctest::run_tests( - options.test_args, - options.nocapture, - options.display_doctest_warnings, - collector.tests, - ); + crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); Ok(()) } diff --git a/src/test/rustdoc-ui/display-output.rs b/src/test/rustdoc-ui/display-output.rs index 5e390029dca33..30b32c511b5fa 100644 --- a/src/test/rustdoc-ui/display-output.rs +++ b/src/test/rustdoc-ui/display-output.rs @@ -1,9 +1,15 @@ +// Test that `--show-output` has an effect and `allow(unused)` can be overriden. + // check-pass -// compile-flags:-Zunstable-options --display-doctest-warnings --test +// edition:2018 +// compile-flags:--test --test-args=--show-output // normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" // normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" /// ``` +/// #![warn(unused)] /// let x = 12; +/// +/// fn foo(x: &std::fmt::Display) {} /// ``` pub fn foo() {} diff --git a/src/test/rustdoc-ui/display-output.stdout b/src/test/rustdoc-ui/display-output.stdout index 00467b9359eb2..f76dec1c85008 100644 --- a/src/test/rustdoc-ui/display-output.stdout +++ b/src/test/rustdoc-ui/display-output.stdout @@ -1,24 +1,58 @@ running 1 test -test $DIR/display-output.rs - foo (line 6) ... ok +test $DIR/display-output.rs - foo (line 9) ... ok successes: ----- $DIR/display-output.rs - foo (line 6) stdout ---- +---- $DIR/display-output.rs - foo (line 9) stdout ---- +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/display-output.rs:13:12 + | +LL | fn foo(x: &std::fmt::Display) {} + | ^^^^^^^^^^^^^^^^^ help: use `dyn`: `dyn std::fmt::Display` + | + = note: `#[warn(bare_trait_objects)]` on by default + = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! + = note: for more information, see + warning: unused variable: `x` - --> $DIR/display-output.rs:7:5 + --> $DIR/display-output.rs:11:5 | LL | let x = 12; | ^ help: if this is intentional, prefix it with an underscore: `_x` | - = note: `#[warn(unused_variables)]` on by default +note: the lint level is defined here + --> $DIR/display-output.rs:9:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: unused variable: `x` + --> $DIR/display-output.rs:13:8 + | +LL | fn foo(x: &std::fmt::Display) {} + | ^ help: if this is intentional, prefix it with an underscore: `_x` + +warning: function is never used: `foo` + --> $DIR/display-output.rs:13:4 + | +LL | fn foo(x: &std::fmt::Display) {} + | ^^^ + | +note: the lint level is defined here + --> $DIR/display-output.rs:9:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(dead_code)]` implied by `#[warn(unused)]` -warning: 1 warning emitted +warning: 4 warnings emitted successes: - $DIR/display-output.rs - foo (line 6) + $DIR/display-output.rs - foo (line 9) test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME