From 69fe39e8a8e6a66d97836e3068a4b1f183c79ea6 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 5 Jul 2021 22:50:26 -0400 Subject: [PATCH 1/5] Add primitive documentation to libcore This works by doing two things: - Adding links that are specific to the crate. Since not all primitive items are defined in `core` (due to lang_items), these need to use relative links and not intra-doc links. - Duplicating `primitive_docs` in both core and std. This allows not needing CARGO_PKG_NAME to build the standard library. It also adds a tidy check to make sure they stay the same. --- library/core/primitive_docs/box_into_raw.md | 1 + library/core/primitive_docs/fs_file.md | 1 + library/core/primitive_docs/io_bufread.md | 1 + library/core/primitive_docs/io_read.md | 1 + library/core/primitive_docs/io_seek.md | 1 + library/core/primitive_docs/io_write.md | 1 + .../core/primitive_docs/net_tosocketaddrs.md | 1 + library/core/primitive_docs/process_exit.md | 1 + library/core/primitive_docs/string_string.md | 1 + library/core/src/bool.rs | 6 +- library/core/src/char/methods.rs | 12 +- library/core/src/lib.rs | 3 + library/core/src/primitive_docs.rs | 1307 +++++++++++++++++ library/std/primitive_docs/box_into_raw.md | 1 + library/std/primitive_docs/fs_file.md | 1 + library/std/primitive_docs/io_bufread.md | 1 + library/std/primitive_docs/io_read.md | 1 + library/std/primitive_docs/io_seek.md | 1 + library/std/primitive_docs/io_write.md | 1 + .../std/primitive_docs/net_tosocketaddrs.md | 1 + library/std/primitive_docs/process_exit.md | 1 + library/std/primitive_docs/string_string.md | 1 + library/std/src/primitive_docs.rs | 46 +- src/test/rustdoc/auxiliary/primitive-doc.rs | 3 + src/test/rustdoc/cross-crate-primitive-doc.rs | 6 +- src/test/rustdoc/primitive/no_std.rs | 5 + src/tools/tidy/src/lib.rs | 1 + src/tools/tidy/src/main.rs | 1 + src/tools/tidy/src/primitive_docs.rs | 17 + 29 files changed, 1394 insertions(+), 31 deletions(-) create mode 100644 library/core/primitive_docs/box_into_raw.md create mode 100644 library/core/primitive_docs/fs_file.md create mode 100644 library/core/primitive_docs/io_bufread.md create mode 100644 library/core/primitive_docs/io_read.md create mode 100644 library/core/primitive_docs/io_seek.md create mode 100644 library/core/primitive_docs/io_write.md create mode 100644 library/core/primitive_docs/net_tosocketaddrs.md create mode 100644 library/core/primitive_docs/process_exit.md create mode 100644 library/core/primitive_docs/string_string.md create mode 100644 library/core/src/primitive_docs.rs create mode 100644 library/std/primitive_docs/box_into_raw.md create mode 100644 library/std/primitive_docs/fs_file.md create mode 100644 library/std/primitive_docs/io_bufread.md create mode 100644 library/std/primitive_docs/io_read.md create mode 100644 library/std/primitive_docs/io_seek.md create mode 100644 library/std/primitive_docs/io_write.md create mode 100644 library/std/primitive_docs/net_tosocketaddrs.md create mode 100644 library/std/primitive_docs/process_exit.md create mode 100644 library/std/primitive_docs/string_string.md create mode 100644 src/test/rustdoc/primitive/no_std.rs create mode 100644 src/tools/tidy/src/primitive_docs.rs diff --git a/library/core/primitive_docs/box_into_raw.md b/library/core/primitive_docs/box_into_raw.md new file mode 100644 index 0000000000000..9dd0344c7c7b1 --- /dev/null +++ b/library/core/primitive_docs/box_into_raw.md @@ -0,0 +1 @@ +../std/boxed/struct.Box.html#method.into_raw diff --git a/library/core/primitive_docs/fs_file.md b/library/core/primitive_docs/fs_file.md new file mode 100644 index 0000000000000..4023e340a5182 --- /dev/null +++ b/library/core/primitive_docs/fs_file.md @@ -0,0 +1 @@ +../std/fs/struct.File.html diff --git a/library/core/primitive_docs/io_bufread.md b/library/core/primitive_docs/io_bufread.md new file mode 100644 index 0000000000000..7beda2cd39085 --- /dev/null +++ b/library/core/primitive_docs/io_bufread.md @@ -0,0 +1 @@ +../std/io/trait.BufRead.html diff --git a/library/core/primitive_docs/io_read.md b/library/core/primitive_docs/io_read.md new file mode 100644 index 0000000000000..b7ecf5e273cea --- /dev/null +++ b/library/core/primitive_docs/io_read.md @@ -0,0 +1 @@ +../std/io/trait.Read.html diff --git a/library/core/primitive_docs/io_seek.md b/library/core/primitive_docs/io_seek.md new file mode 100644 index 0000000000000..db0274d291c6f --- /dev/null +++ b/library/core/primitive_docs/io_seek.md @@ -0,0 +1 @@ +../std/io/trait.Seek.html diff --git a/library/core/primitive_docs/io_write.md b/library/core/primitive_docs/io_write.md new file mode 100644 index 0000000000000..92a3b88a79c9c --- /dev/null +++ b/library/core/primitive_docs/io_write.md @@ -0,0 +1 @@ +../std/io/trait.Write.html diff --git a/library/core/primitive_docs/net_tosocketaddrs.md b/library/core/primitive_docs/net_tosocketaddrs.md new file mode 100644 index 0000000000000..4daa10ddbe2b2 --- /dev/null +++ b/library/core/primitive_docs/net_tosocketaddrs.md @@ -0,0 +1 @@ +../std/net/trait.ToSocketAddrs.html diff --git a/library/core/primitive_docs/process_exit.md b/library/core/primitive_docs/process_exit.md new file mode 100644 index 0000000000000..cae34d12d5249 --- /dev/null +++ b/library/core/primitive_docs/process_exit.md @@ -0,0 +1 @@ +../std/process/fn.exit.html diff --git a/library/core/primitive_docs/string_string.md b/library/core/primitive_docs/string_string.md new file mode 100644 index 0000000000000..303dc07b1855d --- /dev/null +++ b/library/core/primitive_docs/string_string.md @@ -0,0 +1 @@ +../std/string/struct.String.html diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index ca1c0ae759892..f14c2a4641627 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -2,7 +2,8 @@ #[lang = "bool"] impl bool { - /// Returns `Some(t)` if the `bool` is [`true`](keyword.true.html), or `None` otherwise. + /// Returns `Some(t)` if the `bool` is [`true`](../std/keyword.true.html), + /// or `None` otherwise. /// /// # Examples /// @@ -18,7 +19,8 @@ impl bool { if self { Some(t) } else { None } } - /// Returns `Some(f())` if the `bool` is [`true`](keyword.true.html), or `None` otherwise. + /// Returns `Some(f())` if the `bool` is [`true`](../std/keyword.true.html), + /// or `None` otherwise. /// /// # Examples /// diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index e6d3ac8f2d2c5..07ffd06511885 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -96,7 +96,7 @@ impl char { /// Converts a `u32` to a `char`. /// /// Note that all `char`s are valid [`u32`]s, and can be cast to one with - /// [`as`](keyword.as.html): + /// [`as`](../std/keyword.as.html): /// /// ``` /// let c = 'šŸ’Æ'; @@ -372,7 +372,7 @@ impl char { /// println!("\\u{{2764}}"); /// ``` /// - /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string): + /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string): /// /// ``` /// assert_eq!('ā¤'.escape_unicode().to_string(), "\\u{2764}"); @@ -448,7 +448,7 @@ impl char { /// println!("\\n"); /// ``` /// - /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string): + /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string): /// /// ``` /// assert_eq!('\n'.escape_debug().to_string(), "\\n"); @@ -502,7 +502,7 @@ impl char { /// println!("\\\""); /// ``` /// - /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string): + /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string): /// /// ``` /// assert_eq!('"'.escape_default().to_string(), "\\\""); @@ -937,7 +937,7 @@ impl char { /// println!("i\u{307}"); /// ``` /// - /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string): + /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string): /// /// ``` /// assert_eq!('C'.to_lowercase().to_string(), "c"); @@ -1002,7 +1002,7 @@ impl char { /// println!("SS"); /// ``` /// - /// Using [`to_string`](string/trait.ToString.html#tymethod.to_string): + /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string): /// /// ``` /// assert_eq!('c'.to_uppercase().to_string(), "C"); diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 06a409c6a2eb9..265ba9f1bb91b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -130,6 +130,7 @@ #![feature(decl_macro)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] +#![feature(doc_primitive)] #![feature(exhaustive_patterns)] #![feature(extern_types)] #![feature(fundamental)] @@ -355,3 +356,5 @@ pub mod arch { /* compiler built-in */ } } + +include!("primitive_docs.rs"); diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs new file mode 100644 index 0000000000000..0de9126dab2fe --- /dev/null +++ b/library/core/src/primitive_docs.rs @@ -0,0 +1,1307 @@ +// `library/{std,core}/src/primitive_docs.rs` should have the same contents. +// These are different files so that relative links work properly without +// having to have `CARGO_PKG_NAME` set, but conceptually they should always be the same. +#[doc(primitive = "bool")] +#[doc(alias = "true")] +#[doc(alias = "false")] +/// The boolean type. +/// +/// The `bool` represents a value, which could only be either [`true`] or [`false`]. If you cast +/// a `bool` into an integer, [`true`] will be 1 and [`false`] will be 0. +/// +/// # Basic usage +/// +/// `bool` implements various traits, such as [`BitAnd`], [`BitOr`], [`Not`], etc., +/// which allow us to perform boolean operations using `&`, `|` and `!`. +/// +/// [`if`] requires a `bool` value as its conditional. [`assert!`], which is an +/// important macro in testing, checks whether an expression is [`true`] and panics +/// if it isn't. +/// +/// ``` +/// let bool_val = true & false | false; +/// assert!(!bool_val); +/// ``` +/// +/// [`true`]: ../std/keyword.true.html +/// [`false`]: ../std/keyword.false.html +/// [`BitAnd`]: ops::BitAnd +/// [`BitOr`]: ops::BitOr +/// [`Not`]: ops::Not +/// [`if`]: ../std/keyword.if.html +/// +/// # Examples +/// +/// A trivial example of the usage of `bool`: +/// +/// ``` +/// let praise_the_borrow_checker = true; +/// +/// // using the `if` conditional +/// if praise_the_borrow_checker { +/// println!("oh, yeah!"); +/// } else { +/// println!("what?!!"); +/// } +/// +/// // ... or, a match pattern +/// match praise_the_borrow_checker { +/// true => println!("keep praising!"), +/// false => println!("you should praise!"), +/// } +/// ``` +/// +/// Also, since `bool` implements the [`Copy`] trait, we don't +/// have to worry about the move semantics (just like the integer and float primitives). +/// +/// Now an example of `bool` cast to integer type: +/// +/// ``` +/// assert_eq!(true as i32, 1); +/// assert_eq!(false as i32, 0); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_bool {} + +#[doc(primitive = "never")] +#[doc(alias = "!")] +// +/// The `!` type, also called "never". +/// +/// `!` represents the type of computations which never resolve to any value at all. For example, +/// the [`exit`] function `fn exit(code: i32) -> !` exits the process without ever returning, and +/// so returns `!`. +/// +/// `break`, `continue` and `return` expressions also have type `!`. For example we are allowed to +/// write: +/// +/// ``` +/// #![feature(never_type)] +/// # fn foo() -> u32 { +/// let x: ! = { +/// return 123 +/// }; +/// # } +/// ``` +/// +/// Although the `let` is pointless here, it illustrates the meaning of `!`. Since `x` is never +/// assigned a value (because `return` returns from the entire function), `x` can be given type +/// `!`. We could also replace `return 123` with a `panic!` or a never-ending `loop` and this code +/// would still be valid. +/// +/// A more realistic usage of `!` is in this code: +/// +/// ``` +/// # fn get_a_number() -> Option { None } +/// # loop { +/// let num: u32 = match get_a_number() { +/// Some(num) => num, +/// None => break, +/// }; +/// # } +/// ``` +/// +/// Both match arms must produce values of type [`u32`], but since `break` never produces a value +/// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another +/// behaviour of the `!` type - expressions with type `!` will coerce into any other type. +/// +/// [`u32`]: prim@u32 +#[doc = concat!("[`exit`]: ", include_str!("../primitive_docs/process_exit.md"))] +/// +/// # `!` and generics +/// +/// ## Infallible errors +/// +/// The main place you'll see `!` used explicitly is in generic code. Consider the [`FromStr`] +/// trait: +/// +/// ``` +/// trait FromStr: Sized { +/// type Err; +/// fn from_str(s: &str) -> Result; +/// } +/// ``` +/// +/// When implementing this trait for [`String`] we need to pick a type for [`Err`]. And since +/// converting a string into a string will never result in an error, the appropriate type is `!`. +/// (Currently the type actually used is an enum with no variants, though this is only because `!` +/// was added to Rust at a later date and it may change in the future.) With an [`Err`] type of +/// `!`, if we have to call [`String::from_str`] for some reason the result will be a +/// [`Result`] which we can unpack like this: +/// +/// ``` +/// #![feature(exhaustive_patterns)] +/// use std::str::FromStr; +/// let Ok(s) = String::from_str("hello"); +/// ``` +/// +/// Since the [`Err`] variant contains a `!`, it can never occur. If the `exhaustive_patterns` +/// feature is present this means we can exhaustively match on [`Result`] by just taking the +/// [`Ok`] variant. This illustrates another behaviour of `!` - it can be used to "delete" certain +/// enum variants from generic types like `Result`. +/// +/// ## Infinite loops +/// +/// While [`Result`] is very useful for removing errors, `!` can also be used to remove +/// successes as well. If we think of [`Result`] as "if this function returns, it has not +/// errored," we get a very intuitive idea of [`Result`] as well: if the function returns, it +/// *has* errored. +/// +/// For example, consider the case of a simple web server, which can be simplified to: +/// +/// ```ignore (hypothetical-example) +/// loop { +/// let (client, request) = get_request().expect("disconnected"); +/// let response = request.process(); +/// response.send(client); +/// } +/// ``` +/// +/// Currently, this isn't ideal, because we simply panic whenever we fail to get a new connection. +/// Instead, we'd like to keep track of this error, like this: +/// +/// ```ignore (hypothetical-example) +/// loop { +/// match get_request() { +/// Err(err) => break err, +/// Ok((client, request)) => { +/// let response = request.process(); +/// response.send(client); +/// }, +/// } +/// } +/// ``` +/// +/// Now, when the server disconnects, we exit the loop with an error instead of panicking. While it +/// might be intuitive to simply return the error, we might want to wrap it in a [`Result`] +/// instead: +/// +/// ```ignore (hypothetical-example) +/// fn server_loop() -> Result { +/// loop { +/// let (client, request) = get_request()?; +/// let response = request.process(); +/// response.send(client); +/// } +/// } +/// ``` +/// +/// Now, we can use `?` instead of `match`, and the return type makes a lot more sense: if the loop +/// ever stops, it means that an error occurred. We don't even have to wrap the loop in an `Ok` +/// because `!` coerces to `Result` automatically. +/// +/// [`String::from_str`]: str::FromStr::from_str +#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))] +/// [`FromStr`]: str::FromStr +/// +/// # `!` and traits +/// +/// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl` +/// which doesn't `panic!`. The reason is that functions returning an `impl Trait` where `!` +/// does not have an `impl` of `Trait` cannot diverge as their only possible code path. In other +/// words, they can't return `!` from every code path. As an example, this code doesn't compile: +/// +/// ```compile_fail +/// use std::ops::Add; +/// +/// fn foo() -> impl Add { +/// unimplemented!() +/// } +/// ``` +/// +/// But this code does: +/// +/// ``` +/// use std::ops::Add; +/// +/// fn foo() -> impl Add { +/// if true { +/// unimplemented!() +/// } else { +/// 0 +/// } +/// } +/// ``` +/// +/// The reason is that, in the first example, there are many possible types that `!` could coerce +/// to, because many types implement `Add`. However, in the second example, +/// the `else` branch returns a `0`, which the compiler infers from the return type to be of type +/// `u32`. Since `u32` is a concrete type, `!` can and will be coerced to it. See issue [#36375] +/// for more information on this quirk of `!`. +/// +/// [#36375]: https://github.com/rust-lang/rust/issues/36375 +/// +/// As it turns out, though, most traits can have an `impl` for `!`. Take [`Debug`] +/// for example: +/// +/// ``` +/// #![feature(never_type)] +/// # use std::fmt; +/// # trait Debug { +/// # fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result; +/// # } +/// impl Debug for ! { +/// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +/// *self +/// } +/// } +/// ``` +/// +/// Once again we're using `!`'s ability to coerce into any other type, in this case +/// [`fmt::Result`]. Since this method takes a `&!` as an argument we know that it can never be +/// called (because there is no value of type `!` for it to be called with). Writing `*self` +/// essentially tells the compiler "We know that this code can never be run, so just treat the +/// entire function body as having type [`fmt::Result`]". This pattern can be used a lot when +/// implementing traits for `!`. Generally, any trait which only has methods which take a `self` +/// parameter should have such an impl. +/// +/// On the other hand, one trait which would not be appropriate to implement is [`Default`]: +/// +/// ``` +/// trait Default { +/// fn default() -> Self; +/// } +/// ``` +/// +/// Since `!` has no values, it has no default value either. It's true that we could write an +/// `impl` for this which simply panics, but the same is true for any type (we could `impl +/// Default` for (eg.) [`File`] by just making [`default()`] panic.) +/// +#[doc = concat!("[`File`]: ", include_str!("../primitive_docs/fs_file.md"))] +/// [`Debug`]: fmt::Debug +/// [`default()`]: Default::default +/// +#[unstable(feature = "never_type", issue = "35121")] +mod prim_never {} + +#[doc(primitive = "char")] +/// A character type. +/// +/// The `char` type represents a single character. More specifically, since +/// 'character' isn't a well-defined concept in Unicode, `char` is a '[Unicode +/// scalar value]', which is similar to, but not the same as, a '[Unicode code +/// point]'. +/// +/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value +/// [Unicode code point]: https://www.unicode.org/glossary/#code_point +/// +/// This documentation describes a number of methods and trait implementations on the +/// `char` type. For technical reasons, there is additional, separate +/// documentation in [the `std::char` module](char/index.html) as well. +/// +/// # Representation +/// +/// `char` is always four bytes in size. This is a different representation than +/// a given character would have as part of a [`String`]. For example: +/// +/// ``` +/// let v = vec!['h', 'e', 'l', 'l', 'o']; +/// +/// // five elements times four bytes for each element +/// assert_eq!(20, v.len() * std::mem::size_of::()); +/// +/// let s = String::from("hello"); +/// +/// // five elements times one byte per element +/// assert_eq!(5, s.len() * std::mem::size_of::()); +/// ``` +/// +#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))] +/// +/// As always, remember that a human intuition for 'character' might not map to +/// Unicode's definitions. For example, despite looking similar, the 'Ć©' +/// character is one Unicode code point while 'eĢ' is two Unicode code points: +/// +/// ``` +/// let mut chars = "Ć©".chars(); +/// // U+00e9: 'latin small letter e with acute' +/// assert_eq!(Some('\u{00e9}'), chars.next()); +/// assert_eq!(None, chars.next()); +/// +/// let mut chars = "eĢ".chars(); +/// // U+0065: 'latin small letter e' +/// assert_eq!(Some('\u{0065}'), chars.next()); +/// // U+0301: 'combining acute accent' +/// assert_eq!(Some('\u{0301}'), chars.next()); +/// assert_eq!(None, chars.next()); +/// ``` +/// +/// This means that the contents of the first string above _will_ fit into a +/// `char` while the contents of the second string _will not_. Trying to create +/// a `char` literal with the contents of the second string gives an error: +/// +/// ```text +/// error: character literal may only contain one codepoint: 'eĢ' +/// let c = 'eĢ'; +/// ^^^ +/// ``` +/// +/// Another implication of the 4-byte fixed size of a `char` is that +/// per-`char` processing can end up using a lot more memory: +/// +/// ``` +/// let s = String::from("love: ā¤ļø"); +/// let v: Vec = s.chars().collect(); +/// +/// assert_eq!(12, std::mem::size_of_val(&s[..])); +/// assert_eq!(32, std::mem::size_of_val(&v[..])); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_char {} + +#[doc(primitive = "unit")] +#[doc(alias = "(")] +#[doc(alias = ")")] +#[doc(alias = "()")] +// +/// The `()` type, also called "unit". +/// +/// The `()` type has exactly one value `()`, and is used when there +/// is no other meaningful value that could be returned. `()` is most +/// commonly seen implicitly: functions without a `-> ...` implicitly +/// have return type `()`, that is, these are equivalent: +/// +/// ```rust +/// fn long() -> () {} +/// +/// fn short() {} +/// ``` +/// +/// The semicolon `;` can be used to discard the result of an +/// expression at the end of a block, making the expression (and thus +/// the block) evaluate to `()`. For example, +/// +/// ```rust +/// fn returns_i64() -> i64 { +/// 1i64 +/// } +/// fn returns_unit() { +/// 1i64; +/// } +/// +/// let is_i64 = { +/// returns_i64() +/// }; +/// let is_unit = { +/// returns_i64(); +/// }; +/// ``` +/// +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_unit {} + +#[doc(primitive = "pointer")] +#[doc(alias = "ptr")] +#[doc(alias = "*")] +#[doc(alias = "*const")] +#[doc(alias = "*mut")] +// +/// Raw, unsafe pointers, `*const T`, and `*mut T`. +/// +/// *[See also the `std::ptr` module](ptr).* +/// +/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. +/// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is +/// dereferenced (using the `*` operator), it must be non-null and aligned. +/// +/// Storing through a raw pointer using `*ptr = data` calls `drop` on the old value, so +/// [`write`] must be used if the type has drop glue and memory is not already +/// initialized - otherwise `drop` would be called on the uninitialized memory. +/// +/// Use the [`null`] and [`null_mut`] functions to create null pointers, and the +/// [`is_null`] method of the `*const T` and `*mut T` types to check for null. +/// The `*const T` and `*mut T` types also define the [`offset`] method, for +/// pointer math. +/// +/// # Common ways to create raw pointers +/// +/// ## 1. Coerce a reference (`&T`) or mutable reference (`&mut T`). +/// +/// ``` +/// let my_num: i32 = 10; +/// let my_num_ptr: *const i32 = &my_num; +/// let mut my_speed: i32 = 88; +/// let my_speed_ptr: *mut i32 = &mut my_speed; +/// ``` +/// +/// To get a pointer to a boxed value, dereference the box: +/// +/// ``` +/// let my_num: Box = Box::new(10); +/// let my_num_ptr: *const i32 = &*my_num; +/// let mut my_speed: Box = Box::new(88); +/// let my_speed_ptr: *mut i32 = &mut *my_speed; +/// ``` +/// +/// This does not take ownership of the original allocation +/// and requires no resource management later, +/// but you must not use the pointer after its lifetime. +/// +/// ## 2. Consume a box (`Box`). +/// +/// The [`into_raw`] function consumes a box and returns +/// the raw pointer. It doesn't destroy `T` or deallocate any memory. +/// +/// ``` +/// let my_speed: Box = Box::new(88); +/// let my_speed: *mut i32 = Box::into_raw(my_speed); +/// +/// // By taking ownership of the original `Box` though +/// // we are obligated to put it together later to be destroyed. +/// unsafe { +/// drop(Box::from_raw(my_speed)); +/// } +/// ``` +/// +/// Note that here the call to [`drop`] is for clarity - it indicates +/// that we are done with the given value and it should be destroyed. +/// +/// ## 3. Create it using `ptr::addr_of!` +/// +/// Instead of coercing a reference to a raw pointer, you can use the macros +/// [`ptr::addr_of!`] (for `*const T`) and [`ptr::addr_of_mut!`] (for `*mut T`). +/// These macros allow you to create raw pointers to fields to which you cannot +/// create a reference (without causing undefined behaviour), such as an +/// unaligned field. This might be necessary if packed structs or uninitialized +/// memory is involved. +/// +/// ``` +/// #[derive(Debug, Default, Copy, Clone)] +/// #[repr(C, packed)] +/// struct S { +/// aligned: u8, +/// unaligned: u32, +/// } +/// let s = S::default(); +/// let p = std::ptr::addr_of!(s.unaligned); // not allowed with coercion +/// ``` +/// +/// ## 4. Get it from C. +/// +/// ``` +/// # #![feature(rustc_private)] +/// extern crate libc; +/// +/// use std::mem; +/// +/// unsafe { +/// let my_num: *mut i32 = libc::malloc(mem::size_of::()) as *mut i32; +/// if my_num.is_null() { +/// panic!("failed to allocate memory"); +/// } +/// libc::free(my_num as *mut libc::c_void); +/// } +/// ``` +/// +/// Usually you wouldn't literally use `malloc` and `free` from Rust, +/// but C APIs hand out a lot of pointers generally, so are a common source +/// of raw pointers in Rust. +/// +/// [`null`]: ptr::null +/// [`null_mut`]: ptr::null_mut +/// [`is_null`]: pointer::is_null +/// [`offset`]: pointer::offset +#[doc = concat!("[`into_raw`]: ", include_str!("../primitive_docs/box_into_raw.md"))] +/// [`drop`]: mem::drop +/// [`write`]: ptr::write +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_pointer {} + +#[doc(primitive = "array")] +#[doc(alias = "[]")] +#[doc(alias = "[T;N]")] // unfortunately, rustdoc doesn't have fuzzy search for aliases +#[doc(alias = "[T; N]")] +/// A fixed-size array, denoted `[T; N]`, for the element type, `T`, and the +/// non-negative compile-time constant size, `N`. +/// +/// There are two syntactic forms for creating an array: +/// +/// * A list with each element, i.e., `[x, y, z]`. +/// * A repeat expression `[x; N]`, which produces an array with `N` copies of `x`. +/// The type of `x` must be [`Copy`]. +/// +/// Note that `[expr; 0]` is allowed, and produces an empty array. +/// This will still evaluate `expr`, however, and immediately drop the resulting value, so +/// be mindful of side effects. +/// +/// Arrays of *any* size implement the following traits if the element type allows it: +/// +/// - [`Copy`] +/// - [`Clone`] +/// - [`Debug`] +/// - [`IntoIterator`] (implemented for `[T; N]`, `&[T; N]` and `&mut [T; N]`) +/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] +/// - [`Hash`] +/// - [`AsRef`], [`AsMut`] +/// - [`Borrow`], [`BorrowMut`] +/// +/// Arrays of sizes from 0 to 32 (inclusive) implement the [`Default`] trait +/// if the element type allows it. As a stopgap, trait implementations are +/// statically generated up to size 32. +/// +/// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on +/// an array. Indeed, this provides most of the API for working with arrays. +/// Slices have a dynamic size and do not coerce to arrays. +/// +/// You can move elements out of an array with a [slice pattern]. If you want +/// one element, see [`mem::replace`]. +/// +/// # Examples +/// +/// ``` +/// let mut array: [i32; 3] = [0; 3]; +/// +/// array[1] = 1; +/// array[2] = 2; +/// +/// assert_eq!([1, 2], &array[1..]); +/// +/// // This loop prints: 0 1 2 +/// for x in array { +/// print!("{} ", x); +/// } +/// ``` +/// +/// You can also iterate over reference to the array's elements: +/// +/// ``` +/// let array: [i32; 3] = [0; 3]; +/// +/// for x in &array { } +/// ``` +/// +/// You can use a [slice pattern] to move elements out of an array: +/// +/// ``` +/// fn move_away(_: String) { /* Do interesting things. */ } +/// +/// let [john, roa] = ["John".to_string(), "Roa".to_string()]; +/// move_away(john); +/// move_away(roa); +/// ``` +/// +/// # Editions +/// +/// Prior to Rust 1.53, arrays did not implement [`IntoIterator`] by value, so the method call +/// `array.into_iter()` auto-referenced into a [slice iterator](slice::iter). Right now, the old +/// behavior is preserved in the 2015 and 2018 editions of Rust for compatibility, ignoring +/// [`IntoIterator`] by value. In the future, the behavior on the 2015 and 2018 edition +/// might be made consistent to the behavior of later editions. +/// +/// ```rust,edition2018 +/// // Rust 2015 and 2018: +/// +/// # #![allow(array_into_iter)] // override our `deny(warnings)` +/// let array: [i32; 3] = [0; 3]; +/// +/// // This creates a slice iterator, producing references to each value. +/// for item in array.into_iter().enumerate() { +/// let (i, x): (usize, &i32) = item; +/// println!("array[{}] = {}", i, x); +/// } +/// +/// // The `array_into_iter` lint suggests this change for future compatibility: +/// for item in array.iter().enumerate() { +/// let (i, x): (usize, &i32) = item; +/// println!("array[{}] = {}", i, x); +/// } +/// +/// // You can explicitly iterate an array by value using +/// // `IntoIterator::into_iter` or `std::array::IntoIter::new`: +/// for item in IntoIterator::into_iter(array).enumerate() { +/// let (i, x): (usize, i32) = item; +/// println!("array[{}] = {}", i, x); +/// } +/// ``` +/// +/// Starting in the 2021 edition, `array.into_iter()` uses `IntoIterator` normally to iterate +/// by value, and `iter()` should be used to iterate by reference like previous editions. +/// +/// ```rust,edition2021 +/// // Rust 2021: +/// +/// let array: [i32; 3] = [0; 3]; +/// +/// // This iterates by reference: +/// for item in array.iter().enumerate() { +/// let (i, x): (usize, &i32) = item; +/// println!("array[{}] = {}", i, x); +/// } +/// +/// // This iterates by value: +/// for item in array.into_iter().enumerate() { +/// let (i, x): (usize, i32) = item; +/// println!("array[{}] = {}", i, x); +/// } +/// ``` +/// +/// Future language versions might start treating the `array.into_iter()` +/// syntax on editions 2015 and 2018 the same as on edition 2021. So code using +/// those older editions should still be written with this change in mind, to +/// prevent breakage in the future. The safest way to accomplish this is to +/// avoid the `into_iter` syntax on those editions. If an edition update is not +/// viable/desired, there are multiple alternatives: +/// * use `iter`, equivalent to the old behavior, creating references +/// * use [`IntoIterator::into_iter`], equivalent to the post-2021 behavior (Rust 1.53+) +/// * replace `for ... in array.into_iter() {` with `for ... in array {`, +/// equivalent to the post-2021 behavior (Rust 1.53+) +/// +/// ```rust,edition2018 +/// // Rust 2015 and 2018: +/// +/// let array: [i32; 3] = [0; 3]; +/// +/// // This iterates by reference: +/// for item in array.iter() { +/// let x: &i32 = item; +/// println!("{}", x); +/// } +/// +/// // This iterates by value: +/// for item in IntoIterator::into_iter(array) { +/// let x: i32 = item; +/// println!("{}", x); +/// } +/// +/// // This iterates by value: +/// for item in array { +/// let x: i32 = item; +/// println!("{}", x); +/// } +/// +/// // IntoIter can also start a chain. +/// // This iterates by value: +/// for item in IntoIterator::into_iter(array).enumerate() { +/// let (i, x): (usize, i32) = item; +/// println!("array[{}] = {}", i, x); +/// } +/// ``` +/// +/// [slice]: prim@slice +/// [`Debug`]: fmt::Debug +/// [`Hash`]: hash::Hash +/// [`Borrow`]: borrow::Borrow +/// [`BorrowMut`]: borrow::BorrowMut +/// [slice pattern]: ../reference/patterns.html#slice-patterns +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_array {} + +#[doc(primitive = "slice")] +#[doc(alias = "[")] +#[doc(alias = "]")] +#[doc(alias = "[]")] +/// A dynamically-sized view into a contiguous sequence, `[T]`. Contiguous here +/// means that elements are laid out so that every element is the same +/// distance from its neighbors. +/// +/// *[See also the `std::slice` module](crate::slice).* +/// +/// Slices are a view into a block of memory represented as a pointer and a +/// length. +/// +/// ``` +/// // slicing a Vec +/// let vec = vec![1, 2, 3]; +/// let int_slice = &vec[..]; +/// // coercing an array to a slice +/// let str_slice: &[&str] = &["one", "two", "three"]; +/// ``` +/// +/// Slices are either mutable or shared. The shared slice type is `&[T]`, +/// while the mutable slice type is `&mut [T]`, where `T` represents the element +/// type. For example, you can mutate the block of memory that a mutable slice +/// points to: +/// +/// ``` +/// let mut x = [1, 2, 3]; +/// let x = &mut x[..]; // Take a full slice of `x`. +/// x[1] = 7; +/// assert_eq!(x, &[1, 7, 3]); +/// ``` +/// +/// As slices store the length of the sequence they refer to, they have twice +/// the size of pointers to [`Sized`](marker/trait.Sized.html) types. +/// Also see the reference on +/// [dynamically sized types](../reference/dynamically-sized-types.html). +/// +/// ``` +/// # use std::rc::Rc; +/// let pointer_size = std::mem::size_of::<&u8>(); +/// assert_eq!(2 * pointer_size, std::mem::size_of::<&[u8]>()); +/// assert_eq!(2 * pointer_size, std::mem::size_of::<*const [u8]>()); +/// assert_eq!(2 * pointer_size, std::mem::size_of::>()); +/// assert_eq!(2 * pointer_size, std::mem::size_of::>()); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_slice {} + +#[doc(primitive = "str")] +// +/// String slices. +/// +/// *[See also the `std::str` module](crate::str).* +/// +/// The `str` type, also called a 'string slice', is the most primitive string +/// type. It is usually seen in its borrowed form, `&str`. It is also the type +/// of string literals, `&'static str`. +/// +/// String slices are always valid UTF-8. +/// +/// # Examples +/// +/// String literals are string slices: +/// +/// ``` +/// let hello = "Hello, world!"; +/// +/// // with an explicit type annotation +/// let hello: &'static str = "Hello, world!"; +/// ``` +/// +/// They are `'static` because they're stored directly in the final binary, and +/// so will be valid for the `'static` duration. +/// +/// # Representation +/// +/// A `&str` is made up of two components: a pointer to some bytes, and a +/// length. You can look at these with the [`as_ptr`] and [`len`] methods: +/// +/// ``` +/// use std::slice; +/// use std::str; +/// +/// let story = "Once upon a time..."; +/// +/// let ptr = story.as_ptr(); +/// let len = story.len(); +/// +/// // story has nineteen bytes +/// assert_eq!(19, len); +/// +/// // We can re-build a str out of ptr and len. This is all unsafe because +/// // we are responsible for making sure the two components are valid: +/// let s = unsafe { +/// // First, we build a &[u8]... +/// let slice = slice::from_raw_parts(ptr, len); +/// +/// // ... and then convert that slice into a string slice +/// str::from_utf8(slice) +/// }; +/// +/// assert_eq!(s, Ok(story)); +/// ``` +/// +/// [`as_ptr`]: str::as_ptr +/// [`len`]: str::len +/// +/// Note: This example shows the internals of `&str`. `unsafe` should not be +/// used to get a string slice under normal circumstances. Use `as_str` +/// instead. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_str {} + +#[doc(primitive = "tuple")] +#[doc(alias = "(")] +#[doc(alias = ")")] +#[doc(alias = "()")] +// +/// A finite heterogeneous sequence, `(T, U, ..)`. +/// +/// Let's cover each of those in turn: +/// +/// Tuples are *finite*. In other words, a tuple has a length. Here's a tuple +/// of length `3`: +/// +/// ``` +/// ("hello", 5, 'c'); +/// ``` +/// +/// 'Length' is also sometimes called 'arity' here; each tuple of a different +/// length is a different, distinct type. +/// +/// Tuples are *heterogeneous*. This means that each element of the tuple can +/// have a different type. In that tuple above, it has the type: +/// +/// ``` +/// # let _: +/// (&'static str, i32, char) +/// # = ("hello", 5, 'c'); +/// ``` +/// +/// Tuples are a *sequence*. This means that they can be accessed by position; +/// this is called 'tuple indexing', and it looks like this: +/// +/// ```rust +/// let tuple = ("hello", 5, 'c'); +/// +/// assert_eq!(tuple.0, "hello"); +/// assert_eq!(tuple.1, 5); +/// assert_eq!(tuple.2, 'c'); +/// ``` +/// +/// The sequential nature of the tuple applies to its implementations of various +/// traits. For example, in [`PartialOrd`] and [`Ord`], the elements are compared +/// sequentially until the first non-equal set is found. +/// +/// For more about tuples, see [the book](../book/ch03-02-data-types.html#the-tuple-type). +/// +/// # Trait implementations +/// +/// If every type inside a tuple implements one of the following traits, then a +/// tuple itself also implements it. +/// +/// * [`Clone`] +/// * [`Copy`] +/// * [`PartialEq`] +/// * [`Eq`] +/// * [`PartialOrd`] +/// * [`Ord`] +/// * [`Debug`] +/// * [`Default`] +/// * [`Hash`] +/// +/// [`Debug`]: fmt::Debug +/// [`Hash`]: hash::Hash +/// +/// Due to a temporary restriction in Rust's type system, these traits are only +/// implemented on tuples of arity 12 or less. In the future, this may change. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let tuple = ("hello", 5, 'c'); +/// +/// assert_eq!(tuple.0, "hello"); +/// ``` +/// +/// Tuples are often used as a return type when you want to return more than +/// one value: +/// +/// ``` +/// fn calculate_point() -> (i32, i32) { +/// // Don't do a calculation, that's not the point of the example +/// (4, 5) +/// } +/// +/// let point = calculate_point(); +/// +/// assert_eq!(point.0, 4); +/// assert_eq!(point.1, 5); +/// +/// // Combining this with patterns can be nicer. +/// +/// let (x, y) = calculate_point(); +/// +/// assert_eq!(x, 4); +/// assert_eq!(y, 5); +/// ``` +/// +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_tuple {} + +#[doc(primitive = "f32")] +/// A 32-bit floating point type (specifically, the "binary32" type defined in IEEE 754-2008). +/// +/// This type can represent a wide range of decimal numbers, like `3.5`, `27`, +/// `-113.75`, `0.0078125`, `34359738368`, `0`, `-1`. So unlike integer types +/// (such as `i32`), floating point types can represent non-integer numbers, +/// too. +/// +/// However, being able to represent this wide range of numbers comes at the +/// cost of precision: floats can only represent some of the real numbers and +/// calculation with floats round to a nearby representable number. For example, +/// `5.0` and `1.0` can be exactly represented as `f32`, but `1.0 / 5.0` results +/// in `0.20000000298023223876953125` since `0.2` cannot be exactly represented +/// as `f32`. Note, however, that printing floats with `println` and friends will +/// often discard insignificant digits: `println!("{}", 1.0f32 / 5.0f32)` will +/// print `0.2`. +/// +/// Additionally, `f32` can represent some special values: +/// +/// - āˆ’0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so āˆ’0.0 is a +/// possible value. For comparison āˆ’0.0 = +0.0, but floating point operations can carry +/// the sign bit through arithmetic operations. This means āˆ’0.0 Ɨ +0.0 produces āˆ’0.0 and +/// a negative number rounded to a value smaller than a float can represent also produces āˆ’0.0. +/// - [āˆž](#associatedconstant.INFINITY) and +/// [āˆ’āˆž](#associatedconstant.NEG_INFINITY): these result from calculations +/// like `1.0 / 0.0`. +/// - [NaN (not a number)](#associatedconstant.NAN): this value results from +/// calculations like `(-1.0).sqrt()`. NaN has some potentially unexpected +/// behavior: it is unequal to any float, including itself! It is also neither +/// smaller nor greater than any float, making it impossible to sort. Lastly, +/// it is considered infectious as almost all calculations where one of the +/// operands is NaN will also result in NaN. +/// +/// For more information on floating point numbers, see [Wikipedia][wikipedia]. +/// +/// *[See also the `std::f32::consts` module](crate::f32::consts).* +/// +/// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_f32 {} + +#[doc(primitive = "f64")] +/// A 64-bit floating point type (specifically, the "binary64" type defined in IEEE 754-2008). +/// +/// This type is very similar to [`f32`], but has increased +/// precision by using twice as many bits. Please see [the documentation for +/// `f32`][`f32`] or [Wikipedia on double precision +/// values][wikipedia] for more information. +/// +/// *[See also the `std::f64::consts` module](crate::f64::consts).* +/// +/// [`f32`]: prim@f32 +/// [wikipedia]: https://en.wikipedia.org/wiki/Double-precision_floating-point_format +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_f64 {} + +#[doc(primitive = "i8")] +// +/// The 8-bit signed integer type. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_i8 {} + +#[doc(primitive = "i16")] +// +/// The 16-bit signed integer type. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_i16 {} + +#[doc(primitive = "i32")] +// +/// The 32-bit signed integer type. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_i32 {} + +#[doc(primitive = "i64")] +// +/// The 64-bit signed integer type. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_i64 {} + +#[doc(primitive = "i128")] +// +/// The 128-bit signed integer type. +#[stable(feature = "i128", since = "1.26.0")] +mod prim_i128 {} + +#[doc(primitive = "u8")] +// +/// The 8-bit unsigned integer type. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_u8 {} + +#[doc(primitive = "u16")] +// +/// The 16-bit unsigned integer type. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_u16 {} + +#[doc(primitive = "u32")] +// +/// The 32-bit unsigned integer type. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_u32 {} + +#[doc(primitive = "u64")] +// +/// The 64-bit unsigned integer type. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_u64 {} + +#[doc(primitive = "u128")] +// +/// The 128-bit unsigned integer type. +#[stable(feature = "i128", since = "1.26.0")] +mod prim_u128 {} + +#[doc(primitive = "isize")] +// +/// The pointer-sized signed integer type. +/// +/// The size of this primitive is how many bytes it takes to reference any +/// location in memory. For example, on a 32 bit target, this is 4 bytes +/// and on a 64 bit target, this is 8 bytes. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_isize {} + +#[doc(primitive = "usize")] +// +/// The pointer-sized unsigned integer type. +/// +/// The size of this primitive is how many bytes it takes to reference any +/// location in memory. For example, on a 32 bit target, this is 4 bytes +/// and on a 64 bit target, this is 8 bytes. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_usize {} + +#[doc(primitive = "reference")] +#[doc(alias = "&")] +#[doc(alias = "&mut")] +// +/// References, both shared and mutable. +/// +/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut` +/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or +/// [ref](../std/keyword.ref.html) [mut](../std/keyword.mut.html) pattern. +/// +/// For those familiar with pointers, a reference is just a pointer that is assumed to be +/// aligned, not null, and pointing to memory containing a valid value of `T` - for example, +/// &[bool] can only point to an allocation containing the integer values `1` +/// ([`true`](../std/keyword.true.html)) or `0` ([`false`](../std/keyword.false.html)), but +/// creating a &[bool] that points to an allocation containing +/// the value `3` causes undefined behaviour. +/// In fact, [Option]\<&T> has the same memory representation as a +/// nullable but aligned pointer, and can be passed across FFI boundaries as such. +/// +/// In most cases, references can be used much like the original value. Field access, method +/// calling, and indexing work the same (save for mutability rules, of course). In addition, the +/// comparison operators transparently defer to the referent's implementation, allowing references +/// to be compared the same as owned values. +/// +/// References have a lifetime attached to them, which represents the scope for which the borrow is +/// valid. A lifetime is said to "outlive" another one if its representative scope is as long or +/// longer than the other. The `'static` lifetime is the longest lifetime, which represents the +/// total life of the program. For example, string literals have a `'static` lifetime because the +/// text data is embedded into the binary of the program, rather than in an allocation that needs +/// to be dynamically managed. +/// +/// `&mut T` references can be freely coerced into `&T` references with the same referent type, and +/// references with longer lifetimes can be freely coerced into references with shorter ones. +/// +/// Reference equality by address, instead of comparing the values pointed to, is accomplished via +/// implicit reference-pointer coercion and raw pointer equality via [`ptr::eq`], while +/// [`PartialEq`] compares values. +/// +/// ``` +/// use std::ptr; +/// +/// let five = 5; +/// let other_five = 5; +/// let five_ref = &five; +/// let same_five_ref = &five; +/// let other_five_ref = &other_five; +/// +/// assert!(five_ref == same_five_ref); +/// assert!(five_ref == other_five_ref); +/// +/// assert!(ptr::eq(five_ref, same_five_ref)); +/// assert!(!ptr::eq(five_ref, other_five_ref)); +/// ``` +/// +/// For more information on how to use references, see [the book's section on "References and +/// Borrowing"][book-refs]. +/// +/// [book-refs]: ../book/ch04-02-references-and-borrowing.html +/// +/// # Trait implementations +/// +/// The following traits are implemented for all `&T`, regardless of the type of its referent: +/// +/// * [`Copy`] +/// * [`Clone`] \(Note that this will not defer to `T`'s `Clone` implementation if it exists!) +/// * [`Deref`] +/// * [`Borrow`] +/// * [`Pointer`] +/// +/// [`Deref`]: ops::Deref +/// [`Borrow`]: borrow::Borrow +/// [`Pointer`]: fmt::Pointer +/// +/// `&mut T` references get all of the above except `Copy` and `Clone` (to prevent creating +/// multiple simultaneous mutable borrows), plus the following, regardless of the type of its +/// referent: +/// +/// * [`DerefMut`] +/// * [`BorrowMut`] +/// +/// [`DerefMut`]: ops::DerefMut +/// [`BorrowMut`]: borrow::BorrowMut +/// [bool]: prim@bool +/// +/// The following traits are implemented on `&T` references if the underlying `T` also implements +/// that trait: +/// +/// * All the traits in [`std::fmt`] except [`Pointer`] and [`fmt::Write`] +/// * [`PartialOrd`] +/// * [`Ord`] +/// * [`PartialEq`] +/// * [`Eq`] +/// * [`AsRef`] +/// * [`Fn`] \(in addition, `&T` references get [`FnMut`] and [`FnOnce`] if `T: Fn`) +/// * [`Hash`] +/// * [`ToSocketAddrs`] +/// +/// [`std::fmt`]: fmt +/// ['Pointer`]: fmt::Pointer +/// [`Hash`]: hash::Hash +#[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))] +/// +/// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T` +/// implements that trait: +/// +/// * [`AsMut`] +/// * [`FnMut`] \(in addition, `&mut T` references get [`FnOnce`] if `T: FnMut`) +/// * [`fmt::Write`] +/// * [`Iterator`] +/// * [`DoubleEndedIterator`] +/// * [`ExactSizeIterator`] +/// * [`FusedIterator`] +/// * [`TrustedLen`] +/// * [`Send`] \(note that `&T` references only get `Send` if T: [Sync]) +/// * [`io::Write`] +/// * [`Read`] +/// * [`Seek`] +/// * [`BufRead`] +/// +/// [`FusedIterator`]: iter::FusedIterator +/// [`TrustedLen`]: iter::TrustedLen +#[doc = concat!("[`Seek`]: ", include_str!("../primitive_docs/io_seek.md"))] +#[doc = concat!("[`BufRead`]: ", include_str!("../primitive_docs/io_bufread.md"))] +#[doc = concat!("[`Read`]: ", include_str!("../primitive_docs/io_read.md"))] +#[doc = concat!("[`io::Write`]: ", include_str!("../primitive_docs/io_write.md"))] +/// +/// Note that due to method call deref coercion, simply calling a trait method will act like they +/// work on references as well as they do on owned values! The implementations described here are +/// meant for generic contexts, where the final type `T` is a type parameter or otherwise not +/// locally known. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_ref {} + +#[doc(primitive = "fn")] +// +/// Function pointers, like `fn(usize) -> bool`. +/// +/// *See also the traits [`Fn`], [`FnMut`], and [`FnOnce`].* +/// +/// [`Fn`]: ops::Fn +/// [`FnMut`]: ops::FnMut +/// [`FnOnce`]: ops::FnOnce +/// +/// Function pointers are pointers that point to *code*, not data. They can be called +/// just like functions. Like references, function pointers are, among other things, assumed to +/// not be null, so if you want to pass a function pointer over FFI and be able to accommodate null +/// pointers, make your type [`Option`](core::option#options-and-pointers-nullable-pointers) +/// with your required signature. +/// +/// ### Safety +/// +/// Plain function pointers are obtained by casting either plain functions, or closures that don't +/// capture an environment: +/// +/// ``` +/// fn add_one(x: usize) -> usize { +/// x + 1 +/// } +/// +/// let ptr: fn(usize) -> usize = add_one; +/// assert_eq!(ptr(5), 6); +/// +/// let clos: fn(usize) -> usize = |x| x + 5; +/// assert_eq!(clos(5), 10); +/// ``` +/// +/// In addition to varying based on their signature, function pointers come in two flavors: safe +/// and unsafe. Plain `fn()` function pointers can only point to safe functions, +/// while `unsafe fn()` function pointers can point to safe or unsafe functions. +/// +/// ``` +/// fn add_one(x: usize) -> usize { +/// x + 1 +/// } +/// +/// unsafe fn add_one_unsafely(x: usize) -> usize { +/// x + 1 +/// } +/// +/// let safe_ptr: fn(usize) -> usize = add_one; +/// +/// //ERROR: mismatched types: expected normal fn, found unsafe fn +/// //let bad_ptr: fn(usize) -> usize = add_one_unsafely; +/// +/// let unsafe_ptr: unsafe fn(usize) -> usize = add_one_unsafely; +/// let really_safe_ptr: unsafe fn(usize) -> usize = add_one; +/// ``` +/// +/// ### ABI +/// +/// On top of that, function pointers can vary based on what ABI they use. This +/// is achieved by adding the `extern` keyword before the type, followed by the +/// ABI in question. The default ABI is "Rust", i.e., `fn()` is the exact same +/// type as `extern "Rust" fn()`. A pointer to a function with C ABI would have +/// type `extern "C" fn()`. +/// +/// `extern "ABI" { ... }` blocks declare functions with ABI "ABI". The default +/// here is "C", i.e., functions declared in an `extern {...}` block have "C" +/// ABI. +/// +/// For more information and a list of supported ABIs, see [the nomicon's +/// section on foreign calling conventions][nomicon-abi]. +/// +/// [nomicon-abi]: ../nomicon/ffi.html#foreign-calling-conventions +/// +/// ### Variadic functions +/// +/// Extern function declarations with the "C" or "cdecl" ABIs can also be *variadic*, allowing them +/// to be called with a variable number of arguments. Normal Rust functions, even those with an +/// `extern "ABI"`, cannot be variadic. For more information, see [the nomicon's section on +/// variadic functions][nomicon-variadic]. +/// +/// [nomicon-variadic]: ../nomicon/ffi.html#variadic-functions +/// +/// ### Creating function pointers +/// +/// When `bar` is the name of a function, then the expression `bar` is *not* a +/// function pointer. Rather, it denotes a value of an unnameable type that +/// uniquely identifies the function `bar`. The value is zero-sized because the +/// type already identifies the function. This has the advantage that "calling" +/// the value (it implements the `Fn*` traits) does not require dynamic +/// dispatch. +/// +/// This zero-sized type *coerces* to a regular function pointer. For example: +/// +/// ```rust +/// use std::mem; +/// +/// fn bar(x: i32) {} +/// +/// let not_bar_ptr = bar; // `not_bar_ptr` is zero-sized, uniquely identifying `bar` +/// assert_eq!(mem::size_of_val(¬_bar_ptr), 0); +/// +/// let bar_ptr: fn(i32) = not_bar_ptr; // force coercion to function pointer +/// assert_eq!(mem::size_of_val(&bar_ptr), mem::size_of::()); +/// +/// let footgun = &bar; // this is a shared reference to the zero-sized type identifying `bar` +/// ``` +/// +/// The last line shows that `&bar` is not a function pointer either. Rather, it +/// is a reference to the function-specific ZST. `&bar` is basically never what you +/// want when `bar` is a function. +/// +/// ### Traits +/// +/// Function pointers implement the following traits: +/// +/// * [`Clone`] +/// * [`PartialEq`] +/// * [`Eq`] +/// * [`PartialOrd`] +/// * [`Ord`] +/// * [`Hash`] +/// * [`Pointer`] +/// * [`Debug`] +/// +/// [`Hash`]: hash::Hash +/// [`Pointer`]: fmt::Pointer +/// +/// Due to a temporary restriction in Rust's type system, these traits are only implemented on +/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this +/// may change. +/// +/// In addition, function pointers of *any* signature, ABI, or safety are [`Copy`], and all *safe* +/// function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`]. This works because these traits +/// are specially known to the compiler. +#[stable(feature = "rust1", since = "1.0.0")] +mod prim_fn {} diff --git a/library/std/primitive_docs/box_into_raw.md b/library/std/primitive_docs/box_into_raw.md new file mode 100644 index 0000000000000..307b9c85bd67e --- /dev/null +++ b/library/std/primitive_docs/box_into_raw.md @@ -0,0 +1 @@ +Box::into_raw diff --git a/library/std/primitive_docs/fs_file.md b/library/std/primitive_docs/fs_file.md new file mode 100644 index 0000000000000..13e4540835e61 --- /dev/null +++ b/library/std/primitive_docs/fs_file.md @@ -0,0 +1 @@ +fs::File diff --git a/library/std/primitive_docs/io_bufread.md b/library/std/primitive_docs/io_bufread.md new file mode 100644 index 0000000000000..bb688e3a5cc87 --- /dev/null +++ b/library/std/primitive_docs/io_bufread.md @@ -0,0 +1 @@ +io::BufRead diff --git a/library/std/primitive_docs/io_read.md b/library/std/primitive_docs/io_read.md new file mode 100644 index 0000000000000..5118d7c4888ab --- /dev/null +++ b/library/std/primitive_docs/io_read.md @@ -0,0 +1 @@ +io::Read diff --git a/library/std/primitive_docs/io_seek.md b/library/std/primitive_docs/io_seek.md new file mode 100644 index 0000000000000..122e6df77b6d7 --- /dev/null +++ b/library/std/primitive_docs/io_seek.md @@ -0,0 +1 @@ +io::Seek diff --git a/library/std/primitive_docs/io_write.md b/library/std/primitive_docs/io_write.md new file mode 100644 index 0000000000000..15dfc907a6555 --- /dev/null +++ b/library/std/primitive_docs/io_write.md @@ -0,0 +1 @@ +io::Write diff --git a/library/std/primitive_docs/net_tosocketaddrs.md b/library/std/primitive_docs/net_tosocketaddrs.md new file mode 100644 index 0000000000000..a01f318e88771 --- /dev/null +++ b/library/std/primitive_docs/net_tosocketaddrs.md @@ -0,0 +1 @@ +net::ToSocketAddrs diff --git a/library/std/primitive_docs/process_exit.md b/library/std/primitive_docs/process_exit.md new file mode 100644 index 0000000000000..565a71375cd0e --- /dev/null +++ b/library/std/primitive_docs/process_exit.md @@ -0,0 +1 @@ +process::exit diff --git a/library/std/primitive_docs/string_string.md b/library/std/primitive_docs/string_string.md new file mode 100644 index 0000000000000..ce7815ff91b9e --- /dev/null +++ b/library/std/primitive_docs/string_string.md @@ -0,0 +1 @@ +string::String diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index b0ebfe648a523..0de9126dab2fe 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -1,3 +1,6 @@ +// `library/{std,core}/src/primitive_docs.rs` should have the same contents. +// These are different files so that relative links work properly without +// having to have `CARGO_PKG_NAME` set, but conceptually they should always be the same. #[doc(primitive = "bool")] #[doc(alias = "true")] #[doc(alias = "false")] @@ -20,12 +23,12 @@ /// assert!(!bool_val); /// ``` /// -/// [`true`]: keyword.true.html -/// [`false`]: keyword.false.html +/// [`true`]: ../std/keyword.true.html +/// [`false`]: ../std/keyword.false.html /// [`BitAnd`]: ops::BitAnd /// [`BitOr`]: ops::BitOr /// [`Not`]: ops::Not -/// [`if`]: keyword.if.html +/// [`if`]: ../std/keyword.if.html /// /// # Examples /// @@ -103,7 +106,7 @@ mod prim_bool {} /// behaviour of the `!` type - expressions with type `!` will coerce into any other type. /// /// [`u32`]: prim@u32 -/// [`exit`]: process::exit +#[doc = concat!("[`exit`]: ", include_str!("../primitive_docs/process_exit.md"))] /// /// # `!` and generics /// @@ -188,7 +191,7 @@ mod prim_bool {} /// because `!` coerces to `Result` automatically. /// /// [`String::from_str`]: str::FromStr::from_str -/// [`String`]: string::String +#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))] /// [`FromStr`]: str::FromStr /// /// # `!` and traits @@ -264,7 +267,7 @@ mod prim_bool {} /// `impl` for this which simply panics, but the same is true for any type (we could `impl /// Default` for (eg.) [`File`] by just making [`default()`] panic.) /// -/// [`File`]: fs::File +#[doc = concat!("[`File`]: ", include_str!("../primitive_docs/fs_file.md"))] /// [`Debug`]: fmt::Debug /// [`default()`]: Default::default /// @@ -272,7 +275,6 @@ mod prim_bool {} mod prim_never {} #[doc(primitive = "char")] -// /// A character type. /// /// The `char` type represents a single character. More specifically, since @@ -304,7 +306,7 @@ mod prim_never {} /// assert_eq!(5, s.len() * std::mem::size_of::()); /// ``` /// -/// [`String`]: string/struct.String.html +#[doc = concat!("[`String`]: ", include_str!("../primitive_docs/string_string.md"))] /// /// As always, remember that a human intuition for 'character' might not map to /// Unicode's definitions. For example, despite looking similar, the 'Ć©' @@ -499,7 +501,7 @@ mod prim_unit {} /// [`null_mut`]: ptr::null_mut /// [`is_null`]: pointer::is_null /// [`offset`]: pointer::offset -/// [`into_raw`]: Box::into_raw +#[doc = concat!("[`into_raw`]: ", include_str!("../primitive_docs/box_into_raw.md"))] /// [`drop`]: mem::drop /// [`write`]: ptr::write #[stable(feature = "rust1", since = "1.0.0")] @@ -581,9 +583,9 @@ mod prim_pointer {} /// # Editions /// /// Prior to Rust 1.53, arrays did not implement [`IntoIterator`] by value, so the method call -/// `array.into_iter()` auto-referenced into a [slice iterator](slice::iter). Right now, the old behavior -/// is preserved in the 2015 and 2018 editions of Rust for compatibility, ignoring -/// `IntoIterator` by value. In the future, the behavior on the 2015 and 2018 edition +/// `array.into_iter()` auto-referenced into a [slice iterator](slice::iter). Right now, the old +/// behavior is preserved in the 2015 and 2018 editions of Rust for compatibility, ignoring +/// [`IntoIterator`] by value. In the future, the behavior on the 2015 and 2018 edition /// might be made consistent to the behavior of later editions. /// /// ```rust,edition2018 @@ -1042,15 +1044,15 @@ mod prim_usize {} /// References, both shared and mutable. /// /// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut` -/// operators on a value, or by using a [`ref`](keyword.ref.html) or -/// [ref](keyword.ref.html) [mut](keyword.mut.html) pattern. +/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or +/// [ref](../std/keyword.ref.html) [mut](../std/keyword.mut.html) pattern. /// /// For those familiar with pointers, a reference is just a pointer that is assumed to be /// aligned, not null, and pointing to memory containing a valid value of `T` - for example, /// &[bool] can only point to an allocation containing the integer values `1` -/// ([`true`](keyword.true.html)) or `0` ([`false`](keyword.false.html)), but creating a -/// &[bool] that points to an allocation containing the value `3` causes -/// undefined behaviour. +/// ([`true`](../std/keyword.true.html)) or `0` ([`false`](../std/keyword.false.html)), but +/// creating a &[bool] that points to an allocation containing +/// the value `3` causes undefined behaviour. /// In fact, [Option]\<&T> has the same memory representation as a /// nullable but aligned pointer, and can be passed across FFI boundaries as such. /// @@ -1117,6 +1119,7 @@ mod prim_usize {} /// /// [`DerefMut`]: ops::DerefMut /// [`BorrowMut`]: borrow::BorrowMut +/// [bool]: prim@bool /// /// The following traits are implemented on `&T` references if the underlying `T` also implements /// that trait: @@ -1134,7 +1137,7 @@ mod prim_usize {} /// [`std::fmt`]: fmt /// ['Pointer`]: fmt::Pointer /// [`Hash`]: hash::Hash -/// [`ToSocketAddrs`]: net::ToSocketAddrs +#[doc = concat!("[`ToSocketAddrs`]: ", include_str!("../primitive_docs/net_tosocketaddrs.md"))] /// /// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T` /// implements that trait: @@ -1155,9 +1158,10 @@ mod prim_usize {} /// /// [`FusedIterator`]: iter::FusedIterator /// [`TrustedLen`]: iter::TrustedLen -/// [`Seek`]: io::Seek -/// [`BufRead`]: io::BufRead -/// [`Read`]: io::Read +#[doc = concat!("[`Seek`]: ", include_str!("../primitive_docs/io_seek.md"))] +#[doc = concat!("[`BufRead`]: ", include_str!("../primitive_docs/io_bufread.md"))] +#[doc = concat!("[`Read`]: ", include_str!("../primitive_docs/io_read.md"))] +#[doc = concat!("[`io::Write`]: ", include_str!("../primitive_docs/io_write.md"))] /// /// Note that due to method call deref coercion, simply calling a trait method will act like they /// work on references as well as they do on owned values! The implementations described here are diff --git a/src/test/rustdoc/auxiliary/primitive-doc.rs b/src/test/rustdoc/auxiliary/primitive-doc.rs index a5b69740dd447..e8da852a57e63 100644 --- a/src/test/rustdoc/auxiliary/primitive-doc.rs +++ b/src/test/rustdoc/auxiliary/primitive-doc.rs @@ -1,5 +1,8 @@ // compile-flags: --crate-type lib --edition 2018 +#![feature(no_core)] +#![no_core] + #[doc(primitive = "usize")] /// This is the built-in type `usize`. mod usize { diff --git a/src/test/rustdoc/cross-crate-primitive-doc.rs b/src/test/rustdoc/cross-crate-primitive-doc.rs index 05376e4680ec4..709ffa1818066 100644 --- a/src/test/rustdoc/cross-crate-primitive-doc.rs +++ b/src/test/rustdoc/cross-crate-primitive-doc.rs @@ -1,9 +1,11 @@ // aux-build:primitive-doc.rs // compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options - -#![no_std] +#![feature(no_core)] +#![no_core] extern crate primitive_doc; // @has 'cross_crate_primitive_doc/fn.foo.html' '//a[@href="../primitive_doc/primitive.usize.html"]' 'usize' +// @has 'cross_crate_primitive_doc/fn.foo.html' '//a[@href="../primitive_doc/primitive.usize.html"]' 'link' +/// [link](usize) pub fn foo() -> usize { 0 } diff --git a/src/test/rustdoc/primitive/no_std.rs b/src/test/rustdoc/primitive/no_std.rs new file mode 100644 index 0000000000000..8d05a8e6e0136 --- /dev/null +++ b/src/test/rustdoc/primitive/no_std.rs @@ -0,0 +1,5 @@ +#![no_std] + +// @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'u8' +// Link to [u8] +pub fn foo() -> u8 {} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index a1c41eb99810e..35809e599266c 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -46,6 +46,7 @@ pub mod errors; pub mod extdeps; pub mod features; pub mod pal; +pub mod primitive_docs; pub mod style; pub mod target_specific_tests; pub mod ui_tests; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 440c352ea5320..d555f7c8e34ff 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -71,6 +71,7 @@ fn main() { // Checks that only make sense for the std libs. check!(pal, &library_path); + check!(primitive_docs, &library_path); // Checks that need to be done for both the compiler and std libraries. check!(unit_tests, &src_path); diff --git a/src/tools/tidy/src/primitive_docs.rs b/src/tools/tidy/src/primitive_docs.rs new file mode 100644 index 0000000000000..8476650d9b5f7 --- /dev/null +++ b/src/tools/tidy/src/primitive_docs.rs @@ -0,0 +1,17 @@ +//! Tidy check to make sure `library/{std,core}/src/primitive_docs.rs` are the same file. These are +//! different files so that relative links work properly without having to have `CARGO_PKG_NAME` +//! set, but conceptually they should always be the same. + +use std::path::Path; + +pub fn check(library_path: &Path, bad: &mut bool) { + let std_name = "std/src/primitive_docs.rs"; + let core_name = "core/src/primitive_docs.rs"; + let std_contents = std::fs::read_to_string(library_path.join(std_name)) + .unwrap_or_else(|e| panic!("failed to read library/{}: {}", std_name, e)); + let core_contents = std::fs::read_to_string(library_path.join(core_name)) + .unwrap_or_else(|e| panic!("failed to read library/{}: {}", core_name, e)); + if std_contents != core_contents { + tidy_error!(bad, "library/{} and library/{} have different contents", core_name, std_name); + } +} From f78acaee0372aa18ca37ebab8d919acbe997bd1c Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 10 Jul 2021 23:02:10 -0400 Subject: [PATCH 2/5] downgrade some logging --- src/librustdoc/clean/blanket_impl.rs | 13 ++++++++----- src/librustdoc/clean/inline.rs | 4 ++-- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/html/format.rs | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index ff3641d6c9ab0..15d4563ad7461 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -17,7 +17,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { let param_env = self.cx.tcx.param_env(item_def_id); let ty = self.cx.tcx.type_of(item_def_id); - debug!("get_blanket_impls({:?})", ty); + trace!("get_blanket_impls({:?})", ty); let mut impls = Vec::new(); for &trait_def_id in self.cx.tcx.all_traits(()).iter() { if !self.cx.cache.access_levels.is_public(trait_def_id) @@ -28,9 +28,10 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls let trait_impls = self.cx.tcx.trait_impls_of(trait_def_id); for &impl_def_id in trait_impls.blanket_impls() { - debug!( + trace!( "get_blanket_impls: Considering impl for trait '{:?}' {:?}", - trait_def_id, impl_def_id + trait_def_id, + impl_def_id ); let trait_ref = self.cx.tcx.impl_trait_ref(impl_def_id).unwrap(); let is_param = matches!(trait_ref.self_ty().kind(), ty::Param(_)); @@ -50,9 +51,11 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { // FIXME(eddyb) ignoring `obligations` might cause false positives. drop(obligations); - debug!( + trace!( "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}", - param_env, trait_ref, ty + param_env, + trait_ref, + ty ); let predicates = self .cx diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 78e4e4ec116da..40b0175bb1595 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -447,9 +447,9 @@ crate fn build_impl( } let (merged_attrs, cfg) = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs); - debug!("merged_attrs={:?}", merged_attrs); + trace!("merged_attrs={:?}", merged_attrs); - debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id()); + trace!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id()); ret.push(clean::Item::from_def_id_and_attrs_and_parts( did, None, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7ece2acac7a40..6ffdf1df6014a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1406,7 +1406,7 @@ fn normalize(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option> { impl<'tcx> Clean for Ty<'tcx> { fn clean(&self, cx: &mut DocContext<'_>) -> Type { - debug!("cleaning type: {:?}", self); + trace!("cleaning type: {:?}", self); let ty = normalize(cx, self).unwrap_or(self); match *ty.kind() { ty::Never => Never, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index ea0458034899c..cce0006f2be64 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -742,7 +742,7 @@ fn fmt_type<'cx>( use_absolute: bool, cx: &'cx Context<'_>, ) -> fmt::Result { - debug!("fmt_type(t = {:?})", t); + trace!("fmt_type(t = {:?})", t); match *t { clean::Generic(name) => write!(f, "{}", name), From cb7e527692bd01c68f01fd373db0c49dbc8670ce Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 10 Jul 2021 22:25:36 -0400 Subject: [PATCH 3/5] Fix broken handling of primitive items - Fix broken handling of primitive associated items - Remove fragment hack Fixes 83083 - more logging - Update CrateNum hacks The CrateNum has no relation to where in the dependency tree the crate is, only when it's loaded. Explicitly special-case core instead of assuming it will be the first DefId. - Update and add tests - Cache calculation of primitive locations This could possibly be avoided by passing a Cache into collect_intra_doc_links; but that's a much larger change, and doesn't seem valuable other than for this. --- src/librustdoc/clean/types.rs | 119 ++++++++---------- src/librustdoc/formats/cache.rs | 23 ++-- src/librustdoc/html/format.rs | 8 +- src/librustdoc/json/conversions.rs | 4 +- .../passes/collect_intra_doc_links.rs | 59 ++++----- src/test/rustdoc-ui/intra-doc/anchors.rs | 10 -- src/test/rustdoc-ui/intra-doc/anchors.stderr | 35 ++---- src/test/rustdoc/auxiliary/issue-15318.rs | 8 ++ src/test/rustdoc/intra-doc/anchors.rs | 12 ++ .../rustdoc/intra-doc/auxiliary/my-core.rs | 4 + .../intra-doc/prim-methods-external-core.rs | 4 +- .../rustdoc/intra-doc/prim-methods-local.rs | 9 +- src/test/rustdoc/intra-link-prim-self.rs | 4 +- src/test/rustdoc/issue-15318-2.rs | 1 + src/test/rustdoc/no_std-primitive.rs | 6 + src/test/rustdoc/primitive/no_std.rs | 3 +- 16 files changed, 144 insertions(+), 165 deletions(-) create mode 100644 src/test/rustdoc/no_std-primitive.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 4f4952d0afb9d..be1532d450150 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -461,60 +461,20 @@ impl Item { .map_or(&[][..], |v| v.as_slice()) .iter() .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| { - match did { - Some(did) => { - if let Ok((mut href, ..)) = href(*did, cx) { - if let Some(ref fragment) = *fragment { - href.push('#'); - href.push_str(fragment); - } - Some(RenderedLink { - original_text: s.clone(), - new_text: link_text.clone(), - href, - }) - } else { - None - } - } - // FIXME(83083): using fragments as a side-channel for - // primitive names is very unfortunate - None => { - let relative_to = &cx.current; - if let Some(ref fragment) = *fragment { - let url = match cx.cache().extern_locations.get(&self.def_id.krate()) { - Some(&ExternalLocation::Local) => { - if relative_to[0] == "std" { - let depth = relative_to.len() - 1; - "../".repeat(depth) - } else { - let depth = relative_to.len(); - format!("{}std/", "../".repeat(depth)) - } - } - Some(ExternalLocation::Remote(ref s)) => { - format!("{}/std/", s.trim_end_matches('/')) - } - Some(ExternalLocation::Unknown) | None => { - format!("{}/std/", crate::DOC_RUST_LANG_ORG_CHANNEL) - } - }; - // This is a primitive so the url is done "by hand". - let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); - Some(RenderedLink { - original_text: s.clone(), - new_text: link_text.clone(), - href: format!( - "{}primitive.{}.html{}", - url, - &fragment[..tail], - &fragment[tail..] - ), - }) - } else { - panic!("This isn't a primitive?!"); - } + debug!(?did); + if let Ok((mut href, ..)) = href(*did, cx) { + debug!(?href); + if let Some(ref fragment) = *fragment { + href.push('#'); + href.push_str(fragment); } + Some(RenderedLink { + original_text: s.clone(), + new_text: link_text.clone(), + href, + }) + } else { + None } }) .collect() @@ -531,18 +491,10 @@ impl Item { .get(&self.def_id) .map_or(&[][..], |v| v.as_slice()) .iter() - .filter_map(|ItemLink { link: s, link_text, did, fragment }| { - // FIXME(83083): using fragments as a side-channel for - // primitive names is very unfortunate - if did.is_some() || fragment.is_some() { - Some(RenderedLink { - original_text: s.clone(), - new_text: link_text.clone(), - href: String::new(), - }) - } else { - None - } + .map(|ItemLink { link: s, link_text, .. }| RenderedLink { + original_text: s.clone(), + new_text: link_text.clone(), + href: String::new(), }) .collect() } @@ -963,7 +915,7 @@ crate struct Attributes { crate other_attrs: Vec, } -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] /// A link that has not yet been rendered. /// /// This link will be turned into a rendered link by [`Item::links`]. @@ -975,7 +927,7 @@ crate struct ItemLink { /// This may not be the same as `link` if there was a disambiguator /// in an intra-doc link (e.g. \[`fn@f`\]) pub(crate) link_text: String, - pub(crate) did: Option, + pub(crate) did: DefId, /// The url fragment to append to the link pub(crate) fragment: Option, } @@ -1802,6 +1754,39 @@ impl PrimitiveType { Never => sym::never, } } + + /// Returns the DefId of the module with `doc(primitive)` for this primitive type. + /// Panics if there is no such module. + /// + /// This gives precedence to primitives defined in the current crate, and deprioritizes primitives defined in `core`, + /// but otherwise, if multiple crates define the same primitive, there is no guarantee of which will be picked. + /// In particular, if a crate depends on both `std` and another crate that also defines `doc(primitive)`, then + /// it's entirely random whether `std` or the other crate is picked. (no_std crates are usually fine unless multiple dependencies define a primitive.) + crate fn primitive_locations(tcx: TyCtxt<'_>) -> &FxHashMap { + static PRIMITIVE_LOCATIONS: OnceCell> = OnceCell::new(); + PRIMITIVE_LOCATIONS.get_or_init(|| { + let mut primitive_locations = FxHashMap::default(); + // NOTE: technically this misses crates that are only passed with `--extern` and not loaded when checking the crate. + // This is a degenerate case that I don't plan to support. + for &crate_num in tcx.crates(()) { + let e = ExternalCrate { crate_num }; + let crate_name = e.name(tcx); + debug!(?crate_num, ?crate_name); + for &(def_id, prim) in &e.primitives(tcx) { + // HACK: try to link to std instead where possible + if crate_name == sym::core && primitive_locations.get(&prim).is_some() { + continue; + } + primitive_locations.insert(prim, def_id); + } + } + let local_primitives = ExternalCrate { crate_num: LOCAL_CRATE }.primitives(tcx); + for (def_id, prim) in local_primitives { + primitive_locations.insert(prim, def_id); + } + primitive_locations + }) + } } impl From for PrimitiveType { diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 1830909d94460..66fd0d9262d6b 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -6,7 +6,7 @@ use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; -use crate::clean::{self, GetDefId, ItemId}; +use crate::clean::{self, GetDefId, ItemId, PrimitiveType}; use crate::config::RenderOptions; use crate::fold::DocFolder; use crate::formats::item_type::ItemType; @@ -159,17 +159,16 @@ impl Cache { self.external_paths.insert(e.def_id(), (vec![name.to_string()], ItemType::Module)); } - // Cache where all known primitives have their documentation located. - // - // Favor linking to as local extern as possible, so iterate all crates in - // reverse topological order. - for &e in krate.externs.iter().rev() { - for &(def_id, prim) in &e.primitives(tcx) { - self.primitive_locations.insert(prim, def_id); - } - } - for &(def_id, prim) in &krate.primitives { - self.primitive_locations.insert(prim, def_id); + // FIXME: avoid this clone (requires implementing Default manually) + self.primitive_locations = PrimitiveType::primitive_locations(tcx).clone(); + for (prim, &def_id) in &self.primitive_locations { + let crate_name = tcx.crate_name(def_id.krate); + // Recall that we only allow primitive modules to be at the root-level of the crate. + // If that restriction is ever lifted, this will have to include the relative paths instead. + self.external_paths.insert( + def_id, + (vec![crate_name.to_string(), prim.as_sym().to_string()], ItemType::Primitive), + ); } krate = CacheBuilder { tcx, cache: self }.fold_crate(krate); diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index cce0006f2be64..2fde0017dc80c 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -509,7 +509,11 @@ crate fn href_with_root_path( if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] } } - if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private { + if !did.is_local() + && !cache.access_levels.is_public(did) + && !cache.document_private + && !cache.primitive_locations.values().any(|&id| id == did) + { return Err(HrefError::Private); } @@ -517,6 +521,7 @@ crate fn href_with_root_path( let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) { Some(&(ref fqp, shortty)) => (fqp, shortty, { let module_fqp = to_module_fqp(shortty, fqp); + debug!(?fqp, ?shortty, ?module_fqp); href_relative_parts(module_fqp, relative_to) }), None => { @@ -548,6 +553,7 @@ crate fn href_with_root_path( url_parts.insert(0, root); } } + debug!(?url_parts); let last = &fqp.last().unwrap()[..]; let filename; match shortty { diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index e6bbd237cda7d..f8ea7a499b234 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -30,9 +30,7 @@ impl JsonRenderer<'_> { .get(&item.def_id) .into_iter() .flatten() - .filter_map(|clean::ItemLink { link, did, .. }| { - did.map(|did| (link.clone(), from_item_id(did.into()))) - }) + .map(|clean::ItemLink { link, did, .. }| (link.clone(), from_item_id((*did).into()))) .collect(); let docs = item.attrs.collapsed_doc_value(); let attrs = item diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 61f6b4e01c18c..c0c37ee061198 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -14,7 +14,7 @@ use rustc_hir::def::{ }; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_middle::ty::TyCtxt; -use rustc_middle::{bug, ty}; +use rustc_middle::{bug, span_bug, ty}; use rustc_resolve::ParentScope; use rustc_session::lint::Lint; use rustc_span::hygiene::{MacroKind, SyntaxContext}; @@ -98,14 +98,10 @@ impl Res { } } - fn def_id(self) -> DefId { - self.opt_def_id().expect("called def_id() on a primitive") - } - - fn opt_def_id(self) -> Option { + fn def_id(self, tcx: TyCtxt<'_>) -> DefId { match self { - Res::Def(_, id) => Some(id), - Res::Primitive(_) => None, + Res::Def(_, id) => id, + Res::Primitive(prim) => *PrimitiveType::primitive_locations(tcx).get(&prim).unwrap(), } } @@ -237,10 +233,7 @@ enum AnchorFailure { /// link, Rustdoc disallows having a user-specified anchor. /// /// Most of the time this is fine, because you can just link to the page of - /// the item if you want to provide your own anchor. For primitives, though, - /// rustdoc uses the anchor as a side channel to know which page to link to; - /// it doesn't show up in the generated link. Ideally, rustdoc would remove - /// this limitation, allowing you to link to subheaders on primitives. + /// the item if you want to provide your own anchor. RustdocAnchorConflict(Res), } @@ -388,7 +381,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ty::AssocKind::Const => "associatedconstant", ty::AssocKind::Type => "associatedtype", }; - let fragment = format!("{}#{}.{}", prim_ty.as_sym(), out, item_name); + let fragment = format!("{}.{}", out, item_name); (Res::Primitive(prim_ty), fragment, Some((kind.as_def_kind(), item.def_id))) }) }) @@ -475,14 +468,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { return handle_variant(self.cx, res, extra_fragment); } // Not a trait item; just return what we found. - Res::Primitive(ty) => { - if extra_fragment.is_some() { - return Err(ErrorKind::AnchorFailure( - AnchorFailure::RustdocAnchorConflict(res), - )); - } - return Ok((res, Some(ty.as_sym().to_string()))); - } _ => return Ok((res, extra_fragment.clone())), } } @@ -517,6 +502,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let (res, fragment, side_channel) = self.resolve_associated_item(ty_res, item_name, ns, module_id)?; let result = if extra_fragment.is_some() { + // NOTE: can never be a primitive since `side_channel.is_none()` only when `res` + // is a trait (and the side channel DefId is always an associated item). let diag_res = side_channel.map_or(res, |(k, r)| Res::Def(k, r)); Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(diag_res))) } else { @@ -1152,7 +1139,7 @@ impl LinkCollector<'_, '_> { module_id = DefId { krate, index: CRATE_DEF_INDEX }; } - let (mut res, mut fragment) = self.resolve_with_disambiguator_cached( + let (mut res, fragment) = self.resolve_with_disambiguator_cached( ResolutionInfo { module_id, dis: disambiguator, @@ -1174,16 +1161,7 @@ impl LinkCollector<'_, '_> { if let Some(prim) = resolve_primitive(path_str, TypeNS) { // `prim@char` if matches!(disambiguator, Some(Disambiguator::Primitive)) { - if fragment.is_some() { - anchor_failure( - self.cx, - diag_info, - AnchorFailure::RustdocAnchorConflict(prim), - ); - return None; - } res = prim; - fragment = Some(prim.name(self.cx.tcx).to_string()); } else { // `[char]` when a `char` module is in scope let candidates = vec![res, prim]; @@ -1303,12 +1281,17 @@ impl LinkCollector<'_, '_> { } } - Some(ItemLink { link: ori_link.link, link_text, did: None, fragment }) + Some(ItemLink { + link: ori_link.link, + link_text, + did: res.def_id(self.cx.tcx), + fragment, + }) } Res::Def(kind, id) => { verify(kind, id)?; let id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id)); - Some(ItemLink { link: ori_link.link, link_text, did: Some(id), fragment }) + Some(ItemLink { link: ori_link.link, link_text, did: id, fragment }) } } } @@ -2069,8 +2052,11 @@ fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: A diag.span_label(sp, "invalid anchor"); } if let AnchorFailure::RustdocAnchorConflict(Res::Primitive(_)) = failure { - diag.note("this restriction may be lifted in a future release"); - diag.note("see https://github.com/rust-lang/rust/issues/83083 for more information"); + if let Some(sp) = sp { + span_bug!(sp, "anchors should be allowed now"); + } else { + bug!("anchors should be allowed now"); + } } }); } @@ -2198,10 +2184,11 @@ fn handle_variant( use rustc_middle::ty::DefIdTree; if extra_fragment.is_some() { + // NOTE: `res` can never be a primitive since this function is only called when `tcx.def_kind(res) == DefKind::Variant`. return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))); } cx.tcx - .parent(res.def_id()) + .parent(res.def_id(cx.tcx)) .map(|parent| { let parent_def = Res::Def(DefKind::Enum, parent); let variant = cx.tcx.expect_variant_res(res.as_hir_res().unwrap()); diff --git a/src/test/rustdoc-ui/intra-doc/anchors.rs b/src/test/rustdoc-ui/intra-doc/anchors.rs index 6785cb7abeaee..34e11c7c7b7cf 100644 --- a/src/test/rustdoc-ui/intra-doc/anchors.rs +++ b/src/test/rustdoc-ui/intra-doc/anchors.rs @@ -37,13 +37,3 @@ pub fn bar() {} /// Damn enum's variants: [Enum::A#whatever]. //~^ ERROR `Enum::A#whatever` contains an anchor pub fn enum_link() {} - -/// Primitives? -/// -/// [u32#hello] -//~^ ERROR `u32#hello` contains an anchor -pub fn x() {} - -/// [prim@usize#x] -//~^ ERROR `prim@usize#x` contains an anchor -pub mod usize {} diff --git a/src/test/rustdoc-ui/intra-doc/anchors.stderr b/src/test/rustdoc-ui/intra-doc/anchors.stderr index d63e1ee60b3c5..0d226b277535c 100644 --- a/src/test/rustdoc-ui/intra-doc/anchors.stderr +++ b/src/test/rustdoc-ui/intra-doc/anchors.stderr @@ -1,19 +1,3 @@ -error: `prim@usize#x` contains an anchor, but links to builtin types are already anchored - --> $DIR/anchors.rs:47:6 - | -LL | /// [prim@usize#x] - | ^^^^^^^^^^-- - | | - | invalid anchor - | -note: the lint level is defined here - --> $DIR/anchors.rs:1:9 - | -LL | #![deny(rustdoc::broken_intra_doc_links)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this restriction may be lifted in a future release - = note: see https://github.com/rust-lang/rust/issues/83083 for more information - error: `Foo::f#hola` contains an anchor, but links to fields are already anchored --> $DIR/anchors.rs:25:15 | @@ -21,6 +5,12 @@ LL | /// Or maybe [Foo::f#hola]. | ^^^^^^----- | | | invalid anchor + | +note: the lint level is defined here + --> $DIR/anchors.rs:1:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `hello#people#!` contains multiple anchors --> $DIR/anchors.rs:31:28 @@ -38,16 +28,5 @@ LL | /// Damn enum's variants: [Enum::A#whatever]. | | | invalid anchor -error: `u32#hello` contains an anchor, but links to builtin types are already anchored - --> $DIR/anchors.rs:43:6 - | -LL | /// [u32#hello] - | ^^^------ - | | - | invalid anchor - | - = note: this restriction may be lifted in a future release - = note: see https://github.com/rust-lang/rust/issues/83083 for more information - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors diff --git a/src/test/rustdoc/auxiliary/issue-15318.rs b/src/test/rustdoc/auxiliary/issue-15318.rs index 83cc31b587c29..0e1977182ceac 100644 --- a/src/test/rustdoc/auxiliary/issue-15318.rs +++ b/src/test/rustdoc/auxiliary/issue-15318.rs @@ -1,6 +1,14 @@ // compile-flags: -Cmetadata=aux #![doc(html_root_url = "http://example.com/")] +#![feature(lang_items)] +#![no_std] + +#[lang = "eh_personality"] +fn foo() {} + +#[panic_handler] +fn bar(_: &core::panic::PanicInfo) -> ! { loop {} } /// dox #[doc(primitive = "pointer")] diff --git a/src/test/rustdoc/intra-doc/anchors.rs b/src/test/rustdoc/intra-doc/anchors.rs index 8ec1a7b4f9056..3d4c464960bbc 100644 --- a/src/test/rustdoc/intra-doc/anchors.rs +++ b/src/test/rustdoc/intra-doc/anchors.rs @@ -10,3 +10,15 @@ pub struct Something; /// /// To link to [Something#Anchor!] pub struct SomeOtherType; + +/// Primitives? +/// +/// [u32#hello] +// @has anchors/fn.x.html +// @has - '//a/@href' '{{channel}}/std/primitive.u32.html#hello' +pub fn x() {} + +/// [prim@usize#x] +// @has anchors/usize/index.html +// @has - '//a/@href' '{{channel}}/std/primitive.usize.html#x' +pub mod usize {} diff --git a/src/test/rustdoc/intra-doc/auxiliary/my-core.rs b/src/test/rustdoc/intra-doc/auxiliary/my-core.rs index 54e986be9eccf..92cfd46188b01 100644 --- a/src/test/rustdoc/intra-doc/auxiliary/my-core.rs +++ b/src/test/rustdoc/intra-doc/auxiliary/my-core.rs @@ -2,6 +2,10 @@ #![no_core] #![crate_type="rlib"] +#[doc(primitive = "char")] +/// Some char docs +mod char {} + #[lang = "char"] impl char { pub fn len_utf8(self) -> usize { diff --git a/src/test/rustdoc/intra-doc/prim-methods-external-core.rs b/src/test/rustdoc/intra-doc/prim-methods-external-core.rs index 9347d7bb42819..5a92a28556ede 100644 --- a/src/test/rustdoc/intra-doc/prim-methods-external-core.rs +++ b/src/test/rustdoc/intra-doc/prim-methods-external-core.rs @@ -9,8 +9,8 @@ #![crate_type = "rlib"] // @has prim_methods_external_core/index.html -// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html"]' 'char' -// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8' +// @has - '//*[@id="main"]//a[@href="../my_core/primitive.char.html"]' 'char' +// @has - '//*[@id="main"]//a[@href="../my_core/primitive.char.html#method.len_utf8"]' 'char::len_utf8' //! A [`char`] and its [`char::len_utf8`]. diff --git a/src/test/rustdoc/intra-doc/prim-methods-local.rs b/src/test/rustdoc/intra-doc/prim-methods-local.rs index 124faa9a636ff..cfb3c3842ab16 100644 --- a/src/test/rustdoc/intra-doc/prim-methods-local.rs +++ b/src/test/rustdoc/intra-doc/prim-methods-local.rs @@ -5,10 +5,13 @@ // @has prim_methods_local/index.html -// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html"]' 'char' -// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8' +// @has - '//*[@id="main"]//a[@href="primitive.char.html"]' 'char' +// @has - '//*[@id="main"]//a[@href="primitive.char.html#method.len_utf8"]' 'char::len_utf8' -//! A [`char`] and its [`char::len_utf8`]. +//! A [prim@`char`] and its [`char::len_utf8`]. + +#[doc(primitive = "char")] +mod char {} #[lang = "char"] impl char { diff --git a/src/test/rustdoc/intra-link-prim-self.rs b/src/test/rustdoc/intra-link-prim-self.rs index 4744c84b6226d..8c47f7ef77e51 100644 --- a/src/test/rustdoc/intra-link-prim-self.rs +++ b/src/test/rustdoc/intra-link-prim-self.rs @@ -7,8 +7,8 @@ /// [Self::f] /// [Self::MAX] // @has intra_link_prim_self/primitive.usize.html -// @has - '//a[@href="{{channel}}/std/primitive.usize.html#method.f"]' 'Self::f' -// @has - '//a[@href="{{channel}}/std/primitive.usize.html#associatedconstant.MAX"]' 'Self::MAX' +// @has - '//a[@href="primitive.usize.html#method.f"]' 'Self::f' +// @has - '//a[@href="primitive.usize.html#associatedconstant.MAX"]' 'Self::MAX' impl usize { /// Some docs pub fn f() {} diff --git a/src/test/rustdoc/issue-15318-2.rs b/src/test/rustdoc/issue-15318-2.rs index 2af811ad5bbe8..f7f5052a36dd3 100644 --- a/src/test/rustdoc/issue-15318-2.rs +++ b/src/test/rustdoc/issue-15318-2.rs @@ -1,5 +1,6 @@ // aux-build:issue-15318.rs // ignore-cross-compile +#![no_std] extern crate issue_15318; diff --git a/src/test/rustdoc/no_std-primitive.rs b/src/test/rustdoc/no_std-primitive.rs new file mode 100644 index 0000000000000..22fd392dd36c3 --- /dev/null +++ b/src/test/rustdoc/no_std-primitive.rs @@ -0,0 +1,6 @@ +#![no_std] + +/// Link to [intra-doc link][u8] +// @has 'no_std_primitive/fn.foo.html' '//a[@href="{{channel}}/core/primitive.u8.html"]' 'intra-doc link' +// @has - '//a[@href="{{channel}}/core/primitive.u8.html"]' 'u8' +pub fn foo() -> u8 {} diff --git a/src/test/rustdoc/primitive/no_std.rs b/src/test/rustdoc/primitive/no_std.rs index 8d05a8e6e0136..52806bd94f9a0 100644 --- a/src/test/rustdoc/primitive/no_std.rs +++ b/src/test/rustdoc/primitive/no_std.rs @@ -1,5 +1,6 @@ #![no_std] // @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'u8' -// Link to [u8] +// @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'primitive link' +/// Link to [primitive link][u8] pub fn foo() -> u8 {} From 7b469202183c1b29db813be42614ef994c099aca Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 13 Jul 2021 17:41:03 -0400 Subject: [PATCH 4/5] Fix linkcheck issues Most of these are because alloc uses `#[lang_item]` to define methods, but core documents primitives before those methods are available. - Fix rustdoc-js-std test For some reason this change made CStr not show up in the results for `str,u8`. Since it still shows up for str, and since it wasn't a great match for that query anyway, I think this is ok to let slide. - Add test that all primitives can be linked to - Enable `doc(primitive)` in `core` as well - Add linkcheck exception specifically for Windows Ideally this would be done automatically by the linkchecker by replacing `\\` with forward slashes, but this PR is already a ton of work ... - Don't forcibly fail linkchecking if there's a broken intra-doc link on Windows Previously, it would exit with a hard error if a missing file had `::` in it. This changes it to report a missing file instead, which allows adding an exception. --- library/core/src/char/methods.rs | 2 +- library/core/src/slice/mod.rs | 6 +++--- src/librustdoc/clean/types.rs | 2 +- src/test/rustdoc-js-std/multi-query.js | 7 ++++--- src/test/rustdoc/primitive/no_std.rs | 10 ++++++++++ src/tools/linkchecker/main.rs | 26 ++++++++++++++++++++++++++ 6 files changed, 45 insertions(+), 8 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 07ffd06511885..b1dd6aef97493 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -24,7 +24,7 @@ impl char { /// decoding error. /// /// It can occur, for example, when giving ill-formed UTF-8 bytes to - /// [`String::from_utf8_lossy`](string/struct.String.html#method.from_utf8_lossy). + /// [`String::from_utf8_lossy`](../std/string/struct.String.html#method.from_utf8_lossy). #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const REPLACEMENT_CHARACTER: char = '\u{FFFD}'; diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index b67038a7d5c2c..a04e479e8570d 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2257,9 +2257,9 @@ impl [T] { /// assert!(match r { Ok(1..=4) => true, _ => false, }); /// ``` // Lint rustdoc::broken_intra_doc_links is allowed as `slice::sort_by_key` is - // in crate `alloc`, and as such doesn't exists yet when building `core`. - // links to downstream crate: #74481. Since primitives are only documented in - // libstd (#73423), this never leads to broken links in practice. + // in crate `alloc`, and as such doesn't exists yet when building `core`: #74481. + // This breaks links when slice is displayed in core, but changing it to use relative links + // would break when the item is re-exported. So allow the core links to be broken for now. #[allow(rustdoc::broken_intra_doc_links)] #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] #[inline] diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index be1532d450150..454602481e3c5 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1774,7 +1774,7 @@ impl PrimitiveType { debug!(?crate_num, ?crate_name); for &(def_id, prim) in &e.primitives(tcx) { // HACK: try to link to std instead where possible - if crate_name == sym::core && primitive_locations.get(&prim).is_some() { + if crate_name == sym::core && primitive_locations.contains_key(&prim) { continue; } primitive_locations.insert(prim, def_id); diff --git a/src/test/rustdoc-js-std/multi-query.js b/src/test/rustdoc-js-std/multi-query.js index 01e54065189c5..1c92d019606b6 100644 --- a/src/test/rustdoc-js-std/multi-query.js +++ b/src/test/rustdoc-js-std/multi-query.js @@ -2,8 +2,9 @@ const QUERY = 'str,u8'; const EXPECTED = { 'others': [ - { 'path': 'std', 'name': 'str' }, - { 'path': 'std', 'name': 'u8' }, - { 'path': 'std::ffi', 'name': 'CStr' }, + { 'path': 'std', 'name': 'str', 'href': '../std/primitive.str.html' }, + { 'path': 'std', 'name': 'u8', 'href': '../std/primitive.u8.html' }, + { 'path': 'std', 'name': 'str', 'href': '../std/str/index.html' }, + { 'path': 'std', 'name': 'u8', 'href': '../std/u8/index.html' }, ], }; diff --git a/src/test/rustdoc/primitive/no_std.rs b/src/test/rustdoc/primitive/no_std.rs index 52806bd94f9a0..f0f70cb6c1881 100644 --- a/src/test/rustdoc/primitive/no_std.rs +++ b/src/test/rustdoc/primitive/no_std.rs @@ -1,6 +1,16 @@ #![no_std] +#![deny(warnings)] +#![deny(rustdoc::broken_intra_doc_links)] // @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'u8' // @has no_std/fn.foo.html '//a/[@href="{{channel}}/core/primitive.u8.html"]' 'primitive link' /// Link to [primitive link][u8] pub fn foo() -> u8 {} + +// Test that all primitives can be linked to. +/// [isize] [i8] [i16] [i32] [i64] [i128] +/// [usize] [u8] [u16] [u32] [u64] [u128] +/// [f32] [f64] +/// [char] [bool] [str] [slice] [array] [tuple] [unit] +/// [pointer] [reference] [fn] [never] +pub fn bar() {} diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 8cbe0a0c2e8e1..94ebbb33e8d8f 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -30,6 +30,7 @@ use regex::Regex; // If at all possible you should use intra-doc links to avoid linkcheck issues. These // are cases where that does not work // [(generated_documentation_page, &[broken_links])] +#[rustfmt::skip] const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ // These try to link to std::collections, but are defined in alloc // https://github.com/rust-lang/rust/issues/74481 @@ -37,6 +38,19 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ ("std/collections/btree_set/struct.BTreeSet.html", &["#insert-and-complex-keys"]), ("alloc/collections/btree_map/struct.BTreeMap.html", &["#insert-and-complex-keys"]), ("alloc/collections/btree_set/struct.BTreeSet.html", &["#insert-and-complex-keys"]), + + // These try to link to various things in std, but are defined in core. + // The docs in std::primitive use proper intra-doc links, so these seem fine to special-case. + // Most these are broken because liballoc uses `#[lang_item]` magic to define things on + // primitives that aren't available in core. + ("alloc/slice/trait.Join.html", &["#method.join"]), + ("alloc/slice/trait.Concat.html", &["#method.concat"]), + ("alloc/slice/index.html", &["#method.concat", "#method.join"]), + ("alloc/vec/struct.Vec.html", &["#method.sort_by_key", "#method.sort_by_cached_key"]), + ("core/primitive.str.html", &["#method.to_ascii_uppercase", "#method.to_ascii_lowercase"]), + ("core/primitive.slice.html", &["#method.to_ascii_uppercase", "#method.to_ascii_lowercase", + "core/slice::sort_by_key", "core\\slice::sort_by_key", + "#method.sort_by_cached_key"]), ]; #[rustfmt::skip] @@ -376,6 +390,10 @@ impl Checker { /// Load a file from disk, or from the cache if available. fn load_file(&mut self, file: &Path, report: &mut Report) -> (String, &FileEntry) { + // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- + #[cfg(windows)] + const ERROR_INVALID_NAME: i32 = 123; + let pretty_path = file.strip_prefix(&self.root).unwrap_or(&file).to_str().unwrap().to_string(); @@ -392,6 +410,14 @@ impl Checker { } Err(e) if e.kind() == ErrorKind::NotFound => FileEntry::Missing, Err(e) => { + // If a broken intra-doc link contains `::`, on windows, it will cause `ERROR_INVALID_NAME` rather than `NotFound`. + // Explicitly check for that so that the broken link can be allowed in `LINKCHECK_EXCEPTIONS`. + #[cfg(windows)] + if e.raw_os_error() == Some(ERROR_INVALID_NAME) + && file.as_os_str().to_str().map_or(false, |s| s.contains("::")) + { + return FileEntry::Missing; + } panic!("unexpected read error for {}: {}", file.display(), e); } }); From 86fd2505c2e10fa27a49a81978cdcbabebb581a1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 25 Aug 2021 17:57:06 +0200 Subject: [PATCH 5/5] Fix `no_core` and `no_std` rustdoc tests on Windows This prevents the following (very strange) errors: ``` error: linking with `link.exe` failed: exit code: 1120 | = note: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Tools\\MSVC\\14.29.30133\\bin\\HostX64\\x86\\link.exe" "/DEF:C:\\Users\\runneradmin\\AppData\\Local\\Temp\\rustcJih4fa\\lib.def" "/NOLOGO" "/LARGEADDRESSAWARE" "/SAFESEH" "D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary\\issue-15318.issue_15318.0a2a8554-cgu.0.rcgu.o" "D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary\\issue-15318.1na9aylmt25n6w3f.rcgu.o" "/LIBPATH:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\native\\rust-test-helpers" "/LIBPATH:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary" "/LIBPATH:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib" "vcruntime.lib" "ucrt.lib" "/WHOLEARCHIVE:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib\\librustc_std_workspace_core-78744e1360284b1e.rlib" "/WHOLEARCHIVE:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib\\libcore-a900fa3d16956226.rlib" "D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib\\libcompiler_builtins-eb97e6b4dfd2f421.rlib" "/NXCOMPAT" "/LIBPATH:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\i686-pc-windows-msvc\\lib" "/OUT:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary\\issue_15318.dll" "/OPT:REF,ICF" "/DLL" "/IMPLIB:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\test\\rustdoc\\issue-15318-2\\auxiliary\\issue_15318.dll.lib" "/DEBUG" "/NATVIS:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\etc\\intrinsic.natvis" "/NATVIS:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\etc\\liballoc.natvis" "/NATVIS:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\etc\\libcore.natvis" "/NATVIS:D:\\a\\rust\\rust\\build\\i686-pc-windows-msvc\\stage2\\lib\\rustlib\\etc\\libstd.natvis" = note: LINK : warning LNK4216: Exported entry point __DllMainCRTStartup@12 Creating library D:\a\rust\rust\build\i686-pc-windows-msvc\test\rustdoc\issue-15318-2\auxiliary\issue_15318.dll.lib and object D:\a\rust\rust\build\i686-pc-windows-msvc\test\rustdoc\issue-15318-2\auxiliary\issue_15318.dll.exp libcore-a900fa3d16956226.rlib(core-a900fa3d16956226.core.95dedc69-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __aulldiv referenced in function __ZN4core3num7dec2flt7decimal7Decimal10left_shift17hfb9b6c23d6ff0383E libcompiler_builtins-eb97e6b4dfd2f421.rlib(compiler_builtins-eb97e6b4dfd2f421.compiler_builtins.a5ef280a-cgu.51.rcgu.o) : error LNK2001: unresolved external symbol __aulldiv libcore-a900fa3d16956226.rlib(core-a900fa3d16956226.core.95dedc69-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __aullrem referenced in function __ZN4core3fmt3num14parse_u64_into17h90eb20517ec3bd86E D:\a\rust\rust\build\i686-pc-windows-msvc\test\rustdoc\issue-15318-2\auxiliary\issue_15318.dll : fatal error LNK1120: 2 unresolved externals ``` --- src/test/rustdoc/auxiliary/issue-15318.rs | 3 ++- src/test/rustdoc/cross-crate-primitive-doc.rs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/rustdoc/auxiliary/issue-15318.rs b/src/test/rustdoc/auxiliary/issue-15318.rs index 0e1977182ceac..695fa58ef1d55 100644 --- a/src/test/rustdoc/auxiliary/issue-15318.rs +++ b/src/test/rustdoc/auxiliary/issue-15318.rs @@ -1,5 +1,6 @@ +// no-prefer-dynamic // compile-flags: -Cmetadata=aux - +#![crate_type = "rlib"] #![doc(html_root_url = "http://example.com/")] #![feature(lang_items)] #![no_std] diff --git a/src/test/rustdoc/cross-crate-primitive-doc.rs b/src/test/rustdoc/cross-crate-primitive-doc.rs index 709ffa1818066..120b6e9747f4a 100644 --- a/src/test/rustdoc/cross-crate-primitive-doc.rs +++ b/src/test/rustdoc/cross-crate-primitive-doc.rs @@ -1,5 +1,7 @@ // aux-build:primitive-doc.rs // compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options +// ignore-windows + #![feature(no_core)] #![no_core]