From 91fe2d0ed7a8f79a64216c7328b2bea11a3bc9ea Mon Sep 17 00:00:00 2001 From: Roc Yu Date: Wed, 22 Dec 2021 15:37:49 -0500 Subject: [PATCH 01/12] Clean up NestedAttributesExt trait/implementation --- src/librustdoc/clean/types.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index cb4896fbfd23..fc26b82c974f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -879,20 +879,25 @@ impl AttributesExt for [ast::Attribute] { } crate trait NestedAttributesExt { - /// Returns `true` if the attribute list contains a specific `Word` - fn has_word(self, word: Symbol) -> bool; + /// Returns `true` if the attribute list contains a specific `word` + fn has_word(self, word: Symbol) -> bool + where + Self: std::marker::Sized, + { + ::get_word_attr(self, word).is_some() + } + + /// Returns `Some(attr)` if the attribute list contains 'attr' + /// corresponding to a specific `word` fn get_word_attr(self, word: Symbol) -> Option; } -impl + IntoIterator> - NestedAttributesExt for I +impl NestedAttributesExt for I +where + I: IntoIterator, { - fn has_word(self, word: Symbol) -> bool { - self.into_iter().any(|attr| attr.is_word() && attr.has_name(word)) - } - - fn get_word_attr(mut self, word: Symbol) -> Option { - self.find(|attr| attr.is_word() && attr.has_name(word)) + fn get_word_attr(self, word: Symbol) -> Option { + self.into_iter().find(|attr| attr.is_word() && attr.has_name(word)) } } From 1773e8318f0ff7a928867df0d56c9d58b9b906e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alper=20=C3=87ugun?= Date: Mon, 27 Dec 2021 12:28:11 +0100 Subject: [PATCH 02/12] Add another implementation example to Debug trait --- library/core/src/fmt/mod.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 6fc3cd0b7c4a..2d4f477ca018 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -570,11 +570,26 @@ impl Display for Arguments<'_> { /// There are a number of helper methods on the [`Formatter`] struct to help you with manual /// implementations, such as [`debug_struct`]. /// +/// [`debug_struct`]: Formatter::debug_struct +/// +/// For custom cases, it's also possible to implement `Debug` using the [`write!`] macro: +/// ``` +/// # use std::fmt; +/// # struct Point { +/// # x: i32, +/// # y: i32, +/// # } +/// +/// impl fmt::Debug for Point { +/// fn fmt(&self, f: &mut fmt::Formatter <'_>) -> fmt::Result { +/// write!(f, "Point [{} {}]", self.x, self.y) +/// } +/// } +/// ``` +/// /// `Debug` implementations using either `derive` or the debug builder API /// on [`Formatter`] support pretty-printing using the alternate flag: `{:#?}`. /// -/// [`debug_struct`]: Formatter::debug_struct -/// /// Pretty-printing with `#?`: /// /// ``` From 4145877731e9f38a4640d2dfeed7b123d81d061a Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 3 Jan 2022 12:55:42 +0000 Subject: [PATCH 03/12] Explicitly pass `PATH` to the Windows exe resolver --- library/std/src/sys/windows/process.rs | 31 ++++++++++------ library/std/src/sys/windows/process/tests.rs | 39 +++++++++----------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index e84dfbce4a75..5ad570427978 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -268,7 +268,7 @@ impl Command { } else { None }; - let program = resolve_exe(&self.program, child_paths)?; + let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?; let mut cmd_str = make_command_line(program.as_os_str(), &self.args, self.force_quotes_enabled)?; cmd_str.push(0); // add null terminator @@ -362,7 +362,11 @@ impl fmt::Debug for Command { // Therefore this functions first assumes `.exe` was intended. // It falls back to the plain file name if a full path is given and the extension is omitted // or if only a file name is given and it already contains an extension. -fn resolve_exe<'a>(exe_path: &'a OsStr, child_paths: Option<&OsStr>) -> io::Result { +fn resolve_exe<'a>( + exe_path: &'a OsStr, + parent_paths: impl FnOnce() -> Option, + child_paths: Option<&OsStr>, +) -> io::Result { // Early return if there is no filename. if exe_path.is_empty() || path::has_trailing_slash(exe_path) { return Err(io::Error::new_const( @@ -406,7 +410,7 @@ fn resolve_exe<'a>(exe_path: &'a OsStr, child_paths: Option<&OsStr>) -> io::Resu let has_extension = exe_path.bytes().contains(&b'.'); // Search the directories given by `search_paths`. - let result = search_paths(child_paths, |mut path| { + let result = search_paths(parent_paths, child_paths, |mut path| { path.push(&exe_path); if !has_extension { path.set_extension(EXE_EXTENSION); @@ -423,15 +427,20 @@ fn resolve_exe<'a>(exe_path: &'a OsStr, child_paths: Option<&OsStr>) -> io::Resu // Calls `f` for every path that should be used to find an executable. // Returns once `f` returns the path to an executable or all paths have been searched. -fn search_paths(child_paths: Option<&OsStr>, mut f: F) -> Option +fn search_paths( + parent_paths: Paths, + child_paths: Option<&OsStr>, + mut exists: Exists, +) -> Option where - F: FnMut(PathBuf) -> Option, + Paths: FnOnce() -> Option, + Exists: FnMut(PathBuf) -> Option, { // 1. Child paths // This is for consistency with Rust's historic behaviour. if let Some(paths) = child_paths { for path in env::split_paths(paths).filter(|p| !p.as_os_str().is_empty()) { - if let Some(path) = f(path) { + if let Some(path) = exists(path) { return Some(path); } } @@ -440,7 +449,7 @@ where // 2. Application path if let Ok(mut app_path) = env::current_exe() { app_path.pop(); - if let Some(path) = f(app_path) { + if let Some(path) = exists(app_path) { return Some(path); } } @@ -450,7 +459,7 @@ where unsafe { if let Ok(Some(path)) = super::fill_utf16_buf( |buf, size| c::GetSystemDirectoryW(buf, size), - |buf| f(PathBuf::from(OsString::from_wide(buf))), + |buf| exists(PathBuf::from(OsString::from_wide(buf))), ) { return Some(path); } @@ -458,7 +467,7 @@ where { if let Ok(Some(path)) = super::fill_utf16_buf( |buf, size| c::GetWindowsDirectoryW(buf, size), - |buf| f(PathBuf::from(OsString::from_wide(buf))), + |buf| exists(PathBuf::from(OsString::from_wide(buf))), ) { return Some(path); } @@ -466,9 +475,9 @@ where } // 5. Parent paths - if let Some(parent_paths) = env::var_os("PATH") { + if let Some(parent_paths) = parent_paths() { for path in env::split_paths(&parent_paths).filter(|p| !p.as_os_str().is_empty()) { - if let Some(path) = f(path) { + if let Some(path) = exists(path) { return Some(path); } } diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs index 6159a679c0e6..f1221767af30 100644 --- a/library/std/src/sys/windows/process/tests.rs +++ b/library/std/src/sys/windows/process/tests.rs @@ -136,51 +136,46 @@ fn windows_exe_resolver() { use super::resolve_exe; use crate::io; + let env_paths = || env::var_os("PATH"); + // Test a full path, with and without the `exe` extension. let mut current_exe = env::current_exe().unwrap(); - assert!(resolve_exe(current_exe.as_ref(), None).is_ok()); + assert!(resolve_exe(current_exe.as_ref(), env_paths, None).is_ok()); current_exe.set_extension(""); - assert!(resolve_exe(current_exe.as_ref(), None).is_ok()); + assert!(resolve_exe(current_exe.as_ref(), env_paths, None).is_ok()); // Test lone file names. - assert!(resolve_exe(OsStr::new("cmd"), None).is_ok()); - assert!(resolve_exe(OsStr::new("cmd.exe"), None).is_ok()); - assert!(resolve_exe(OsStr::new("cmd.EXE"), None).is_ok()); - assert!(resolve_exe(OsStr::new("fc"), None).is_ok()); + assert!(resolve_exe(OsStr::new("cmd"), env_paths, None).is_ok()); + assert!(resolve_exe(OsStr::new("cmd.exe"), env_paths, None).is_ok()); + assert!(resolve_exe(OsStr::new("cmd.EXE"), env_paths, None).is_ok()); + assert!(resolve_exe(OsStr::new("fc"), env_paths, None).is_ok()); // Invalid file names should return InvalidInput. - assert_eq!(resolve_exe(OsStr::new(""), None).unwrap_err().kind(), io::ErrorKind::InvalidInput); assert_eq!( - resolve_exe(OsStr::new("\0"), None).unwrap_err().kind(), + resolve_exe(OsStr::new(""), env_paths, None).unwrap_err().kind(), + io::ErrorKind::InvalidInput + ); + assert_eq!( + resolve_exe(OsStr::new("\0"), env_paths, None).unwrap_err().kind(), io::ErrorKind::InvalidInput ); // Trailing slash, therefore there's no file name component. assert_eq!( - resolve_exe(OsStr::new(r"C:\Path\to\"), None).unwrap_err().kind(), + resolve_exe(OsStr::new(r"C:\Path\to\"), env_paths, None).unwrap_err().kind(), io::ErrorKind::InvalidInput ); - /* FIXME: fix and re-enable these tests before making changes to the resolver. - /* Some of the following tests may need to be changed if you are deliberately changing the behaviour of `resolve_exe`. */ - let paths = env::var_os("PATH").unwrap(); - env::set_var("PATH", ""); - - assert_eq!(resolve_exe(OsStr::new("rustc"), None).unwrap_err().kind(), io::ErrorKind::NotFound); - - let child_paths = Some(paths.as_os_str()); - assert!(resolve_exe(OsStr::new("rustc"), child_paths).is_ok()); + let empty_paths = || None; // The resolver looks in system directories even when `PATH` is empty. - assert!(resolve_exe(OsStr::new("cmd.exe"), None).is_ok()); + assert!(resolve_exe(OsStr::new("cmd.exe"), empty_paths, None).is_ok()); // The application's directory is also searched. let current_exe = env::current_exe().unwrap(); - assert!(resolve_exe(current_exe.file_name().unwrap().as_ref(), None).is_ok()); - - */ + assert!(resolve_exe(current_exe.file_name().unwrap().as_ref(), empty_paths, None).is_ok()); } From 14a285ca4fe8db2a8b93442d804059fca249a215 Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Thu, 30 Dec 2021 17:07:05 +0000 Subject: [PATCH 04/12] Rustdoc: resolve associated traits for primitive types Fixes #90703 --- .../passes/collect_intra_doc_links.rs | 97 +++++++++++++++---- .../intra-doc/non-path-primitives.rs | 1 - .../intra-doc/non-path-primitives.stderr | 10 +- .../intra-doc/prim-associated-traits.rs | 7 ++ 4 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 src/test/rustdoc/intra-doc/prim-associated-traits.rs diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 26ccdb1c87ec..680a74e5aa63 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -13,7 +13,7 @@ use rustc_hir::def::{ PerNS, }; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; use rustc_resolve::ParentScope; use rustc_session::lint::Lint; @@ -618,6 +618,44 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }) } + /// Convert a PrimitiveType to a Ty, where possible. + /// + /// This is used for resolving trait impls for primitives + fn primitive_type_to_ty(&mut self, prim: PrimitiveType) -> Option> { + use PrimitiveType::*; + let tcx = self.cx.tcx; + + Some(tcx.mk_ty(match prim { + Bool => ty::Bool, + Str => ty::Str, + Char => ty::Char, + Never => ty::Never, + I8 => ty::Int(ty::IntTy::I8), + I16 => ty::Int(ty::IntTy::I16), + I32 => ty::Int(ty::IntTy::I32), + I64 => ty::Int(ty::IntTy::I64), + I128 => ty::Int(ty::IntTy::I128), + Isize => ty::Int(ty::IntTy::Isize), + F32 => ty::Float(ty::FloatTy::F32), + F64 => ty::Float(ty::FloatTy::F64), + U8 => ty::Uint(ty::UintTy::U8), + U16 => ty::Uint(ty::UintTy::U16), + U32 => ty::Uint(ty::UintTy::U32), + U64 => ty::Uint(ty::UintTy::U64), + U128 => ty::Uint(ty::UintTy::U128), + Usize => ty::Uint(ty::UintTy::Usize), + //ty::Tuple(tys) if tys.is_empty() => Res::Primitive(Unit), + //ty::Tuple(_) => Res::Primitive(Tuple), + //ty::Array(..) => Res::Primitive(Array), + //ty::Slice(_) => Res::Primitive(Slice), + //ty::RawPtr(_) => Res::Primitive(RawPointer), + //ty::Ref(..) => Res::Primitive(Reference), + //ty::FnDef(..) => panic!("type alias to a function definition"), + //ty::FnPtr(_) => Res::Primitive(Fn), + _ => return None, + })) + } + /// Returns: /// - None if no associated item was found /// - Some((_, _, Some(_))) if an item was found and should go through a side channel @@ -632,7 +670,25 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let tcx = self.cx.tcx; match root_res { - Res::Primitive(prim) => self.resolve_primitive_associated_item(prim, ns, item_name), + Res::Primitive(prim) => { + self.resolve_primitive_associated_item(prim, ns, item_name).or_else(|| { + let assoc_item = self + .primitive_type_to_ty(prim) + .map(|ty| { + resolve_associated_trait_item(ty, module_id, item_name, ns, self.cx) + }) + .flatten(); + + assoc_item.map(|item| { + let kind = item.kind; + let fragment = UrlFragment::from_assoc_item(item_name, kind, false); + // HACK(jynelson): `clean` expects the type, not the associated item + // but the disambiguator logic expects the associated item. + // Store the kind in a side channel so that only the disambiguator logic looks at it. + (root_res, fragment, Some((kind.as_def_kind(), item.def_id))) + }) + }) + } 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, @@ -666,8 +722,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // To handle that properly resolve() would have to support // something like [`ambi_fn`](::ambi_fn) .or_else(|| { - let item = - resolve_associated_trait_item(did, module_id, item_name, ns, self.cx); + let item = resolve_associated_trait_item( + tcx.type_of(did), + module_id, + item_name, + ns, + self.cx, + ); debug!("got associated item {:?}", item); item }); @@ -767,12 +828,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { /// Given `[std::io::Error::source]`, where `source` is unresolved, this would /// find `std::error::Error::source` and return /// `::source`. -fn resolve_associated_trait_item( - did: DefId, +fn resolve_associated_trait_item<'a>( + ty: Ty<'a>, module: DefId, item_name: Symbol, ns: Namespace, - cx: &mut DocContext<'_>, + cx: &mut DocContext<'a>, ) -> Option { // FIXME: this should also consider blanket impls (`impl X for T`). Unfortunately // `get_auto_trait_and_blanket_impls` is broken because the caching behavior is wrong. In the @@ -780,7 +841,7 @@ fn resolve_associated_trait_item( // Next consider explicit impls: `impl MyTrait for MyType` // Give precedence to inherent impls. - let traits = traits_implemented_by(cx, did, module); + let traits = traits_implemented_by(cx, ty, module); debug!("considering traits {:?}", traits); let mut candidates = traits.iter().filter_map(|&trait_| { cx.tcx.associated_items(trait_).find_by_name_and_namespace( @@ -799,7 +860,11 @@ fn resolve_associated_trait_item( /// /// NOTE: this cannot be a query because more traits could be available when more crates are compiled! /// So it is not stable to serialize cross-crate. -fn traits_implemented_by(cx: &mut DocContext<'_>, type_: DefId, module: DefId) -> FxHashSet { +fn traits_implemented_by<'a>( + cx: &mut DocContext<'a>, + ty: Ty<'a>, + module: DefId, +) -> FxHashSet { let mut resolver = cx.resolver.borrow_mut(); let in_scope_traits = cx.module_trait_cache.entry(module).or_insert_with(|| { resolver.access(|resolver| { @@ -813,7 +878,6 @@ fn traits_implemented_by(cx: &mut DocContext<'_>, type_: DefId, module: DefId) - }); let tcx = cx.tcx; - let ty = tcx.type_of(type_); let iter = in_scope_traits.iter().flat_map(|&trait_| { trace!("considering explicit impl for trait {:?}", trait_); @@ -826,19 +890,10 @@ fn traits_implemented_by(cx: &mut DocContext<'_>, type_: DefId, module: DefId) - "comparing type {} with kind {:?} against type {:?}", impl_type, impl_type.kind(), - type_ + ty ); // Fast path: if this is a primitive simple `==` will work - let saw_impl = impl_type == ty - || match impl_type.kind() { - // Check if these are the same def_id - ty::Adt(def, _) => { - debug!("adt def_id: {:?}", def.did); - def.did == type_ - } - ty::Foreign(def_id) => *def_id == type_, - _ => false, - }; + let saw_impl = impl_type == ty; if saw_impl { Some(trait_) } else { None } }) diff --git a/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs b/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs index 75159979e889..587cbad68486 100644 --- a/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs +++ b/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs @@ -28,7 +28,6 @@ //! [unit::eq] //~ ERROR unresolved //! [tuple::eq] //~ ERROR unresolved //! [fn::eq] //~ ERROR unresolved -//! [never::eq] //~ ERROR unresolved // FIXME(#78800): This breaks because it's a blanket impl // (I think? Might break for other reasons too.) diff --git a/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr b/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr index 610c83056052..4828a3044635 100644 --- a/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr +++ b/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr @@ -53,17 +53,11 @@ error: unresolved link to `fn::eq` LL | //! [fn::eq] | ^^^^^^ the builtin type `fn` has no associated item named `eq` -error: unresolved link to `never::eq` - --> $DIR/non-path-primitives.rs:31:6 - | -LL | //! [never::eq] - | ^^^^^^^^^ the builtin type `never` has no associated item named `eq` - error: unresolved link to `reference::deref` - --> $DIR/non-path-primitives.rs:35:6 + --> $DIR/non-path-primitives.rs:34:6 | LL | //! [reference::deref] | ^^^^^^^^^^^^^^^^ the builtin type `reference` has no associated item named `deref` -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors diff --git a/src/test/rustdoc/intra-doc/prim-associated-traits.rs b/src/test/rustdoc/intra-doc/prim-associated-traits.rs new file mode 100644 index 000000000000..efda8987d061 --- /dev/null +++ b/src/test/rustdoc/intra-doc/prim-associated-traits.rs @@ -0,0 +1,7 @@ +use std::{num::ParseFloatError, str::FromStr}; + +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.from_str"]' 'f64::from_str()' +/// Uses the rules from [`f64::from_str()`]. +pub struct Number { + pub value: f64, +} From 5512b8dfcaf8002cca8ad8fe289a67d2f14c4fc7 Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Mon, 3 Jan 2022 10:28:01 +0000 Subject: [PATCH 05/12] add a test case for each supported primitive type --- .../intra-doc/prim-associated-traits.rs | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/test/rustdoc/intra-doc/prim-associated-traits.rs b/src/test/rustdoc/intra-doc/prim-associated-traits.rs index efda8987d061..2c9363adbe0f 100644 --- a/src/test/rustdoc/intra-doc/prim-associated-traits.rs +++ b/src/test/rustdoc/intra-doc/prim-associated-traits.rs @@ -1,7 +1,46 @@ -use std::{num::ParseFloatError, str::FromStr}; +#![feature(never_type)] +use std::str::FromStr; // @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.from_str"]' 'f64::from_str()' -/// Uses the rules from [`f64::from_str()`]. +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.from_str"]' 'f32::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.isize.html#method.from_str"]' 'isize::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.i8.html#method.from_str"]' 'i8::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.i16.html#method.from_str"]' 'i16::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.i32.html#method.from_str"]' 'i32::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.i64.html#method.from_str"]' 'i64::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.i128.html#method.from_str"]' 'i128::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.usize.html#method.from_str"]' 'usize::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.u8.html#method.from_str"]' 'u8::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.u16.html#method.from_str"]' 'u16::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.u32.html#method.from_str"]' 'u32::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.u64.html#method.from_str"]' 'u64::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.u128.html#method.from_str"]' 'u128::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.char.html#method.from_str"]' 'char::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.bool.html#method.from_str"]' 'bool::from_str()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html#method.eq"]' 'str::eq()' +// @has 'prim_associated_traits/struct.Number.html' '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.never.html#method.eq"]' 'never::eq()' +/// [`f64::from_str()`] [`f32::from_str()`] [`isize::from_str()`] [`i8::from_str()`] +/// [`i16::from_str()`] [`i32::from_str()`] [`i64::from_str()`] [`i128::from_str()`] +/// [`u16::from_str()`] [`u32::from_str()`] [`u64::from_str()`] [`u128::from_str()`] +/// [`usize::from_str()`] [`u8::from_str()`] [`char::from_str()`] [`bool::from_str()`] +/// [`str::eq()`] [`never::eq()`] pub struct Number { - pub value: f64, + pub f_64: f64, + pub f_32: f32, + pub i_size: isize, + pub i_8: i8, + pub i_16: i16, + pub i_32: i32, + pub i_64: i64, + pub i_128: i128, + pub u_size: usize, + pub u_8: u8, + pub u_16: u16, + pub u_32: u32, + pub u_64: u64, + pub u_128: u128, + pub ch: char, + pub boolean: bool, + pub string: str, + pub n: !, } From eb00e14bca13c1f48424f60b6b539e3ed14eab7f Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Mon, 3 Jan 2022 18:08:19 +0000 Subject: [PATCH 06/12] Remove unsupported types in primitive_to_ty conversion, add FIXME note --- src/librustdoc/passes/collect_intra_doc_links.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 680a74e5aa63..795300862820 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -625,6 +625,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { use PrimitiveType::*; let tcx = self.cx.tcx; + // FIXME: Only simple types are supported here, see if we can support + // other types such as Tuple, Array, Slice, etc. + // See https://github.com/rust-lang/rust/issues/90703#issuecomment-1004263455 Some(tcx.mk_ty(match prim { Bool => ty::Bool, Str => ty::Str, @@ -644,14 +647,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { U64 => ty::Uint(ty::UintTy::U64), U128 => ty::Uint(ty::UintTy::U128), Usize => ty::Uint(ty::UintTy::Usize), - //ty::Tuple(tys) if tys.is_empty() => Res::Primitive(Unit), - //ty::Tuple(_) => Res::Primitive(Tuple), - //ty::Array(..) => Res::Primitive(Array), - //ty::Slice(_) => Res::Primitive(Slice), - //ty::RawPtr(_) => Res::Primitive(RawPointer), - //ty::Ref(..) => Res::Primitive(Reference), - //ty::FnDef(..) => panic!("type alias to a function definition"), - //ty::FnPtr(_) => Res::Primitive(Fn), _ => return None, })) } From 10e3d92306ad4022cff547aa1e02b13cdfab816d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 22 Dec 2021 06:41:26 +1100 Subject: [PATCH 07/12] Label more build steps. Currently the output of a command like `./x.py build --stage 0 library/std` is this: ``` Updating only changed submodules Submodules updated in 0.02 seconds extracting [...] Compiling [...] Finished dev [unoptimized] target(s) in 17.53s Building stage0 std artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu) Compling [...] Finished release [optimized + debuginfo] target(s) in 21.99s Copying stage0 std from stage0 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu / x86_64-unknown-linux-gnu) Build completed successfully in 0:00:51 ``` I find the part before the "Building stage0 std artifacts" a bit confusing. After this commit, it looks like this: ``` Updating only changed submodules Submodules updated in 0.02 seconds extracting [...] Building rustbuild Compiling [...] Finished dev [unoptimized] target(s) in 17.53s Building stage0 std artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu) Compling [...] Finished release [optimized + debuginfo] target(s) in 21.99s Copying stage0 std from stage0 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu / x86_64-unknown-linux-gnu) Build completed successfully in 0:00:51 ``` The "Building rustbuild" label makes it clear what the first cargo build invocation is for. The indentation of the "Submodules updated" line indicates it is a sub-step of a parent task. --- src/bootstrap/bootstrap.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 5a33073e6b02..5235a6b81805 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -974,6 +974,7 @@ def bootstrap_binary(self): def build_bootstrap(self): """Build bootstrap""" + print("Building rustbuild") build_dir = os.path.join(self.build_dir, "bootstrap") if self.clean and os.path.exists(build_dir): shutil.rmtree(build_dir) @@ -1133,7 +1134,7 @@ def update_submodules(self): recorded_submodules[data[3]] = data[2] for module in filtered_submodules: self.update_submodule(module[0], module[1], recorded_submodules) - print("Submodules updated in %.2f seconds" % (time() - start_time)) + print(" Submodules updated in %.2f seconds" % (time() - start_time)) def set_dist_environment(self, url): """Set download URL for normal environment""" From 2e74ca18e43cbaa11300f24cfa0571d7c44d175e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 22 Dec 2021 10:29:49 +1100 Subject: [PATCH 08/12] Tweak the usage messages for `x.py build` and `x.py check`. They're a bit out of date, and overly complicated. --- src/bootstrap/flags.rs | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 2fddda74a28e..9180c5f03af6 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -401,26 +401,19 @@ To learn more about a subcommand, run `./x.py -h`", "\n Arguments: This subcommand accepts a number of paths to directories to the crates - and/or artifacts to compile. For example: - - ./x.py build library/core - ./x.py build library/core library/proc_macro - ./x.py build library/std --stage 1 - - If no arguments are passed then the complete artifacts for that stage are - also compiled. + and/or artifacts to compile. For example, for a quick build of a usable + compiler: - ./x.py build - ./x.py build --stage 1 + ./x.py build --stage 1 library/std - For a quick build of a usable compiler, you can pass: + This will build a compiler and standard library from the local source code. + Once this is done, build/$ARCH/stage1 contains a usable compiler. - ./x.py build --stage 1 library/test + If no arguments are passed then the default artifacts for that stage are + compiled. For example: - This will first build everything once (like `--stage 0` without further - arguments would), and then use the compiler built in stage 0 to build - library/test and its dependencies. - Once this is done, build/$ARCH/stage1 contains a usable compiler.", + ./x.py build --stage 0 + ./x.py build ", ); } "check" | "c" => { @@ -430,14 +423,9 @@ Arguments: This subcommand accepts a number of paths to directories to the crates and/or artifacts to compile. For example: - ./x.py check library/core - ./x.py check library/core library/proc_macro + ./x.py check library/std - If no arguments are passed then the complete artifacts are compiled: std, test, and rustc. Note - also that since we use `cargo check`, by default this will automatically enable incremental - compilation, so there's no need to pass it separately, though it won't hurt. We also completely - ignore the stage passed, as there's no way to compile in non-stage 0 without actually building - the compiler.", + If no arguments are passed then many artifacts are checked.", ); } "clippy" => { From 6152d15e7c06910a3fe1e7621f624ceaa9d010a8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 3 Jan 2022 16:32:52 -0800 Subject: [PATCH 09/12] Extract init_env_logger to crate --- Cargo.lock | 14 ++++-- compiler/rustc_driver/Cargo.toml | 6 +-- compiler/rustc_driver/src/lib.rs | 57 +++-------------------- compiler/rustc_log/Cargo.toml | 16 +++++++ compiler/rustc_log/src/lib.rs | 78 ++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 57 deletions(-) create mode 100644 compiler/rustc_log/Cargo.toml create mode 100644 compiler/rustc_log/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a912eee97dbf..17f7d2ca15a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3808,7 +3808,6 @@ dependencies = [ name = "rustc_driver" version = "0.0.0" dependencies = [ - "atty", "libc", "rustc_ast", "rustc_ast_pretty", @@ -3822,6 +3821,7 @@ dependencies = [ "rustc_hir_pretty", "rustc_interface", "rustc_lint", + "rustc_log", "rustc_metadata", "rustc_middle", "rustc_parse", @@ -3833,8 +3833,6 @@ dependencies = [ "rustc_target", "rustc_typeck", "tracing", - "tracing-subscriber", - "tracing-tree", "winapi", ] @@ -4077,6 +4075,16 @@ dependencies = [ "libc", ] +[[package]] +name = "rustc_log" +version = "0.0.0" +dependencies = [ + "atty", + "tracing", + "tracing-subscriber", + "tracing-tree", +] + [[package]] name = "rustc_macros" version = "0.1.0" diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 2383a000687f..872f946bf7d9 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -8,10 +8,8 @@ crate-type = ["dylib"] [dependencies] libc = "0.2" -atty = "0.2" tracing = { version = "0.1.28" } -tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } -tracing-tree = "0.2.0" +rustc_log = { path = "../rustc_log" } rustc_middle = { path = "../rustc_middle" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_target = { path = "../rustc_target" } @@ -40,4 +38,4 @@ winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] [features] llvm = ['rustc_interface/llvm'] -max_level_info = ['tracing/max_level_info'] +max_level_info = ['rustc_log/max_level_info'] diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 12e0b7a4977e..3d58b27f8392 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -24,6 +24,7 @@ use rustc_feature::find_gated_cfg; use rustc_interface::util::{self, collect_crate_types, get_codegen_backend}; use rustc_interface::{interface, Queries}; use rustc_lint::LintStore; +use rustc_log::stdout_isatty; use rustc_metadata::locator; use rustc_save_analysis as save; use rustc_save_analysis::DumpHandler; @@ -514,14 +515,6 @@ impl Compilation { #[derive(Copy, Clone)] pub struct RustcDefaultCalls; -fn stdout_isatty() -> bool { - atty::is(atty::Stream::Stdout) -} - -fn stderr_isatty() -> bool { - atty::is(atty::Stream::Stderr) -} - fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { let upper_cased_code = code.to_ascii_uppercase(); let normalised = if upper_cased_code.starts_with('E') { @@ -1254,54 +1247,18 @@ pub fn install_ice_hook() { /// This allows tools to enable rust logging without having to magically match rustc's /// tracing crate version. pub fn init_rustc_env_logger() { - init_env_logger("RUSTC_LOG") + if let Err(error) = rustc_log::init_rustc_env_logger() { + early_error(ErrorOutputType::default(), &error.to_string()); + } } /// This allows tools to enable rust logging without having to magically match rustc's /// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var /// other than `RUSTC_LOG`. pub fn init_env_logger(env: &str) { - use tracing_subscriber::{ - filter::{self, EnvFilter, LevelFilter}, - layer::SubscriberExt, - }; - - let filter = match std::env::var(env) { - Ok(env) => EnvFilter::new(env), - _ => EnvFilter::default().add_directive(filter::Directive::from(LevelFilter::WARN)), - }; - - let color_logs = match std::env::var(String::from(env) + "_COLOR") { - Ok(value) => match value.as_ref() { - "always" => true, - "never" => false, - "auto" => stderr_isatty(), - _ => early_error( - ErrorOutputType::default(), - &format!( - "invalid log color value '{}': expected one of always, never, or auto", - value - ), - ), - }, - Err(std::env::VarError::NotPresent) => stderr_isatty(), - Err(std::env::VarError::NotUnicode(_value)) => early_error( - ErrorOutputType::default(), - "non-Unicode log color value: expected one of always, never, or auto", - ), - }; - - let layer = tracing_tree::HierarchicalLayer::default() - .with_writer(io::stderr) - .with_indent_lines(true) - .with_ansi(color_logs) - .with_targets(true) - .with_indent_amount(2); - #[cfg(parallel_compiler)] - let layer = layer.with_thread_ids(true).with_thread_names(true); - - let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); - tracing::subscriber::set_global_default(subscriber).unwrap(); + if let Err(error) = rustc_log::init_env_logger(env) { + early_error(ErrorOutputType::default(), &error.to_string()); + } } #[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))] diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml new file mode 100644 index 000000000000..5b0379462246 --- /dev/null +++ b/compiler/rustc_log/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rustc_log" +version = "0.0.0" +edition = "2021" + +[lib] +doctest = false + +[dependencies] +atty = "0.2" +tracing = "0.1.28" +tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } +tracing-tree = "0.2.0" + +[features] +max_level_info = ['tracing/max_level_info'] diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs new file mode 100644 index 000000000000..498c8cf9c5e9 --- /dev/null +++ b/compiler/rustc_log/src/lib.rs @@ -0,0 +1,78 @@ +//! This crate allows tools to enable rust logging without having to magically +//! match rustc's tracing crate version. + +use std::env::{self, VarError}; +use std::fmt::{self, Display}; +use std::io; +use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter}; +use tracing_subscriber::layer::SubscriberExt; + +pub fn init_rustc_env_logger() -> Result<(), Error> { + init_env_logger("RUSTC_LOG") +} + +/// In contrast to `init_rustc_env_logger` this allows you to choose an env var +/// other than `RUSTC_LOG`. +pub fn init_env_logger(env: &str) -> Result<(), Error> { + let filter = match env::var(env) { + Ok(env) => EnvFilter::new(env), + _ => EnvFilter::default().add_directive(Directive::from(LevelFilter::WARN)), + }; + + let color_logs = match env::var(String::from(env) + "_COLOR") { + Ok(value) => match value.as_ref() { + "always" => true, + "never" => false, + "auto" => stderr_isatty(), + _ => return Err(Error::InvalidColorValue(value)), + }, + Err(VarError::NotPresent) => stderr_isatty(), + Err(VarError::NotUnicode(_value)) => return Err(Error::NonUnicodeColorValue), + }; + + let layer = tracing_tree::HierarchicalLayer::default() + .with_writer(io::stderr) + .with_indent_lines(true) + .with_ansi(color_logs) + .with_targets(true) + .with_indent_amount(2); + #[cfg(parallel_compiler)] + let layer = layer.with_thread_ids(true).with_thread_names(true); + + let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); + tracing::subscriber::set_global_default(subscriber).unwrap(); + + Ok(()) +} + +pub fn stdout_isatty() -> bool { + atty::is(atty::Stream::Stdout) +} + +pub fn stderr_isatty() -> bool { + atty::is(atty::Stream::Stderr) +} + +#[derive(Debug)] +pub enum Error { + InvalidColorValue(String), + NonUnicodeColorValue, +} + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::InvalidColorValue(value) => write!( + formatter, + "invalid log color value '{}': expected one of always, never, or auto", + value, + ), + Error::NonUnicodeColorValue => write!( + formatter, + "non-Unicode log color value: expected one of always, never, or auto", + ), + } + } +} From dd5ee326bc565b19421ac952c23203f9ec63140c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 3 Jan 2022 20:23:59 -0800 Subject: [PATCH 10/12] Justify why rustc_log exists --- compiler/rustc_log/src/lib.rs | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 498c8cf9c5e9..44c76a7379bd 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -1,5 +1,42 @@ //! This crate allows tools to enable rust logging without having to magically //! match rustc's tracing crate version. +//! +//! For example if someone is working on rustc_ast and wants to write some +//! minimal code against it to run in a debugger, with access to the `debug!` +//! logs emitted by rustc_ast, that can be done by writing: +//! +//! ```toml +//! [dependencies] +//! rustc_ast = { path = "../rust/compiler/rustc_ast" } +//! rustc_log = { path = "../rust/compiler/rustc_log" } +//! rustc_span = { path = "../rust/compiler/rustc_span" } +//! ``` +//! +//! ```ignore +//! fn main() { +//! rustc_log::init_rustc_env_logger().unwrap(); +//! +//! let edition = rustc_span::edition::Edition::Edition2021; +//! rustc_span::create_session_globals_then(edition, || { +//! /* ... */ +//! }); +//! } +//! ``` +//! +//! Now `RUSTC_LOG=debug cargo run` will run your minimal main.rs and show +//! rustc's debug logging. In a workflow like this, one might also add +//! `std::env::set_var("RUSTC_LOG", "debug")` to the top of main so that `cargo +//! run` by itself is sufficient to get logs. +//! +//! The reason rustc_log is a tiny separate crate, as opposed to exposing the +//! same things in rustc_driver only, is to enable the above workflow. If you +//! had to depend on rustc_driver in order to turn on rustc's debug logs, that's +//! an enormously bigger dependency tree; every change you make to rustc_ast (or +//! whichever piece of the compiler you are interested in) would involve +//! rebuilding all the rest of rustc up to rustc_driver in order to run your +//! main.rs. Whereas by depending only on rustc_log and the few crates you are +//! debugging, you can make changes inside those crates and quickly run main.rs +//! to read the debug logs. use std::env::{self, VarError}; use std::fmt::{self, Display}; From ffbeebbf7a6dc5fba40b439fc0ebe80ac72af309 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 3 Jan 2022 22:01:35 -0800 Subject: [PATCH 11/12] Make rustc_log doc test runnable --- Cargo.lock | 1 + compiler/rustc_log/Cargo.toml | 6 +++--- compiler/rustc_log/src/lib.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17f7d2ca15a9..eed6cb275164 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4080,6 +4080,7 @@ name = "rustc_log" version = "0.0.0" dependencies = [ "atty", + "rustc_span", "tracing", "tracing-subscriber", "tracing-tree", diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index 5b0379462246..1b2cde605556 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -3,14 +3,14 @@ name = "rustc_log" version = "0.0.0" edition = "2021" -[lib] -doctest = false - [dependencies] atty = "0.2" tracing = "0.1.28" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } tracing-tree = "0.2.0" +[dev-dependencies] +rustc_span = { path = "../rustc_span" } + [features] max_level_info = ['tracing/max_level_info'] diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 44c76a7379bd..f5e7435d36e3 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -12,7 +12,7 @@ //! rustc_span = { path = "../rust/compiler/rustc_span" } //! ``` //! -//! ```ignore +//! ``` //! fn main() { //! rustc_log::init_rustc_env_logger().unwrap(); //! From 4df1a5561adc0ee6e1635df825d34026e04530b1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 4 Jan 2022 14:25:20 -0800 Subject: [PATCH 12/12] Touch up Debug example from PR 92322 --- library/core/src/fmt/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 2d4f477ca018..8a2a64f8dc97 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -572,16 +572,20 @@ impl Display for Arguments<'_> { /// /// [`debug_struct`]: Formatter::debug_struct /// -/// For custom cases, it's also possible to implement `Debug` using the [`write!`] macro: +/// Types that do not wish to use the standard suite of debug representations +/// provided by the `Formatter` trait (`debug_struct`, `debug_tuple`, +/// `debut_list`, `debug_set`, `debug_map`) can do something totally custom by +/// manually writing an arbitrary representation to the `Formatter`. +/// /// ``` /// # use std::fmt; /// # struct Point { /// # x: i32, /// # y: i32, /// # } -/// +/// # /// impl fmt::Debug for Point { -/// fn fmt(&self, f: &mut fmt::Formatter <'_>) -> fmt::Result { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "Point [{} {}]", self.x, self.y) /// } /// }