From f45bdcce69c754a3e5308b71c810e233a100f65c Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 14:20:51 -0500 Subject: [PATCH 01/62] Implement size_hint for BufReader --- library/std/src/io/mod.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index db3b0e2628f2a..0fc99baa66639 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2216,7 +2216,11 @@ impl Read for Chain { unsafe fn initializer(&self) -> Initializer { let initializer = self.first.initializer(); - if initializer.should_initialize() { initializer } else { self.second.initializer() } + if initializer.should_initialize() { + initializer + } else { + self.second.initializer() + } } } @@ -2235,7 +2239,11 @@ impl BufRead for Chain { } fn consume(&mut self, amt: usize) { - if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) } + if !self.done_first { + self.first.consume(amt) + } else { + self.second.consume(amt) + } } } @@ -2465,6 +2473,17 @@ impl Iterator for Bytes { }; } } + + default fn size_hint(&self) -> (usize, Option) { + (0, None) + } +} + +#[stable(feature = "bufreader_size_hint", since = "1.51.0")] +impl Iterator for Bytes> { + fn size_hint(&self) -> (usize, Option) { + (self.inner.buffer().len(), None) + } } /// An iterator over the contents of an instance of `BufRead` split on a From c3e47d974ab1c1c4d4b00989631586e60d78b554 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 14:43:20 -0500 Subject: [PATCH 02/62] Fix implementation to specialize --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 0fc99baa66639..e02ee58cdbcb2 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2480,7 +2480,7 @@ impl Iterator for Bytes { } #[stable(feature = "bufreader_size_hint", since = "1.51.0")] -impl Iterator for Bytes> { +impl Iterator for Bytes> { fn size_hint(&self) -> (usize, Option) { (self.inner.buffer().len(), None) } From fa76db3104c11416f125be7fcc27d2435dcb84c5 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 17:37:29 -0500 Subject: [PATCH 03/62] Use helper trait to follow min_specialization rules --- library/std/src/io/mod.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index e02ee58cdbcb2..c98f8e383eda0 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2459,7 +2459,7 @@ pub struct Bytes { } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Bytes { +impl Iterator for Bytes { type Item = Result; fn next(&mut self) -> Option> { @@ -2475,14 +2475,34 @@ impl Iterator for Bytes { } default fn size_hint(&self) -> (usize, Option) { - (0, None) + self.inner.size_hint() } } #[stable(feature = "bufreader_size_hint", since = "1.51.0")] -impl Iterator for Bytes> { +trait SizeHint { + fn lower_bound(&self) -> usize; + + fn upper_bound(&self) -> Option { + None + } + fn size_hint(&self) -> (usize, Option) { - (self.inner.buffer().len(), None) + (self.lower_bound(), self.upper_bound()) + } +} + +#[stable(feature = "bufreader_size_hint", since = "1.51.0")] +impl SizeHint for T { + fn lower_bound(&self) -> usize { + 0 + } +} + +#[stable(feature = "bufreader_size_hint", since = "1.51.0")] +impl SizeHint for BufReader { + fn lower_bound(&self) -> usize { + self.buffer().len() } } From 11c49f6a2a6cc7aa5b0223c4d2ec59fb4fc9b739 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 17:41:42 -0500 Subject: [PATCH 04/62] Add missing generic --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index c98f8e383eda0..ae01972339498 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2493,7 +2493,7 @@ trait SizeHint { } #[stable(feature = "bufreader_size_hint", since = "1.51.0")] -impl SizeHint for T { +impl SizeHint for T { fn lower_bound(&self) -> usize { 0 } From 260a270f7c531a3b8f951b69c9e793e3afd67c89 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 17:47:25 -0500 Subject: [PATCH 05/62] Move default to trait definition --- library/std/src/io/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index ae01972339498..cd54b8cc5699e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2481,7 +2481,9 @@ impl Iterator for Bytes { #[stable(feature = "bufreader_size_hint", since = "1.51.0")] trait SizeHint { - fn lower_bound(&self) -> usize; + fn lower_bound(&self) -> usize { + 0 + } fn upper_bound(&self) -> Option { None @@ -2493,11 +2495,7 @@ trait SizeHint { } #[stable(feature = "bufreader_size_hint", since = "1.51.0")] -impl SizeHint for T { - fn lower_bound(&self) -> usize { - 0 - } -} +impl SizeHint for T; #[stable(feature = "bufreader_size_hint", since = "1.51.0")] impl SizeHint for BufReader { From 5f60a3048ed0d6b2b56e253769fbd6593ccb1d71 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 17:52:55 -0500 Subject: [PATCH 06/62] Fix incorrect token --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index cd54b8cc5699e..53da24409459d 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2495,7 +2495,7 @@ trait SizeHint { } #[stable(feature = "bufreader_size_hint", since = "1.51.0")] -impl SizeHint for T; +impl SizeHint for T {} #[stable(feature = "bufreader_size_hint", since = "1.51.0")] impl SizeHint for BufReader { From eea99f491b6000a836a8887a29d1ce85c666fe47 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 18:45:00 -0500 Subject: [PATCH 07/62] Add default keyword for specialization --- library/std/src/io/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 53da24409459d..712e808a02474 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2481,10 +2481,6 @@ impl Iterator for Bytes { #[stable(feature = "bufreader_size_hint", since = "1.51.0")] trait SizeHint { - fn lower_bound(&self) -> usize { - 0 - } - fn upper_bound(&self) -> Option { None } @@ -2495,7 +2491,11 @@ trait SizeHint { } #[stable(feature = "bufreader_size_hint", since = "1.51.0")] -impl SizeHint for T {} +impl SizeHint for T { + default fn lower_bound(&self) -> usize { + 0 + } +} #[stable(feature = "bufreader_size_hint", since = "1.51.0")] impl SizeHint for BufReader { From 7e56637c749c4427ce0456022ea84e8393e5b0f7 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 19:29:49 -0500 Subject: [PATCH 08/62] Add back lower_bound as memeber --- library/std/src/io/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 712e808a02474..018fe1c52bfb9 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2481,6 +2481,8 @@ impl Iterator for Bytes { #[stable(feature = "bufreader_size_hint", since = "1.51.0")] trait SizeHint { + fn lower_bound(&self) -> usize { + fn upper_bound(&self) -> Option { None } From 442de9ac4531cc31955b62fbbebf80d2b99b4bb0 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 19:43:52 -0500 Subject: [PATCH 09/62] Fix semicolon --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 018fe1c52bfb9..e9c99e5a62738 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2481,7 +2481,7 @@ impl Iterator for Bytes { #[stable(feature = "bufreader_size_hint", since = "1.51.0")] trait SizeHint { - fn lower_bound(&self) -> usize { + fn lower_bound(&self) -> usize; fn upper_bound(&self) -> Option { None From 1190321b7633369ec9e678d2fe4ac986b8157cc1 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 20:02:55 -0500 Subject: [PATCH 10/62] Remove exposing private trait --- library/std/src/io/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index e9c99e5a62738..a05adedaed2d6 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2459,7 +2459,7 @@ pub struct Bytes { } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Bytes { +impl Iterator for Bytes { type Item = Result; fn next(&mut self) -> Option> { @@ -2475,7 +2475,7 @@ impl Iterator for Bytes { } default fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() + (&self.inner as &SizeHint).size_hint() } } From 421b40cd6a72f039b319f8da6b80c67c385e9965 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 20:09:43 -0500 Subject: [PATCH 11/62] Add dyn for SizeHint cast --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index a05adedaed2d6..e81bfd27a9ad1 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2475,7 +2475,7 @@ impl Iterator for Bytes { } default fn size_hint(&self) -> (usize, Option) { - (&self.inner as &SizeHint).size_hint() + (&self.inner as &dyn SizeHint).size_hint() } } From 265db94dc29b4e089e134795eac24b1b0a2faab1 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 20:25:32 -0500 Subject: [PATCH 12/62] Fix formatting --- library/std/src/io/mod.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index e81bfd27a9ad1..766da2ed6264f 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2216,11 +2216,7 @@ impl Read for Chain { unsafe fn initializer(&self) -> Initializer { let initializer = self.first.initializer(); - if initializer.should_initialize() { - initializer - } else { - self.second.initializer() - } + if initializer.should_initialize() { initializer } else { self.second.initializer() } } } @@ -2239,11 +2235,7 @@ impl BufRead for Chain { } fn consume(&mut self, amt: usize) { - if !self.done_first { - self.first.consume(amt) - } else { - self.second.consume(amt) - } + if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) } } } From 93870c8d5ff5750db2e138f35f783078e8ad8dd0 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 20:40:10 -0500 Subject: [PATCH 13/62] Remove stable annotation --- library/std/src/io/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 766da2ed6264f..754e22f228821 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2471,7 +2471,6 @@ impl Iterator for Bytes { } } -#[stable(feature = "bufreader_size_hint", since = "1.51.0")] trait SizeHint { fn lower_bound(&self) -> usize; @@ -2484,14 +2483,12 @@ trait SizeHint { } } -#[stable(feature = "bufreader_size_hint", since = "1.51.0")] impl SizeHint for T { default fn lower_bound(&self) -> usize { 0 } } -#[stable(feature = "bufreader_size_hint", since = "1.51.0")] impl SizeHint for BufReader { fn lower_bound(&self) -> usize { self.buffer().len() From 7869371bf1e6ab88bb61771c5dca217cc92ba0c9 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Fri, 15 Jan 2021 20:45:43 -0500 Subject: [PATCH 14/62] Remove unnecessary default keyword --- library/std/src/io/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 754e22f228821..d7fd811cdb64b 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2466,7 +2466,7 @@ impl Iterator for Bytes { } } - default fn size_hint(&self) -> (usize, Option) { + fn size_hint(&self) -> (usize, Option) { (&self.inner as &dyn SizeHint).size_hint() } } @@ -2474,9 +2474,7 @@ impl Iterator for Bytes { trait SizeHint { fn lower_bound(&self) -> usize; - fn upper_bound(&self) -> Option { - None - } + fn upper_bound(&self) -> Option; fn size_hint(&self) -> (usize, Option) { (self.lower_bound(), self.upper_bound()) @@ -2487,6 +2485,10 @@ impl SizeHint for T { default fn lower_bound(&self) -> usize { 0 } + + default fn upper_bound(&self) -> Option { + None + } } impl SizeHint for BufReader { From c8e0f8aaa3e8a21158b596814d8a53cfe604a294 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Sat, 16 Jan 2021 10:42:52 -0500 Subject: [PATCH 15/62] Use fully qualified syntax to avoid dyn --- library/std/src/io/mod.rs | 2 +- library/std/src/io/tests.rs | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index d7fd811cdb64b..3839f7cd28d17 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2467,7 +2467,7 @@ impl Iterator for Bytes { } fn size_hint(&self) -> (usize, Option) { - (&self.inner as &dyn SizeHint).size_hint() + SizeHint::size_hint(&self.inner) } } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index f176c2f088cb3..03ed8ba74c520 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -1,7 +1,7 @@ use super::{repeat, Cursor, SeekFrom}; use crate::cmp::{self, min}; use crate::io::{self, IoSlice, IoSliceMut}; -use crate::io::{BufRead, Read, Seek, Write}; +use crate::io::{BufRead, BufReader, Read, Seek, Write}; use crate::ops::Deref; #[test] @@ -198,6 +198,26 @@ fn chain_bufread() { cmp_bufread(chain1, chain2, &testdata[..]); } +#[test] +fn bufreader_size_hint() { + let testdata = b"ABCDEFGHIJKL"; + let mut buf_reader = BufReader::new(&testdata[..]); + assert_eq!(buf_reader.buffer().len(), 0); + + let buffer = buf_reader.fill_buf().unwrap(); + let buffer_length = buffer.len(); + + // Check that size hint matches buffer contents + let mut buffered_bytes = buf_reader.bytes(); + let (lower_bound, _upper_bound) = buffered_bytes.size_hint(); + assert_eq!(lower_bound, buffer_length); + + // Check that size hint matches buffer contents after advancing + buffered_bytes.next().unwrap().unwrap(); + let (lower_bound, _upper_bound) = buffered_bytes.size_hint(); + assert_eq!(lower_bound, buffer_length - 1); +} + #[test] fn chain_zero_length_read_is_not_eof() { let a = b"A"; From 96255f82c9209fc2366ef32937c2bb53817687c1 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Sat, 16 Jan 2021 15:03:58 -0500 Subject: [PATCH 16/62] Implement SizeHint trait for BufReader, Emtpy, and Chain --- library/std/src/io/buffered/bufreader.rs | 9 ++++++++- library/std/src/io/mod.rs | 20 ++++++++++++++------ library/std/src/io/util.rs | 8 +++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 987371f50ec22..e737a104014f4 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -1,6 +1,6 @@ use crate::cmp; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE}; +use crate::io::{self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE}; /// The `BufReader` struct adds buffering to any reader. /// @@ -435,3 +435,10 @@ impl Seek for BufReader { }) } } + +impl SizeHint for BufReader { + fn lower_bound(&self) -> usize { + self.buffer().len() + } +} + diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 3839f7cd28d17..08b258bb816ec 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2239,6 +2239,20 @@ impl BufRead for Chain { } } +impl SizeHint for Chain { + fn lower_bound(&self) -> usize { + SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second) + } + + fn upper_bound(&self) -> Option { + match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) { + (Some(first), Some(second)) => Some(first + second), + _ => None, + } + } +} + + /// Reader adaptor which limits the bytes read from an underlying reader. /// /// This struct is generally created by calling [`take`] on a reader. @@ -2491,12 +2505,6 @@ impl SizeHint for T { } } -impl SizeHint for BufReader { - fn lower_bound(&self) -> usize { - self.buffer().len() - } -} - /// An iterator over the contents of an instance of `BufRead` split on a /// particular byte. /// diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index e43ce4cdb4b8e..7de37c67abebc 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -4,7 +4,7 @@ mod tests; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write}; /// A reader which is always at EOF. /// @@ -80,6 +80,12 @@ impl fmt::Debug for Empty { } } +impl SizeHint for Empty { + fn upper_bound(&self) -> Option { + Some(0) + } +} + /// A reader which yields one byte over and over and over and over and over and... /// /// This struct is generally created by calling [`repeat()`]. Please From 389e638c054434c4b9df9427dab29b0f325923ac Mon Sep 17 00:00:00 2001 From: Xavientois Date: Sat, 16 Jan 2021 15:04:26 -0500 Subject: [PATCH 17/62] Add tests for SizeHint implementations --- library/std/src/io/tests.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 03ed8ba74c520..a85dd0d982715 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -204,8 +204,8 @@ fn bufreader_size_hint() { let mut buf_reader = BufReader::new(&testdata[..]); assert_eq!(buf_reader.buffer().len(), 0); - let buffer = buf_reader.fill_buf().unwrap(); - let buffer_length = buffer.len(); + let buffer_length = testdata.len(); + buf_reader.fill_buf().unwrap(); // Check that size hint matches buffer contents let mut buffered_bytes = buf_reader.bytes(); @@ -218,6 +218,33 @@ fn bufreader_size_hint() { assert_eq!(lower_bound, buffer_length - 1); } +#[test] +fn empty_size_hint() { + let size_hint = io::empty().bytes().size_hint(); + assert_eq!(size_hint, (0, Some(0))); +} + +#[test] +fn chain_empty_size_hint() { + let chain = io::empty().chain(io::empty()); + let size_hint = chain.bytes().size_hint(); + assert_eq!(size_hint, (0, Some(0))); +} + +#[test] +fn chain_size_hint() { + let testdata = b"ABCDEFGHIJKL"; + let mut buf_reader_1 = BufReader::new(&testdata[..6]); + let mut buf_reader_2 = BufReader::new(&testdata[6..]); + + buf_reader_1.fill_buf().unwrap(); + buf_reader_2.fill_buf().unwrap(); + + let chain = buf_reader_1.chain(buf_reader_2); + let size_hint = chain.bytes().size_hint(); + assert_eq!(size_hint, (testdata.len(), None)); +} + #[test] fn chain_zero_length_read_is_not_eof() { let a = b"A"; From b837f3a99b8dc767c4a6a3216ad3940f23896932 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Sat, 16 Jan 2021 15:20:40 -0500 Subject: [PATCH 18/62] Remove trailing newline --- library/std/src/io/buffered/bufreader.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index e737a104014f4..839c64ee5c443 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -1,6 +1,8 @@ use crate::cmp; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE}; +use crate::io::{ + self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE, +}; /// The `BufReader` struct adds buffering to any reader. /// @@ -441,4 +443,3 @@ impl SizeHint for BufReader { self.buffer().len() } } - From 81aba388f16502cbb7b305d64de9ccc95057b339 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Sat, 16 Jan 2021 15:36:59 -0500 Subject: [PATCH 19/62] Add space for proper indentation --- library/std/src/io/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index 7de37c67abebc..e5e5f29b22d09 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -82,7 +82,7 @@ impl fmt::Debug for Empty { impl SizeHint for Empty { fn upper_bound(&self) -> Option { - Some(0) + Some(0) } } From fc9cd4a14be3ea1d5ee29b495045fa51edfc0810 Mon Sep 17 00:00:00 2001 From: Xavientois Date: Sat, 16 Jan 2021 17:48:22 -0500 Subject: [PATCH 20/62] Fix formatting on mod --- library/std/src/io/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 08b258bb816ec..b7ccf9f21efb0 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2244,7 +2244,7 @@ impl SizeHint for Chain { SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second) } - fn upper_bound(&self) -> Option { + fn upper_bound(&self) -> Option { match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) { (Some(first), Some(second)) => Some(first + second), _ => None, @@ -2252,7 +2252,6 @@ impl SizeHint for Chain { } } - /// Reader adaptor which limits the bytes read from an underlying reader. /// /// This struct is generally created by calling [`take`] on a reader. From 7674ae1a4e10ab6bafbda57f9fc2175fdf012f5e Mon Sep 17 00:00:00 2001 From: Xavientois Date: Sun, 31 Jan 2021 08:52:57 -0500 Subject: [PATCH 21/62] Fix line length format --- library/std/src/io/util.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index e5e5f29b22d09..f472361f916db 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -4,7 +4,9 @@ mod tests; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write}; +use crate::io::{ + self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, +}; /// A reader which is always at EOF. /// From 4cb089bb542f30faa708c65347aacef42c2427cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 23 Apr 2020 11:15:04 -0700 Subject: [PATCH 22/62] Inherit `#[stable(..)]` annotations in enum variants and fields from its item --- compiler/rustc_passes/src/lib_features.rs | 2 +- compiler/rustc_passes/src/stability.rs | 48 +++++-- .../deprecation/deprecation-in-staged-api.rs | 8 +- .../deprecation-in-staged-api.stderr | 10 +- .../lint/auxiliary/lint_stability_fields.rs | 23 +++- .../lint/lint-stability-fields-deprecated.rs | 11 +- .../lint-stability-fields-deprecated.stderr | 126 +++++++++--------- src/test/ui/lint/lint-stability-fields.rs | 20 ++- src/test/ui/lint/lint-stability-fields.stderr | 86 ++++++------ .../stability-attribute-issue-43027.rs | 9 +- .../stability-attribute-issue-43027.stderr | 8 -- .../stability-attribute-sanity.rs | 5 +- .../stability-attribute-sanity.stderr | 8 +- 13 files changed, 207 insertions(+), 157 deletions(-) delete mode 100644 src/test/ui/stability-attribute/stability-attribute-issue-43027.stderr diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 7c62a234dbaec..02b20e45d000c 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -109,7 +109,7 @@ impl LibFeatureCollector<'tcx> { } fn span_feature_error(&self, span: Span, msg: &str) { - struct_span_err!(self.tcx.sess, span, E0711, "{}", &msg,).emit(); + struct_span_err!(self.tcx.sess, span, E0711, "{}", &msg).emit(); } } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index e1d03e3504800..0e142665911da 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -70,6 +70,17 @@ impl InheritConstStability { } } +enum InheritStability { + Yes, + No, +} + +impl InheritStability { + fn yes(&self) -> bool { + matches!(self, InheritStability::Yes) + } +} + // A private tree-walker for producing an Index. struct Annotator<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -91,6 +102,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { kind: AnnotationKind, inherit_deprecation: InheritDeprecation, inherit_const_stability: InheritConstStability, + inherit_from_parent: InheritStability, visit_children: F, ) where F: FnOnce(&mut Self), @@ -131,12 +143,13 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } if self.tcx.features().staged_api { - if let Some(..) = attrs.iter().find(|a| self.tcx.sess.check_name(a, sym::deprecated)) { - self.tcx.sess.span_err( - item_sp, - "`#[deprecated]` cannot be used in staged API; \ - use `#[rustc_deprecated]` instead", - ); + if let Some(a) = attrs.iter().find(|a| self.tcx.sess.check_name(a, sym::deprecated)) { + self.tcx + .sess + .struct_span_err(a.span, "`#[deprecated]` cannot be used in staged API") + .span_label(a.span, "use `#[rustc_deprecated]` instead") + .span_label(item_sp, "") + .emit(); } } else { self.recurse_with_stability_attrs( @@ -185,7 +198,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if kind == AnnotationKind::Prohibited || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated) { - self.tcx.sess.span_err(item_sp, "This stability annotation is useless"); + self.tcx.sess.span_err(item_sp, "this stability annotation is useless"); } debug!("annotate: found {:?}", stab); @@ -202,7 +215,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { { match stab_v.parse::() { Err(_) => { - self.tcx.sess.span_err(item_sp, "Invalid stability version found"); + self.tcx.sess.span_err(item_sp, "invalid stability version found"); break; } Ok(stab_vp) => match dep_v.parse::() { @@ -210,7 +223,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { Ordering::Less => { self.tcx.sess.span_err( item_sp, - "An API can't be stabilized after it is deprecated", + "an API can't be stabilized after it is deprecated", ); break; } @@ -221,7 +234,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if dep_v != "TBD" { self.tcx .sess - .span_err(item_sp, "Invalid deprecation version found"); + .span_err(item_sp, "invalid deprecation version found"); } break; } @@ -237,7 +250,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if stab.is_none() { debug!("annotate: stab not found, parent = {:?}", self.parent_stab); if let Some(stab) = self.parent_stab { - if inherit_deprecation.yes() && stab.level.is_unstable() { + if inherit_deprecation.yes() && stab.level.is_unstable() + || inherit_from_parent.yes() + { self.index.stab_map.insert(hir_id, stab); } } @@ -368,6 +383,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, + InheritStability::Yes, |_| {}, ) } @@ -382,6 +398,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { kind, InheritDeprecation::Yes, const_stab_inherit, + InheritStability::No, |v| intravisit::walk_item(v, i), ); self.in_trait_impl = orig_in_trait_impl; @@ -395,6 +412,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, + InheritStability::No, |v| { intravisit::walk_trait_item(v, ti); }, @@ -411,6 +429,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { kind, InheritDeprecation::Yes, InheritConstStability::No, + InheritStability::No, |v| { intravisit::walk_impl_item(v, ii); }, @@ -425,6 +444,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, + InheritStability::Yes, |v| { if let Some(ctor_hir_id) = var.data.ctor_hir_id() { v.annotate( @@ -434,6 +454,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, + InheritStability::No, |_| {}, ); } @@ -451,6 +472,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, + InheritStability::Yes, |v| { intravisit::walk_struct_field(v, s); }, @@ -465,6 +487,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, + InheritStability::No, |v| { intravisit::walk_foreign_item(v, i); }, @@ -479,6 +502,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, + InheritStability::No, |_| {}, ); } @@ -499,6 +523,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { kind, InheritDeprecation::No, InheritConstStability::No, + InheritStability::No, |v| { intravisit::walk_generic_param(v, p); }, @@ -669,6 +694,7 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> { AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, + InheritStability::No, |v| intravisit::walk_crate(v, krate), ); } diff --git a/src/test/ui/deprecation/deprecation-in-staged-api.rs b/src/test/ui/deprecation/deprecation-in-staged-api.rs index f667de83b56d4..910bfd1b5e48a 100644 --- a/src/test/ui/deprecation/deprecation-in-staged-api.rs +++ b/src/test/ui/deprecation/deprecation-in-staged-api.rs @@ -1,8 +1,4 @@ -// #[deprecated] cannot be used in staged API - #![feature(staged_api)] - #![stable(feature = "stable_test_feature", since = "1.0.0")] - -#[deprecated] -fn main() { } //~ ERROR `#[deprecated]` cannot be used in staged API +#[deprecated] //~ ERROR `#[deprecated]` cannot be used in staged API +fn main() {} diff --git a/src/test/ui/deprecation/deprecation-in-staged-api.stderr b/src/test/ui/deprecation/deprecation-in-staged-api.stderr index c6881d5a5735f..cf977fa4b7b44 100644 --- a/src/test/ui/deprecation/deprecation-in-staged-api.stderr +++ b/src/test/ui/deprecation/deprecation-in-staged-api.stderr @@ -1,8 +1,10 @@ -error: `#[deprecated]` cannot be used in staged API; use `#[rustc_deprecated]` instead - --> $DIR/deprecation-in-staged-api.rs:8:1 +error: `#[deprecated]` cannot be used in staged API + --> $DIR/deprecation-in-staged-api.rs:3:1 | -LL | fn main() { } - | ^^^^^^^^^^^^^ +LL | #[deprecated] + | ^^^^^^^^^^^^^ use `#[rustc_deprecated]` instead +LL | fn main() {} + | ------------ error: aborting due to previous error diff --git a/src/test/ui/lint/auxiliary/lint_stability_fields.rs b/src/test/ui/lint/auxiliary/lint_stability_fields.rs index 0efe7686ef76a..3cbb48c4a6be0 100644 --- a/src/test/ui/lint/auxiliary/lint_stability_fields.rs +++ b/src/test/ui/lint/auxiliary/lint_stability_fields.rs @@ -3,20 +3,35 @@ #[stable(feature = "rust1", since = "1.0.0")] pub struct Stable { - #[stable(feature = "rust1", since = "1.0.0")] - pub inherit: u8, // it's a lie (stable doesn't inherit) + pub inherit: u8, #[unstable(feature = "unstable_test_feature", issue = "none")] pub override1: u8, #[rustc_deprecated(since = "1.0.0", reason = "text")] #[unstable(feature = "unstable_test_feature", issue = "none")] pub override2: u8, + #[stable(feature = "rust2", since = "2.0.0")] + pub override3: u8, } #[stable(feature = "rust1", since = "1.0.0")] -pub struct Stable2(#[stable(feature = "rust1", since = "1.0.0")] pub u8, +pub struct Stable2(#[stable(feature = "rust2", since = "2.0.0")] pub u8, #[unstable(feature = "unstable_test_feature", issue = "none")] pub u8, #[unstable(feature = "unstable_test_feature", issue = "none")] - #[rustc_deprecated(since = "1.0.0", reason = "text")] pub u8); + #[rustc_deprecated(since = "1.0.0", reason = "text")] pub u8, + pub u8); + +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Stable3 { + Inherit(u8), + InheritOverride(#[stable(feature = "rust2", since = "2.0.0")] u8), + #[stable(feature = "rust2", since = "2.0.0")] + Override1, + #[unstable(feature = "unstable_test_feature", issue = "none")] + Override2, + #[rustc_deprecated(since = "1.0.0", reason = "text")] + #[unstable(feature = "unstable_test_feature", issue = "none")] + Override3, +} #[unstable(feature = "unstable_test_feature", issue = "none")] pub struct Unstable { diff --git a/src/test/ui/lint/lint-stability-fields-deprecated.rs b/src/test/ui/lint/lint-stability-fields-deprecated.rs index 14c6383806fb8..2024cf15ab0a1 100644 --- a/src/test/ui/lint/lint-stability-fields-deprecated.rs +++ b/src/test/ui/lint/lint-stability-fields-deprecated.rs @@ -17,33 +17,38 @@ mod cross_crate { override1: 2, override2: 3, //~^ ERROR use of deprecated field + override3: 4, }; let _ = x.inherit; let _ = x.override1; let _ = x.override2; //~^ ERROR use of deprecated field + let _ = x.override3; let Stable { inherit: _, override1: _, - override2: _ + override2: _, //~^ ERROR use of deprecated field + override3: _, } = x; // all fine let Stable { .. } = x; - let x = Stable2(1, 2, 3); + let x = Stable2(1, 2, 3, 4); let _ = x.0; let _ = x.1; let _ = x.2; //~^ ERROR use of deprecated field + let _ = x.3; let Stable2(_, _, + _, + //~^ ERROR use of deprecated field _) - //~^ ERROR use of deprecated field = x; // all fine let Stable2(..) = x; diff --git a/src/test/ui/lint/lint-stability-fields-deprecated.stderr b/src/test/ui/lint/lint-stability-fields-deprecated.stderr index ec786786023b9..151b3e59b9130 100644 --- a/src/test/ui/lint/lint-stability-fields-deprecated.stderr +++ b/src/test/ui/lint/lint-stability-fields-deprecated.stderr @@ -1,5 +1,5 @@ error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated`: text - --> $DIR/lint-stability-fields-deprecated.rs:94:17 + --> $DIR/lint-stability-fields-deprecated.rs:99:17 | LL | let x = Deprecated { | ^^^^^^^^^^ @@ -11,67 +11,67 @@ LL | #![deny(deprecated)] | ^^^^^^^^^^ error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated`: text - --> $DIR/lint-stability-fields-deprecated.rs:111:13 + --> $DIR/lint-stability-fields-deprecated.rs:116:13 | LL | let Deprecated { | ^^^^^^^^^^ error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated`: text - --> $DIR/lint-stability-fields-deprecated.rs:121:13 + --> $DIR/lint-stability-fields-deprecated.rs:126:13 | LL | let Deprecated | ^^^^^^^^^^ error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated2`: text - --> $DIR/lint-stability-fields-deprecated.rs:126:17 + --> $DIR/lint-stability-fields-deprecated.rs:131:17 | LL | let x = Deprecated2(1, 2, 3); | ^^^^^^^^^^^ error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated2`: text - --> $DIR/lint-stability-fields-deprecated.rs:136:13 + --> $DIR/lint-stability-fields-deprecated.rs:141:13 | LL | let Deprecated2 | ^^^^^^^^^^^ error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated2`: text - --> $DIR/lint-stability-fields-deprecated.rs:145:13 + --> $DIR/lint-stability-fields-deprecated.rs:150:13 | LL | let Deprecated2 | ^^^^^^^^^^^ error: use of deprecated struct `this_crate::Deprecated`: text - --> $DIR/lint-stability-fields-deprecated.rs:281:17 + --> $DIR/lint-stability-fields-deprecated.rs:286:17 | LL | let x = Deprecated { | ^^^^^^^^^^ error: use of deprecated struct `this_crate::Deprecated`: text - --> $DIR/lint-stability-fields-deprecated.rs:298:13 + --> $DIR/lint-stability-fields-deprecated.rs:303:13 | LL | let Deprecated { | ^^^^^^^^^^ error: use of deprecated struct `this_crate::Deprecated`: text - --> $DIR/lint-stability-fields-deprecated.rs:308:13 + --> $DIR/lint-stability-fields-deprecated.rs:313:13 | LL | let Deprecated | ^^^^^^^^^^ error: use of deprecated tuple struct `this_crate::Deprecated2`: text - --> $DIR/lint-stability-fields-deprecated.rs:313:17 + --> $DIR/lint-stability-fields-deprecated.rs:318:17 | LL | let x = Deprecated2(1, 2, 3); | ^^^^^^^^^^^ error: use of deprecated tuple struct `this_crate::Deprecated2`: text - --> $DIR/lint-stability-fields-deprecated.rs:323:13 + --> $DIR/lint-stability-fields-deprecated.rs:328:13 | LL | let Deprecated2 | ^^^^^^^^^^^ error: use of deprecated tuple struct `this_crate::Deprecated2`: text - --> $DIR/lint-stability-fields-deprecated.rs:332:13 + --> $DIR/lint-stability-fields-deprecated.rs:337:13 | LL | let Deprecated2 | ^^^^^^^^^^^ @@ -83,295 +83,295 @@ LL | override2: 3, | ^^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Stable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:24:17 + --> $DIR/lint-stability-fields-deprecated.rs:25:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Stable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:30:13 + --> $DIR/lint-stability-fields-deprecated.rs:32:13 | -LL | override2: _ +LL | override2: _, | ^^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Stable2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:40:17 + --> $DIR/lint-stability-fields-deprecated.rs:43:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Stable2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:45:20 + --> $DIR/lint-stability-fields-deprecated.rs:49:20 | -LL | _) +LL | _, | ^ error: use of deprecated field `cross_crate::lint_stability_fields::Unstable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:55:13 + --> $DIR/lint-stability-fields-deprecated.rs:60:13 | LL | override2: 3, | ^^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Unstable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:61:17 + --> $DIR/lint-stability-fields-deprecated.rs:66:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Unstable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:67:13 + --> $DIR/lint-stability-fields-deprecated.rs:72:13 | LL | override2: _ | ^^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Unstable2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:80:17 + --> $DIR/lint-stability-fields-deprecated.rs:85:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Unstable2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:86:14 + --> $DIR/lint-stability-fields-deprecated.rs:91:14 | LL | _) | ^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::inherit`: text - --> $DIR/lint-stability-fields-deprecated.rs:96:13 + --> $DIR/lint-stability-fields-deprecated.rs:101:13 | LL | inherit: 1, | ^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override1`: text - --> $DIR/lint-stability-fields-deprecated.rs:98:13 + --> $DIR/lint-stability-fields-deprecated.rs:103:13 | LL | override1: 2, | ^^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:100:13 + --> $DIR/lint-stability-fields-deprecated.rs:105:13 | LL | override2: 3, | ^^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::inherit`: text - --> $DIR/lint-stability-fields-deprecated.rs:104:17 + --> $DIR/lint-stability-fields-deprecated.rs:109:17 | LL | let _ = x.inherit; | ^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override1`: text - --> $DIR/lint-stability-fields-deprecated.rs:106:17 + --> $DIR/lint-stability-fields-deprecated.rs:111:17 | LL | let _ = x.override1; | ^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:108:17 + --> $DIR/lint-stability-fields-deprecated.rs:113:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::inherit`: text - --> $DIR/lint-stability-fields-deprecated.rs:113:13 + --> $DIR/lint-stability-fields-deprecated.rs:118:13 | LL | inherit: _, | ^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override1`: text - --> $DIR/lint-stability-fields-deprecated.rs:115:13 + --> $DIR/lint-stability-fields-deprecated.rs:120:13 | LL | override1: _, | ^^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:117:13 + --> $DIR/lint-stability-fields-deprecated.rs:122:13 | LL | override2: _ | ^^^^^^^^^^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::0`: text - --> $DIR/lint-stability-fields-deprecated.rs:129:17 + --> $DIR/lint-stability-fields-deprecated.rs:134:17 | LL | let _ = x.0; | ^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::1`: text - --> $DIR/lint-stability-fields-deprecated.rs:131:17 + --> $DIR/lint-stability-fields-deprecated.rs:136:17 | LL | let _ = x.1; | ^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:133:17 + --> $DIR/lint-stability-fields-deprecated.rs:138:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::0`: text - --> $DIR/lint-stability-fields-deprecated.rs:138:14 + --> $DIR/lint-stability-fields-deprecated.rs:143:14 | LL | (_, | ^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::1`: text - --> $DIR/lint-stability-fields-deprecated.rs:140:14 + --> $DIR/lint-stability-fields-deprecated.rs:145:14 | LL | _, | ^ error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:142:14 + --> $DIR/lint-stability-fields-deprecated.rs:147:14 | LL | _) | ^ error: use of deprecated field `this_crate::Stable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:205:13 + --> $DIR/lint-stability-fields-deprecated.rs:210:13 | LL | override2: 3, | ^^^^^^^^^^^^ error: use of deprecated field `this_crate::Stable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:211:17 + --> $DIR/lint-stability-fields-deprecated.rs:216:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ error: use of deprecated field `this_crate::Stable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:217:13 + --> $DIR/lint-stability-fields-deprecated.rs:222:13 | LL | override2: _ | ^^^^^^^^^^^^ error: use of deprecated field `this_crate::Stable2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:227:17 + --> $DIR/lint-stability-fields-deprecated.rs:232:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `this_crate::Stable2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:232:20 + --> $DIR/lint-stability-fields-deprecated.rs:237:20 | LL | _) | ^ error: use of deprecated field `this_crate::Unstable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:242:13 + --> $DIR/lint-stability-fields-deprecated.rs:247:13 | LL | override2: 3, | ^^^^^^^^^^^^ error: use of deprecated field `this_crate::Unstable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:248:17 + --> $DIR/lint-stability-fields-deprecated.rs:253:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ error: use of deprecated field `this_crate::Unstable::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:254:13 + --> $DIR/lint-stability-fields-deprecated.rs:259:13 | LL | override2: _ | ^^^^^^^^^^^^ error: use of deprecated field `this_crate::Unstable2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:267:17 + --> $DIR/lint-stability-fields-deprecated.rs:272:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `this_crate::Unstable2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:273:14 + --> $DIR/lint-stability-fields-deprecated.rs:278:14 | LL | _) | ^ error: use of deprecated field `this_crate::Deprecated::inherit`: text - --> $DIR/lint-stability-fields-deprecated.rs:283:13 + --> $DIR/lint-stability-fields-deprecated.rs:288:13 | LL | inherit: 1, | ^^^^^^^^^^ error: use of deprecated field `this_crate::Deprecated::override1`: text - --> $DIR/lint-stability-fields-deprecated.rs:285:13 + --> $DIR/lint-stability-fields-deprecated.rs:290:13 | LL | override1: 2, | ^^^^^^^^^^^^ error: use of deprecated field `this_crate::Deprecated::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:287:13 + --> $DIR/lint-stability-fields-deprecated.rs:292:13 | LL | override2: 3, | ^^^^^^^^^^^^ error: use of deprecated field `this_crate::Deprecated::inherit`: text - --> $DIR/lint-stability-fields-deprecated.rs:291:17 + --> $DIR/lint-stability-fields-deprecated.rs:296:17 | LL | let _ = x.inherit; | ^^^^^^^^^ error: use of deprecated field `this_crate::Deprecated::override1`: text - --> $DIR/lint-stability-fields-deprecated.rs:293:17 + --> $DIR/lint-stability-fields-deprecated.rs:298:17 | LL | let _ = x.override1; | ^^^^^^^^^^^ error: use of deprecated field `this_crate::Deprecated::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:295:17 + --> $DIR/lint-stability-fields-deprecated.rs:300:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ error: use of deprecated field `this_crate::Deprecated::inherit`: text - --> $DIR/lint-stability-fields-deprecated.rs:300:13 + --> $DIR/lint-stability-fields-deprecated.rs:305:13 | LL | inherit: _, | ^^^^^^^^^^ error: use of deprecated field `this_crate::Deprecated::override1`: text - --> $DIR/lint-stability-fields-deprecated.rs:302:13 + --> $DIR/lint-stability-fields-deprecated.rs:307:13 | LL | override1: _, | ^^^^^^^^^^^^ error: use of deprecated field `this_crate::Deprecated::override2`: text - --> $DIR/lint-stability-fields-deprecated.rs:304:13 + --> $DIR/lint-stability-fields-deprecated.rs:309:13 | LL | override2: _ | ^^^^^^^^^^^^ error: use of deprecated field `this_crate::Deprecated2::0`: text - --> $DIR/lint-stability-fields-deprecated.rs:316:17 + --> $DIR/lint-stability-fields-deprecated.rs:321:17 | LL | let _ = x.0; | ^^^ error: use of deprecated field `this_crate::Deprecated2::1`: text - --> $DIR/lint-stability-fields-deprecated.rs:318:17 + --> $DIR/lint-stability-fields-deprecated.rs:323:17 | LL | let _ = x.1; | ^^^ error: use of deprecated field `this_crate::Deprecated2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:320:17 + --> $DIR/lint-stability-fields-deprecated.rs:325:17 | LL | let _ = x.2; | ^^^ error: use of deprecated field `this_crate::Deprecated2::0`: text - --> $DIR/lint-stability-fields-deprecated.rs:325:14 + --> $DIR/lint-stability-fields-deprecated.rs:330:14 | LL | (_, | ^ error: use of deprecated field `this_crate::Deprecated2::1`: text - --> $DIR/lint-stability-fields-deprecated.rs:327:14 + --> $DIR/lint-stability-fields-deprecated.rs:332:14 | LL | _, | ^ error: use of deprecated field `this_crate::Deprecated2::2`: text - --> $DIR/lint-stability-fields-deprecated.rs:329:14 + --> $DIR/lint-stability-fields-deprecated.rs:334:14 | LL | _) | ^ diff --git a/src/test/ui/lint/lint-stability-fields.rs b/src/test/ui/lint/lint-stability-fields.rs index c5de5748aa300..4083648984867 100644 --- a/src/test/ui/lint/lint-stability-fields.rs +++ b/src/test/ui/lint/lint-stability-fields.rs @@ -20,29 +20,34 @@ mod cross_crate { inherit: 1, override1: 2, //~ ERROR use of unstable override2: 3, //~ ERROR use of unstable + override3: 4, }; let _ = x.inherit; let _ = x.override1; //~ ERROR use of unstable let _ = x.override2; //~ ERROR use of unstable + let _ = x.override3; let Stable { inherit: _, override1: _, //~ ERROR use of unstable - override2: _ //~ ERROR use of unstable + override2: _, //~ ERROR use of unstable + override3: _ } = x; // all fine let Stable { .. } = x; - let x = Stable2(1, 2, 3); + let x = Stable2(1, 2, 3, 4); let _ = x.0; let _ = x.1; //~ ERROR use of unstable let _ = x.2; //~ ERROR use of unstable + let _ = x.3; let Stable2(_, _, //~ ERROR use of unstable - _) //~ ERROR use of unstable + _, //~ ERROR use of unstable + _) = x; // all fine let Stable2(..) = x; @@ -133,11 +138,13 @@ mod this_crate { #[rustc_deprecated(since = "1.0.0", reason = "text")] #[unstable(feature = "unstable_test_feature", issue = "none")] override2: u8, + #[stable(feature = "rust2", since = "2.0.0")] + override3: u8, } #[stable(feature = "rust1", since = "1.0.0")] struct Stable2(u8, - #[stable(feature = "rust1", since = "1.0.0")] u8, + #[stable(feature = "rust2", since = "2.0.0")] u8, #[unstable(feature = "unstable_test_feature", issue = "none")] #[rustc_deprecated(since = "1.0.0", reason = "text")] u8); @@ -178,16 +185,19 @@ mod this_crate { inherit: 1, override1: 2, override2: 3, + override3: 4, }; let _ = x.inherit; let _ = x.override1; let _ = x.override2; + let _ = x.override3; let Stable { inherit: _, override1: _, - override2: _ + override2: _, + override3: _ } = x; // all fine let Stable { .. } = x; diff --git a/src/test/ui/lint/lint-stability-fields.stderr b/src/test/ui/lint/lint-stability-fields.stderr index b6a08186b5fb8..3d2e73c1e8e92 100644 --- a/src/test/ui/lint/lint-stability-fields.stderr +++ b/src/test/ui/lint/lint-stability-fields.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:51:17 + --> $DIR/lint-stability-fields.rs:56:17 | LL | let x = Unstable { | ^^^^^^^^ @@ -7,7 +7,7 @@ LL | let x = Unstable { = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:61:13 + --> $DIR/lint-stability-fields.rs:66:13 | LL | let Unstable { | ^^^^^^^^ @@ -15,7 +15,7 @@ LL | let Unstable { = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:67:13 + --> $DIR/lint-stability-fields.rs:72:13 | LL | let Unstable | ^^^^^^^^ @@ -23,7 +23,7 @@ LL | let Unstable = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:72:17 + --> $DIR/lint-stability-fields.rs:77:17 | LL | let x = reexport::Unstable2(1, 2, 3); | ^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | let x = reexport::Unstable2(1, 2, 3); = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:74:17 + --> $DIR/lint-stability-fields.rs:79:17 | LL | let x = Unstable2(1, 2, 3); | ^^^^^^^^^ @@ -39,7 +39,7 @@ LL | let x = Unstable2(1, 2, 3); = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:80:13 + --> $DIR/lint-stability-fields.rs:85:13 | LL | let Unstable2 | ^^^^^^^^^ @@ -47,7 +47,7 @@ LL | let Unstable2 = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:85:13 + --> $DIR/lint-stability-fields.rs:90:13 | LL | let Unstable2 | ^^^^^^^^^ @@ -55,7 +55,7 @@ LL | let Unstable2 = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:90:17 + --> $DIR/lint-stability-fields.rs:95:17 | LL | let x = Deprecated { | ^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | let x = Deprecated { = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:100:13 + --> $DIR/lint-stability-fields.rs:105:13 | LL | let Deprecated { | ^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | let Deprecated { = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:106:13 + --> $DIR/lint-stability-fields.rs:111:13 | LL | let Deprecated | ^^^^^^^^^^ @@ -79,7 +79,7 @@ LL | let Deprecated = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:110:17 + --> $DIR/lint-stability-fields.rs:115:17 | LL | let x = Deprecated2(1, 2, 3); | ^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | let x = Deprecated2(1, 2, 3); = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:116:13 + --> $DIR/lint-stability-fields.rs:121:13 | LL | let Deprecated2 | ^^^^^^^^^^^ @@ -95,7 +95,7 @@ LL | let Deprecated2 = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:121:13 + --> $DIR/lint-stability-fields.rs:126:13 | LL | let Deprecated2 | ^^^^^^^^^^^ @@ -119,7 +119,7 @@ LL | override2: 3, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:26:17 + --> $DIR/lint-stability-fields.rs:27:17 | LL | let _ = x.override1; | ^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | let _ = x.override1; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:27:17 + --> $DIR/lint-stability-fields.rs:28:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | let _ = x.override2; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:31:13 + --> $DIR/lint-stability-fields.rs:33:13 | LL | override1: _, | ^^^^^^^^^^^^ @@ -143,15 +143,15 @@ LL | override1: _, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:32:13 + --> $DIR/lint-stability-fields.rs:34:13 | -LL | override2: _ +LL | override2: _, | ^^^^^^^^^^^^ | = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:40:17 + --> $DIR/lint-stability-fields.rs:43:17 | LL | let _ = x.1; | ^^^ @@ -159,7 +159,7 @@ LL | let _ = x.1; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:41:17 + --> $DIR/lint-stability-fields.rs:44:17 | LL | let _ = x.2; | ^^^ @@ -167,7 +167,7 @@ LL | let _ = x.2; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:44:20 + --> $DIR/lint-stability-fields.rs:48:20 | LL | _, | ^ @@ -175,15 +175,15 @@ LL | _, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:45:20 + --> $DIR/lint-stability-fields.rs:49:20 | -LL | _) +LL | _, | ^ | = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:52:13 + --> $DIR/lint-stability-fields.rs:57:13 | LL | inherit: 1, | ^^^^^^^^^^ @@ -191,7 +191,7 @@ LL | inherit: 1, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:54:13 + --> $DIR/lint-stability-fields.rs:59:13 | LL | override2: 3, | ^^^^^^^^^^^^ @@ -199,7 +199,7 @@ LL | override2: 3, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:57:17 + --> $DIR/lint-stability-fields.rs:62:17 | LL | let _ = x.inherit; | ^^^^^^^^^ @@ -207,7 +207,7 @@ LL | let _ = x.inherit; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:59:17 + --> $DIR/lint-stability-fields.rs:64:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ @@ -215,7 +215,7 @@ LL | let _ = x.override2; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:62:13 + --> $DIR/lint-stability-fields.rs:67:13 | LL | inherit: _, | ^^^^^^^^^^ @@ -223,7 +223,7 @@ LL | inherit: _, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:64:13 + --> $DIR/lint-stability-fields.rs:69:13 | LL | override2: _ | ^^^^^^^^^^^^ @@ -231,7 +231,7 @@ LL | override2: _ = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:76:17 + --> $DIR/lint-stability-fields.rs:81:17 | LL | let _ = x.0; | ^^^ @@ -239,7 +239,7 @@ LL | let _ = x.0; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:78:17 + --> $DIR/lint-stability-fields.rs:83:17 | LL | let _ = x.2; | ^^^ @@ -247,7 +247,7 @@ LL | let _ = x.2; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:81:14 + --> $DIR/lint-stability-fields.rs:86:14 | LL | (_, | ^ @@ -255,7 +255,7 @@ LL | (_, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:83:14 + --> $DIR/lint-stability-fields.rs:88:14 | LL | _) | ^ @@ -263,7 +263,7 @@ LL | _) = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:91:13 + --> $DIR/lint-stability-fields.rs:96:13 | LL | inherit: 1, | ^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | inherit: 1, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:93:13 + --> $DIR/lint-stability-fields.rs:98:13 | LL | override2: 3, | ^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | override2: 3, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:96:17 + --> $DIR/lint-stability-fields.rs:101:17 | LL | let _ = x.inherit; | ^^^^^^^^^ @@ -287,7 +287,7 @@ LL | let _ = x.inherit; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:98:17 + --> $DIR/lint-stability-fields.rs:103:17 | LL | let _ = x.override2; | ^^^^^^^^^^^ @@ -295,7 +295,7 @@ LL | let _ = x.override2; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:101:13 + --> $DIR/lint-stability-fields.rs:106:13 | LL | inherit: _, | ^^^^^^^^^^ @@ -303,7 +303,7 @@ LL | inherit: _, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:103:13 + --> $DIR/lint-stability-fields.rs:108:13 | LL | override2: _ | ^^^^^^^^^^^^ @@ -311,7 +311,7 @@ LL | override2: _ = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:112:17 + --> $DIR/lint-stability-fields.rs:117:17 | LL | let _ = x.0; | ^^^ @@ -319,7 +319,7 @@ LL | let _ = x.0; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:114:17 + --> $DIR/lint-stability-fields.rs:119:17 | LL | let _ = x.2; | ^^^ @@ -327,7 +327,7 @@ LL | let _ = x.2; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:117:14 + --> $DIR/lint-stability-fields.rs:122:14 | LL | (_, | ^ @@ -335,7 +335,7 @@ LL | (_, = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable error[E0658]: use of unstable library feature 'unstable_test_feature' - --> $DIR/lint-stability-fields.rs:119:14 + --> $DIR/lint-stability-fields.rs:124:14 | LL | _) | ^ diff --git a/src/test/ui/stability-attribute/stability-attribute-issue-43027.rs b/src/test/ui/stability-attribute/stability-attribute-issue-43027.rs index 0b243bb52119b..3f4fdfd0180ed 100644 --- a/src/test/ui/stability-attribute/stability-attribute-issue-43027.rs +++ b/src/test/ui/stability-attribute/stability-attribute-issue-43027.rs @@ -1,10 +1,15 @@ +// check-pass #![feature(staged_api)] #![stable(feature = "test", since = "0")] #[stable(feature = "test", since = "0")] -pub struct Reverse(pub T); //~ ERROR field has missing stability attribute +pub struct A(pub T); + +#[stable(feature = "test", since = "0")] +pub struct B(#[stable(feature = "test", since = "0")] pub T); fn main() { // Make sure the field is used to fill the stability cache - Reverse(0).0; + A(0).0; + B(0).0; } diff --git a/src/test/ui/stability-attribute/stability-attribute-issue-43027.stderr b/src/test/ui/stability-attribute/stability-attribute-issue-43027.stderr deleted file mode 100644 index 280c72acccb18..0000000000000 --- a/src/test/ui/stability-attribute/stability-attribute-issue-43027.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: field has missing stability attribute - --> $DIR/stability-attribute-issue-43027.rs:5:23 - | -LL | pub struct Reverse(pub T); - | ^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.rs b/src/test/ui/stability-attribute/stability-attribute-sanity.rs index 0c40f8ae1c67e..1cc31d8ec187d 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.rs +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.rs @@ -62,12 +62,11 @@ fn multiple3() { } #[rustc_deprecated(since = "b", reason = "text")] //~ ERROR multiple deprecated attributes #[rustc_const_unstable(feature = "c", issue = "none")] #[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels -pub const fn multiple4() { } -//~^ ERROR Invalid stability version found +pub const fn multiple4() { } //~ ERROR invalid stability version found #[stable(feature = "a", since = "1.0.0")] #[rustc_deprecated(since = "invalid", reason = "text")] -fn invalid_deprecation_version() {} //~ ERROR Invalid deprecation version found +fn invalid_deprecation_version() {} //~ ERROR invalid deprecation version found #[rustc_deprecated(since = "a", reason = "text")] fn deprecated_without_unstable_or_stable() { } diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr index 674139f4afcce..07e3da73c60c7 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr @@ -96,20 +96,20 @@ error[E0544]: multiple stability levels LL | #[rustc_const_unstable(feature = "d", issue = "none")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Invalid stability version found +error: invalid stability version found --> $DIR/stability-attribute-sanity.rs:65:1 | LL | pub const fn multiple4() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Invalid deprecation version found - --> $DIR/stability-attribute-sanity.rs:70:1 +error: invalid deprecation version found + --> $DIR/stability-attribute-sanity.rs:69:1 | LL | fn invalid_deprecation_version() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0549]: rustc_deprecated attribute must be paired with either stable or unstable attribute - --> $DIR/stability-attribute-sanity.rs:72:1 + --> $DIR/stability-attribute-sanity.rs:71:1 | LL | #[rustc_deprecated(since = "a", reason = "text")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 19806e451476d9a97175d2ca0c095545e8894421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 10 Feb 2021 21:35:27 -0800 Subject: [PATCH 23/62] Tweak stability attribute diagnostic output --- compiler/rustc_attr/src/builtin.rs | 27 ++++++++------ compiler/rustc_expand/src/base.rs | 9 +++-- compiler/rustc_passes/src/stability.rs | 36 ++++++++++--------- .../stability-attribute-sanity.rs | 8 ++--- .../stability-attribute-sanity.stderr | 14 +++++--- 5 files changed, 55 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index aca3fbbca1357..c701441e9e5bc 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -176,7 +176,7 @@ pub fn find_stability( sess: &Session, attrs: &[Attribute], item_sp: Span, -) -> (Option, Option) { +) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) { find_stability_generic(sess, attrs.iter(), item_sp) } @@ -184,15 +184,16 @@ fn find_stability_generic<'a, I>( sess: &Session, attrs_iter: I, item_sp: Span, -) -> (Option, Option) +) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) where I: Iterator, { use StabilityLevel::*; - let mut stab: Option = None; - let mut const_stab: Option = None; + let mut stab: Option<(Stability, Span)> = None; + let mut const_stab: Option<(ConstStability, Span)> = None; let mut promotable = false; + let diagnostic = &sess.parse_sess.span_diagnostic; 'outer: for attr in attrs_iter { @@ -356,10 +357,12 @@ where } let level = Unstable { reason, issue: issue_num, is_soft }; if sym::unstable == meta_name { - stab = Some(Stability { level, feature }); + stab = Some((Stability { level, feature }, attr.span)); } else { - const_stab = - Some(ConstStability { level, feature, promotable: false }); + const_stab = Some(( + ConstStability { level, feature, promotable: false }, + attr.span, + )); } } (None, _, _) => { @@ -432,10 +435,12 @@ where (Some(feature), Some(since)) => { let level = Stable { since }; if sym::stable == meta_name { - stab = Some(Stability { level, feature }); + stab = Some((Stability { level, feature }, attr.span)); } else { - const_stab = - Some(ConstStability { level, feature, promotable: false }); + const_stab = Some(( + ConstStability { level, feature, promotable: false }, + attr.span, + )); } } (None, _) => { @@ -455,7 +460,7 @@ where // Merge the const-unstable info into the stability info if promotable { - if let Some(ref mut stab) = const_stab { + if let Some((ref mut stab, _)) = const_stab { stab.promotable = promotable; } else { struct_span_err!( diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index e3dc793a7fac4..bd93e34af680b 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -770,10 +770,13 @@ impl SyntaxExtension { .find_by_name(attrs, sym::rustc_builtin_macro) .map(|a| a.value_str().unwrap_or(name)); let (stability, const_stability) = attr::find_stability(&sess, attrs, span); - if const_stability.is_some() { + if let Some((_, sp)) = const_stability { sess.parse_sess .span_diagnostic - .span_err(span, "macros cannot have const stability attributes"); + .struct_span_err(sp, "macros cannot have const stability attributes") + .span_label(sp, "invalid stability attribute") + .span_label(span, "in this macro") + .emit(); } SyntaxExtension { @@ -782,7 +785,7 @@ impl SyntaxExtension { allow_internal_unstable, allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe), local_inner_macros, - stability, + stability: stability.map(|(s, _)| s), deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d), helper_attrs, edition, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 0e142665911da..d698512a648aa 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -163,7 +163,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp); - let const_stab = const_stab.map(|const_stab| { + let const_stab = const_stab.map(|(const_stab, _)| { let const_stab = self.tcx.intern_const_stability(const_stab); self.index.const_stab_map.insert(hir_id, const_stab); const_stab @@ -193,12 +193,15 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - let stab = stab.map(|stab| { + let stab = stab.map(|(stab, span)| { // Error if prohibited, or can't inherit anything from a container. if kind == AnnotationKind::Prohibited || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated) { - self.tcx.sess.span_err(item_sp, "this stability annotation is useless"); + self.tcx.sess.struct_span_err(span,"this stability annotation is useless") + .span_label(span, "useless stability annotation") + .span_label(item_sp, "the stability attribute annotates this item") + .emit(); } debug!("annotate: found {:?}", stab); @@ -215,16 +218,19 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { { match stab_v.parse::() { Err(_) => { - self.tcx.sess.span_err(item_sp, "invalid stability version found"); + self.tcx.sess.struct_span_err(span, "invalid stability version found") + .span_label(span, "invalid stability version") + .span_label(item_sp, "the stability attribute annotates this item") + .emit(); break; } Ok(stab_vp) => match dep_v.parse::() { Ok(dep_vp) => match dep_vp.cmp(&stab_vp) { Ordering::Less => { - self.tcx.sess.span_err( - item_sp, - "an API can't be stabilized after it is deprecated", - ); + self.tcx.sess.struct_span_err(span, "an API can't be stabilized after it is deprecated") + .span_label(span, "invalid version") + .span_label(item_sp, "the stability attribute annotates this item") + .emit(); break; } Ordering::Equal => continue, @@ -232,9 +238,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { }, Err(_) => { if dep_v != "TBD" { - self.tcx - .sess - .span_err(item_sp, "invalid deprecation version found"); + self.tcx.sess.struct_span_err(span, "invalid deprecation version found") + .span_label(span, "invalid deprecation version") + .span_label(item_sp, "the stability attribute annotates this item") + .emit(); } break; } @@ -756,18 +763,13 @@ impl Visitor<'tcx> for Checker<'tcx> { // error if all involved types and traits are stable, because // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 - if let (Some(Stability { level: attr::Unstable { .. }, .. }), _) = + if let (Some((Stability { level: attr::Unstable { .. }, .. }, span)), _) = attr::find_stability(&self.tcx.sess, &item.attrs, item.span) { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; c.visit_ty(self_ty); c.visit_trait_ref(t); if c.fully_stable { - let span = item - .attrs - .iter() - .find(|a| a.has_name(sym::unstable)) - .map_or(item.span, |a| a.span); self.tcx.struct_span_lint_hir( INEFFECTIVE_UNSTABLE_TRAIT_IMPL, item.hir_id, diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.rs b/src/test/ui/stability-attribute/stability-attribute-sanity.rs index 1cc31d8ec187d..9f8ecc2628126 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.rs +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.rs @@ -57,16 +57,16 @@ fn multiple2() { } #[stable(feature = "a", since = "b")] //~ ERROR multiple stability levels [E0544] fn multiple3() { } -#[stable(feature = "a", since = "b")] +#[stable(feature = "a", since = "b")] //~ ERROR invalid stability version found #[rustc_deprecated(since = "b", reason = "text")] #[rustc_deprecated(since = "b", reason = "text")] //~ ERROR multiple deprecated attributes #[rustc_const_unstable(feature = "c", issue = "none")] #[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels -pub const fn multiple4() { } //~ ERROR invalid stability version found +pub const fn multiple4() { } -#[stable(feature = "a", since = "1.0.0")] +#[stable(feature = "a", since = "1.0.0")] //~ ERROR invalid deprecation version found #[rustc_deprecated(since = "invalid", reason = "text")] -fn invalid_deprecation_version() {} //~ ERROR invalid deprecation version found +fn invalid_deprecation_version() {} #[rustc_deprecated(since = "a", reason = "text")] fn deprecated_without_unstable_or_stable() { } diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr index 07e3da73c60c7..bde35cca4a289 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr @@ -97,16 +97,22 @@ LL | #[rustc_const_unstable(feature = "d", issue = "none")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid stability version found - --> $DIR/stability-attribute-sanity.rs:65:1 + --> $DIR/stability-attribute-sanity.rs:60:1 | +LL | #[stable(feature = "a", since = "b")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid stability version +... LL | pub const fn multiple4() { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ---------------------------- the stability attribute annotates this item error: invalid deprecation version found - --> $DIR/stability-attribute-sanity.rs:69:1 + --> $DIR/stability-attribute-sanity.rs:67:1 | +LL | #[stable(feature = "a", since = "1.0.0")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid deprecation version +LL | #[rustc_deprecated(since = "invalid", reason = "text")] LL | fn invalid_deprecation_version() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ----------------------------------- the stability attribute annotates this item error[E0549]: rustc_deprecated attribute must be paired with either stable or unstable attribute --> $DIR/stability-attribute-sanity.rs:71:1 From 7b021aacb57d7a120f280302358d7bdd04a67bbc Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 29 Jan 2021 00:59:20 +0300 Subject: [PATCH 24/62] resolve: Reduce scope of `pub_use_of_private_extern_crate` deprecation lint --- compiler/rustc_resolve/src/imports.rs | 32 +++++++++------ src/test/rustdoc/extern-links.rs | 2 +- src/test/rustdoc/issue-28927.rs | 2 +- .../ui/pub/pub-reexport-priv-extern-crate.rs | 8 +--- .../pub/pub-reexport-priv-extern-crate.stderr | 39 +++++++++++-------- 5 files changed, 47 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index bd0296751a535..61f4c00a4ca42 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -156,6 +156,21 @@ impl<'a> NameResolution<'a> { } } +// Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` +// are permitted for backward-compatibility under a deprecation lint. +fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBinding<'_>) -> bool { + match (&import.kind, &binding.kind) { + ( + ImportKind::Single { .. }, + NameBindingKind::Import { + import: Import { kind: ImportKind::ExternCrate { .. }, .. }, + .. + }, + ) => import.vis.get() == ty::Visibility::Public, + _ => false, + } +} + impl<'a> Resolver<'a> { crate fn resolve_ident_in_module_unadjusted( &mut self, @@ -263,10 +278,7 @@ impl<'a> Resolver<'a> { return Err((Determined, Weak::No)); } } - // `extern crate` are always usable for backwards compatibility, see issue #37020, - // remove this together with `PUB_USE_OF_PRIVATE_EXTERN_CRATE`. - let usable = this.is_accessible_from(binding.vis, parent_scope.module) - || binding.is_extern_crate(); + let usable = this.is_accessible_from(binding.vis, parent_scope.module); if usable { Ok(binding) } else { Err((Determined, Weak::No)) } }; @@ -309,10 +321,7 @@ impl<'a> Resolver<'a> { } } - if !(self.is_accessible_from(binding.vis, parent_scope.module) || - // Remove this together with `PUB_USE_OF_PRIVATE_EXTERN_CRATE` - (self.last_import_segment && binding.is_extern_crate())) - { + if !self.is_accessible_from(binding.vis, parent_scope.module) { self.privacy_errors.push(PrivacyError { ident, binding, @@ -455,9 +464,8 @@ impl<'a> Resolver<'a> { binding: &'a NameBinding<'a>, import: &'a Import<'a>, ) -> &'a NameBinding<'a> { - let vis = if binding.vis.is_at_least(import.vis.get(), self) || - // cf. `PUB_USE_OF_PRIVATE_EXTERN_CRATE` - !import.is_glob() && binding.is_extern_crate() + let vis = if binding.vis.is_at_least(import.vis.get(), self) + || pub_use_of_private_extern_crate_hack(import, binding) { import.vis.get() } else { @@ -1188,7 +1196,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { // All namespaces must be re-exported with extra visibility for an error to occur. if !any_successful_reexport { let (ns, binding) = reexport_error.unwrap(); - if ns == TypeNS && binding.is_extern_crate() { + if pub_use_of_private_extern_crate_hack(import, binding) { let msg = format!( "extern crate `{}` is private, and cannot be \ re-exported (error E0365), consider declaring with \ diff --git a/src/test/rustdoc/extern-links.rs b/src/test/rustdoc/extern-links.rs index 991f869138d93..0383ccf7db666 100644 --- a/src/test/rustdoc/extern-links.rs +++ b/src/test/rustdoc/extern-links.rs @@ -3,7 +3,7 @@ #![crate_name = "foo"] -extern crate extern_links; +pub extern crate extern_links; // @!has foo/index.html '//a' 'extern_links' #[doc(no_inline)] diff --git a/src/test/rustdoc/issue-28927.rs b/src/test/rustdoc/issue-28927.rs index 7b535f33bf7e3..38a520850b6dd 100644 --- a/src/test/rustdoc/issue-28927.rs +++ b/src/test/rustdoc/issue-28927.rs @@ -2,5 +2,5 @@ // aux-build:issue-28927-1.rs // ignore-cross-compile -extern crate issue_28927_1 as inner1; +pub extern crate issue_28927_1 as inner1; pub use inner1 as foo; diff --git a/src/test/ui/pub/pub-reexport-priv-extern-crate.rs b/src/test/ui/pub/pub-reexport-priv-extern-crate.rs index e95d6924026ca..dd5cd420fa546 100644 --- a/src/test/ui/pub/pub-reexport-priv-extern-crate.rs +++ b/src/test/ui/pub/pub-reexport-priv-extern-crate.rs @@ -1,5 +1,3 @@ -#![allow(unused)] - extern crate core; pub use core as reexported_core; //~ ERROR `core` is private, and cannot be re-exported //~^ WARN this was previously accepted @@ -9,16 +7,14 @@ mod foo1 { } mod foo2 { - use foo1::core; //~ ERROR `core` is private, and cannot be re-exported - //~^ WARN this was previously accepted + use foo1::core; //~ ERROR crate import `core` is private pub mod bar { extern crate core; } } mod baz { - pub use foo2::bar::core; //~ ERROR `core` is private, and cannot be re-exported - //~^ WARN this was previously accepted + pub use foo2::bar::core; //~ ERROR crate import `core` is private } fn main() {} diff --git a/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr b/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr index 0b44c5a6525f6..e4d73c6475dc4 100644 --- a/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr +++ b/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr @@ -1,30 +1,37 @@ -error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub` - --> $DIR/pub-reexport-priv-extern-crate.rs:4:9 +error[E0603]: crate import `core` is private + --> $DIR/pub-reexport-priv-extern-crate.rs:10:15 | -LL | pub use core as reexported_core; - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | use foo1::core; + | ^^^^ private crate import | - = note: `#[deny(pub_use_of_private_extern_crate)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #34537 +note: the crate import `core` is defined here + --> $DIR/pub-reexport-priv-extern-crate.rs:6:5 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ -error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub` - --> $DIR/pub-reexport-priv-extern-crate.rs:12:9 +error[E0603]: crate import `core` is private + --> $DIR/pub-reexport-priv-extern-crate.rs:17:24 | -LL | use foo1::core; - | ^^^^^^^^^^ +LL | pub use foo2::bar::core; + | ^^^^ private crate import | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #34537 +note: the crate import `core` is defined here + --> $DIR/pub-reexport-priv-extern-crate.rs:12:9 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub` - --> $DIR/pub-reexport-priv-extern-crate.rs:20:13 + --> $DIR/pub-reexport-priv-extern-crate.rs:2:9 | -LL | pub use foo2::bar::core; - | ^^^^^^^^^^^^^^^ +LL | pub use core as reexported_core; + | ^^^^^^^^^^^^^^^^^^^^^^^ | + = note: `#[deny(pub_use_of_private_extern_crate)]` on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0603`. From 49310cee30510170946561814c0a87b4dc8e667d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 11 Feb 2021 10:37:31 -0800 Subject: [PATCH 25/62] Add test for "const stability on macro" --- compiler/rustc_expand/src/base.rs | 7 +++++-- .../ui/attributes/const-stability-on-macro.rs | 13 ++++++++++++ .../const-stability-on-macro.stderr | 20 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/attributes/const-stability-on-macro.rs create mode 100644 src/test/ui/attributes/const-stability-on-macro.stderr diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index bd93e34af680b..5652d77990895 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -774,8 +774,11 @@ impl SyntaxExtension { sess.parse_sess .span_diagnostic .struct_span_err(sp, "macros cannot have const stability attributes") - .span_label(sp, "invalid stability attribute") - .span_label(span, "in this macro") + .span_label(sp, "invalid const stability attribute") + .span_label( + sess.source_map().guess_head_span(span), + "const stability attribute affects this macro", + ) .emit(); } diff --git a/src/test/ui/attributes/const-stability-on-macro.rs b/src/test/ui/attributes/const-stability-on-macro.rs new file mode 100644 index 0000000000000..3fc60f7ce48c2 --- /dev/null +++ b/src/test/ui/attributes/const-stability-on-macro.rs @@ -0,0 +1,13 @@ +#[rustc_const_stable(feature = "foo", since = "0")] +//~^ ERROR macros cannot have const stability attributes +macro_rules! foo { + () => {}; +} + +#[rustc_const_unstable(feature = "bar", issue="none")] +//~^ ERROR macros cannot have const stability attributes +macro_rules! bar { + () => {}; +} + +fn main() {} diff --git a/src/test/ui/attributes/const-stability-on-macro.stderr b/src/test/ui/attributes/const-stability-on-macro.stderr new file mode 100644 index 0000000000000..ef24e44d1908b --- /dev/null +++ b/src/test/ui/attributes/const-stability-on-macro.stderr @@ -0,0 +1,20 @@ +error: macros cannot have const stability attributes + --> $DIR/const-stability-on-macro.rs:1:1 + | +LL | #[rustc_const_stable(feature = "foo", since = "0")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid const stability attribute +LL | +LL | macro_rules! foo { + | ---------------- const stability attribute affects this macro + +error: macros cannot have const stability attributes + --> $DIR/const-stability-on-macro.rs:7:1 + | +LL | #[rustc_const_unstable(feature = "bar", issue="none")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid const stability attribute +LL | +LL | macro_rules! bar { + | ---------------- const stability attribute affects this macro + +error: aborting due to 2 previous errors + From a3db47ab6c976137a26976d5c8060e7eadc05232 Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Tue, 9 Feb 2021 17:18:28 +0000 Subject: [PATCH 26/62] Add suggestion for iterators in iterators --- .../diagnostics/conflict_errors.rs | 33 ++++++++++++++++--- library/core/src/iter/traits/iterator.rs | 1 + src/test/ui/issues/issue-81584.fixed | 8 +++++ src/test/ui/issues/issue-81584.rs | 8 +++++ src/test/ui/issues/issue-81584.stderr | 14 ++++++++ .../ui/static/static-reference-to-fn-2.stderr | 2 ++ 6 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/issues/issue-81584.fixed create mode 100644 src/test/ui/issues/issue-81584.rs create mode 100644 src/test/ui/issues/issue-81584.stderr diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index b0b58a8d00367..24b9408ffb657 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -10,16 +10,18 @@ use rustc_middle::mir::{ FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, suggest_constraining_type_param, Instance, Ty}; -use rustc_span::{source_map::DesugaringKind, symbol::sym, Span}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TypeFoldable}; +use rustc_span::source_map::DesugaringKind; +use rustc_span::symbol::sym; +use rustc_span::Span; use crate::dataflow::drop_flag_effects; use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex}; use crate::util::borrowck_errors; use crate::borrow_check::{ - borrow_set::BorrowData, prefixes::IsPrefixOf, InitializationRequiringAction, MirBorrowckCtxt, - PrefixSet, WriteKind, + borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf, + InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind, }; use super::{ @@ -1267,6 +1269,29 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if return_span != borrow_span { err.span_label(borrow_span, note); + + let tcx = self.infcx.tcx; + let ty_params = ty::List::empty(); + + let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; + let return_ty = tcx.erase_regions(return_ty); + + // to avoid panics + if !return_ty.has_infer_types() { + if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) { + if tcx.type_implements_trait((iter_trait, return_ty, ty_params, self.param_env)) + { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) { + err.span_suggestion_hidden( + return_span, + "use `.collect()` to allocate the iterator", + format!("{}{}", snippet, ".collect::>()"), + Applicability::MaybeIncorrect, + ); + } + } + } + } } Some(err) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index f28c4673cc033..e179ce01c417a 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -93,6 +93,7 @@ fn _assert_is_object_safe(_: &dyn Iterator) {} message = "`{Self}` is not an iterator" )] #[doc(spotlight)] +#[rustc_diagnostic_item = "Iterator"] #[must_use = "iterators are lazy and do nothing unless consumed"] pub trait Iterator { /// The type of the elements being iterated over. diff --git a/src/test/ui/issues/issue-81584.fixed b/src/test/ui/issues/issue-81584.fixed new file mode 100644 index 0000000000000..1cad59f1062c6 --- /dev/null +++ b/src/test/ui/issues/issue-81584.fixed @@ -0,0 +1,8 @@ +// run-rustfix +fn main() { + let _ = vec![vec![0, 1], vec![2]] + .into_iter() + .map(|y| y.iter().map(|x| x + 1).collect::>()) + //~^ ERROR cannot return value referencing function parameter `y` + .collect::>(); +} diff --git a/src/test/ui/issues/issue-81584.rs b/src/test/ui/issues/issue-81584.rs new file mode 100644 index 0000000000000..452288db08bd8 --- /dev/null +++ b/src/test/ui/issues/issue-81584.rs @@ -0,0 +1,8 @@ +// run-rustfix +fn main() { + let _ = vec![vec![0, 1], vec![2]] + .into_iter() + .map(|y| y.iter().map(|x| x + 1)) + //~^ ERROR cannot return value referencing function parameter `y` + .collect::>(); +} diff --git a/src/test/ui/issues/issue-81584.stderr b/src/test/ui/issues/issue-81584.stderr new file mode 100644 index 0000000000000..d57f1b778df17 --- /dev/null +++ b/src/test/ui/issues/issue-81584.stderr @@ -0,0 +1,14 @@ +error[E0515]: cannot return value referencing function parameter `y` + --> $DIR/issue-81584.rs:5:22 + | +LL | .map(|y| y.iter().map(|x| x + 1)) + | -^^^^^^^^^^^^^^^^^^^^^^ + | | + | returns a value referencing data owned by the current function + | `y` is borrowed here + | + = help: use `.collect()` to allocate the iterator + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/src/test/ui/static/static-reference-to-fn-2.stderr b/src/test/ui/static/static-reference-to-fn-2.stderr index 028e11a60cef4..ff15884bd445d 100644 --- a/src/test/ui/static/static-reference-to-fn-2.stderr +++ b/src/test/ui/static/static-reference-to-fn-2.stderr @@ -40,6 +40,8 @@ LL | | statefn: &id(state1 as StateMachineFunc) | | ------------------------------ temporary value created here LL | | } | |_____^ returns a value referencing data owned by the current function + | + = help: use `.collect()` to allocate the iterator error: aborting due to 4 previous errors From 040735c110026bbd494a23c86182ebda201d720b Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 5 Jan 2021 10:07:50 +0100 Subject: [PATCH 27/62] First version of noop-lint --- compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/noop_method_call.rs | 71 +++++++++++++++++++++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/clone.rs | 2 + 4 files changed, 77 insertions(+) create mode 100644 compiler/rustc_lint/src/noop_method_call.rs diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 638b73c27a8d7..b0c22154c0b5b 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -57,6 +57,7 @@ mod methods; mod non_ascii_idents; mod non_fmt_panic; mod nonstandard_style; +mod noop_method_call; mod passes; mod redundant_semicolon; mod traits; @@ -83,6 +84,7 @@ use methods::*; use non_ascii_idents::*; use non_fmt_panic::NonPanicFmt; use nonstandard_style::*; +use noop_method_call::*; use redundant_semicolon::*; use traits::*; use types::*; @@ -170,6 +172,7 @@ macro_rules! late_lint_passes { DropTraitConstraints: DropTraitConstraints, TemporaryCStringAsPtr: TemporaryCStringAsPtr, NonPanicFmt: NonPanicFmt, + NoopMethodCall: NoopMethodCall, ] ); }; diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs new file mode 100644 index 0000000000000..098c50d5abe19 --- /dev/null +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -0,0 +1,71 @@ +use crate::context::LintContext; +use crate::rustc_middle::ty::TypeFoldable; +use crate::LateContext; +use crate::LateLintPass; +use rustc_hir::def::DefKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_middle::ty; +use rustc_span::symbol::sym; + +declare_lint! { + /// The `noop_method_call` lint detects specific calls to noop methods + /// such as a calling `<&T as Clone>::clone` where `T: !Clone`. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// struct Foo; + /// let foo = &Foo; + /// let clone: &Foo = foo.clone(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Some method calls are noops meaning that they do nothing. Usually such methods + /// are the result of blanket implementations that happen to create some method invocations + /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but + /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything + /// as references are copy. This lint detects these calls and warns the user about them. + pub NOOP_METHOD_CALL, + Warn, + "detects the use of well-known noop methods" +} + +declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); + +impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // We only care about method calls + if let ExprKind::MethodCall(..) = expr.kind { + // Get the `DefId` only when dealing with an `AssocFn` + if let Some((DefKind::AssocFn, did)) = + cx.typeck_results().type_dependent_def(expr.hir_id) + { + // Check that we're dealing with a trait method + if let Some(trait_id) = cx.tcx.trait_of_item(did) { + let substs = cx.typeck_results().node_substs(expr.hir_id); + // We can't resolve on types that recursively require monomorphization, + // so check that we don't need to perfom substitution + if !substs.needs_subst() { + let param_env = cx.tcx.param_env(trait_id); + // Resolve the trait method instance + if let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) { + // Check that it implements the noop diagnostic + if cx.tcx.is_diagnostic_item(sym::ref_clone_method, i.def_id()) { + let span = expr.span; + + cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { + let message = "Call to noop method"; + lint.build(&message).emit() + }); + } + } + } + } + } + } + } +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 27bb45bcc8512..2207deb8fb5a0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -915,6 +915,7 @@ symbols! { receiver, recursion_limit, reexport_test_harness_main, + ref_clone_method, reference, reflect, reg, diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index a953a3a4182bc..12f0f9629a31f 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -104,6 +104,7 @@ /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "clone"] +#[rustc_diagnostic_item = "Clone"] pub trait Clone: Sized { /// Returns a copy of the value. /// @@ -221,6 +222,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Clone for &T { #[inline] + #[rustc_diagnostic_item = "ref_clone_method"] fn clone(&self) -> Self { *self } From f49ed7a6b7aa3a44dd0444b508a1d0ddc09b0f15 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 5 Jan 2021 16:14:39 +0100 Subject: [PATCH 28/62] Add tests and support two more noop methods --- compiler/rustc_lint/src/noop_method_call.rs | 12 +++++- compiler/rustc_span/src/symbol.rs | 4 +- library/core/src/borrow.rs | 1 + library/core/src/clone.rs | 3 +- library/core/src/ops/deref.rs | 1 + src/test/ui/lint/noop-method-call.rs | 45 +++++++++++++++++++++ src/test/ui/lint/noop-method-call.stderr | 28 +++++++++++++ 7 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/lint/noop-method-call.rs create mode 100644 src/test/ui/lint/noop-method-call.stderr diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 098c50d5abe19..3ab3fab4272c7 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -54,11 +54,19 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { // Resolve the trait method instance if let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) { // Check that it implements the noop diagnostic - if cx.tcx.is_diagnostic_item(sym::ref_clone_method, i.def_id()) { + tracing::debug!("Resolves to: {:?}", i.def_id()); + if [ + sym::noop_method_borrow, + sym::noop_method_clone, + sym::noop_method_deref, + ] + .iter() + .any(|s| cx.tcx.is_diagnostic_item(*s, i.def_id())) + { let span = expr.span; cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { - let message = "Call to noop method"; + let message = "call to noop method"; lint.build(&message).emit() }); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2207deb8fb5a0..d39fbd61962e7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -789,6 +789,9 @@ symbols! { none_error, nontemporal_store, nontrapping_dash_fptoint: "nontrapping-fptoint", + noop_method_borrow, + noop_method_clone, + noop_method_deref, noreturn, nostack, not, @@ -915,7 +918,6 @@ symbols! { receiver, recursion_limit, reexport_test_harness_main, - ref_clone_method, reference, reflect, reg, diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index c9040cd0a1670..af2ad12dddf0a 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -219,6 +219,7 @@ impl BorrowMut for T { #[stable(feature = "rust1", since = "1.0.0")] impl Borrow for &T { + #[rustc_diagnostic_item = "noop_method_borrow"] fn borrow(&self) -> &T { &**self } diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 12f0f9629a31f..12b4feeb14ae3 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -104,7 +104,6 @@ /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "clone"] -#[rustc_diagnostic_item = "Clone"] pub trait Clone: Sized { /// Returns a copy of the value. /// @@ -222,7 +221,7 @@ mod impls { #[stable(feature = "rust1", since = "1.0.0")] impl Clone for &T { #[inline] - #[rustc_diagnostic_item = "ref_clone_method"] + #[rustc_diagnostic_item = "noop_method_clone"] fn clone(&self) -> Self { *self } diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 2419771eae212..d503a30174191 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -78,6 +78,7 @@ pub trait Deref { impl Deref for &T { type Target = T; + #[rustc_diagnostic_item = "noop_method_deref"] fn deref(&self) -> &T { *self } diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs new file mode 100644 index 0000000000000..4b81e04d3f7f9 --- /dev/null +++ b/src/test/ui/lint/noop-method-call.rs @@ -0,0 +1,45 @@ +// check-pass + +#![allow(unused)] + +use std::borrow::Borrow; +use std::ops::Deref; + +struct Foo(T); + +#[derive(Clone)] +struct Bar(T); + +struct DerefExample(T); + +impl Deref for DerefExample { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn main() { + let foo = &Foo(1u32); + let foo_clone: &Foo = foo.clone(); //~ WARNING call to noop method + + let bar = &Bar(1u32); + let bar_clone: Bar = bar.clone(); + + let deref = &&DerefExample(12u32); + let derefed: &DerefExample = deref.deref(); //~ WARNING call to noop method + + let deref = &DerefExample(12u32); + let derefed: &u32 = deref.deref(); + + let a = &&Foo(1u32); + let borrowed: &Foo = a.borrow(); //~ WARNING call to noop method +} + +fn generic(foo: &Foo) { + foo.clone(); +} + +fn non_generic(foo: &Foo) { + foo.clone(); //~ WARNING call to noop method +} diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr new file mode 100644 index 0000000000000..1120adee121b1 --- /dev/null +++ b/src/test/ui/lint/noop-method-call.stderr @@ -0,0 +1,28 @@ +warning: call to noop method + --> $DIR/noop-method-call.rs:24:32 + | +LL | let foo_clone: &Foo = foo.clone(); + | ^^^^^^^^^^^ + | + = note: `#[warn(noop_method_call)]` on by default + +warning: call to noop method + --> $DIR/noop-method-call.rs:30:39 + | +LL | let derefed: &DerefExample = deref.deref(); + | ^^^^^^^^^^^^^ + +warning: call to noop method + --> $DIR/noop-method-call.rs:36:31 + | +LL | let borrowed: &Foo = a.borrow(); + | ^^^^^^^^^^ + +warning: call to noop method + --> $DIR/noop-method-call.rs:44:5 + | +LL | foo.clone(); + | ^^^^^^^^^^^ + +warning: 4 warnings emitted + From a6d926d80db4a52398dea0cf29e6c501eab50170 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 5 Jan 2021 16:46:50 +0100 Subject: [PATCH 29/62] Fix tests --- compiler/rustc_codegen_ssa/src/back/rpath.rs | 2 +- compiler/rustc_lint/src/noop_method_call.rs | 9 ++++++++- compiler/rustc_middle/src/ty/error.rs | 2 -- compiler/rustc_mir/src/borrow_check/invalidation.rs | 8 ++++---- compiler/rustc_mir_build/src/build/matches/test.rs | 2 +- compiler/rustc_span/src/symbol.rs | 2 ++ .../src/traits/error_reporting/mod.rs | 2 +- compiler/rustc_traits/src/chalk/mod.rs | 2 +- compiler/rustc_typeck/src/check/callee.rs | 2 +- compiler/rustc_typeck/src/check/expr.rs | 2 +- library/core/src/borrow.rs | 1 + library/core/src/clone.rs | 1 + library/core/src/ops/deref.rs | 1 + src/test/ui/issues/issue-11820.rs | 10 ++++++---- src/test/ui/underscore-imports/cycle.rs | 1 + src/test/ui/underscore-imports/hygiene-2.rs | 1 + src/test/ui/underscore-imports/macro-expanded.rs | 1 + 17 files changed, 32 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 005d2efdd3b26..5f21046b05e47 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -24,7 +24,7 @@ pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec { debug!("preparing the RPATH!"); - let libs = config.used_crates.clone(); + let libs = config.used_crates; let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::>(); let rpaths = get_rpaths(config, &libs); let mut flags = rpaths_to_flags(&rpaths); diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 3ab3fab4272c7..dad557128f8b7 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -46,6 +46,14 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { { // Check that we're dealing with a trait method if let Some(trait_id) = cx.tcx.trait_of_item(did) { + // Check we're dealing with one of the traits we care about + if ![sym::Clone, sym::Deref, sym::Borrow] + .iter() + .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) + { + return; + } + let substs = cx.typeck_results().node_substs(expr.hir_id); // We can't resolve on types that recursively require monomorphization, // so check that we don't need to perfom substitution @@ -54,7 +62,6 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { // Resolve the trait method instance if let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) { // Check that it implements the noop diagnostic - tracing::debug!("Resolves to: {:?}", i.def_id()); if [ sym::noop_method_borrow, sym::noop_method_clone, diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index bf315c81588a9..f19cc99844926 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -12,7 +12,6 @@ use rustc_target::spec::abi; use std::borrow::Cow; use std::fmt; -use std::ops::Deref; #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] pub struct ExpectedFound { @@ -548,7 +547,6 @@ impl Trait for X { TargetFeatureCast(def_id) => { let attrs = self.get_attrs(*def_id); let target_spans = attrs - .deref() .iter() .filter(|attr| attr.has_name(sym::target_feature)) .map(|attr| attr.span); diff --git a/compiler/rustc_mir/src/borrow_check/invalidation.rs b/compiler/rustc_mir/src/borrow_check/invalidation.rs index 8c05e6fd5d0e4..e423e449746fc 100644 --- a/compiler/rustc_mir/src/borrow_check/invalidation.rs +++ b/compiler/rustc_mir/src/borrow_check/invalidation.rs @@ -165,7 +165,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.consume_operand(location, value); // Invalidate all borrows of local places - let borrow_set = self.borrow_set.clone(); + let borrow_set = self.borrow_set; let resume = self.location_table.start_index(resume.start_location()); for (i, data) in borrow_set.iter_enumerated() { if borrow_of_local_data(data.borrowed_place) { @@ -177,7 +177,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { // Invalidate all borrows of local places - let borrow_set = self.borrow_set.clone(); + let borrow_set = self.borrow_set; let start = self.location_table.start_index(location); for (i, data) in borrow_set.iter_enumerated() { if borrow_of_local_data(data.borrowed_place) { @@ -369,7 +369,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { ); let tcx = self.tcx; let body = self.body; - let borrow_set = self.borrow_set.clone(); + let borrow_set = self.borrow_set; let indices = self.borrow_set.indices(); each_borrow_involving_path( self, @@ -377,7 +377,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { body, location, (sd, place), - &borrow_set.clone(), + borrow_set, indices, |this, borrow_index, borrow| { match (rw, borrow.kind) { diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 126fb957a6a99..4db7debee7e8f 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -51,7 +51,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PatKind::Constant { value } => Test { span: match_pair.pattern.span, - kind: TestKind::Eq { value, ty: match_pair.pattern.ty.clone() }, + kind: TestKind::Eq { value, ty: match_pair.pattern.ty }, }, PatKind::Range(range) => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d39fbd61962e7..61f9a080a5217 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -129,6 +129,7 @@ symbols! { BTreeMap, BTreeSet, BinaryHeap, + Borrow, C, CString, Center, @@ -141,6 +142,7 @@ symbols! { Decodable, Decoder, Default, + Deref, Encodable, Encoder, Eq, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index bfb5ebcea58b1..a3faf4cb7d4c1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -819,7 +819,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { sig.decl .inputs .iter() - .map(|arg| match arg.clone().kind { + .map(|arg| match arg.kind { hir::TyKind::Tup(ref tys) => ArgKind::Tuple( Some(arg.span), vec![("_".to_owned(), "_".to_owned()); tys.len()], diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index d98f18182c843..b7275bac19048 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -165,7 +165,7 @@ crate fn evaluate_goal<'tcx>( // let's just ignore that let sol = Canonical { max_universe: ty::UniverseIndex::from_usize(0), - variables: obligation.variables.clone(), + variables: obligation.variables, value: QueryResponse { var_values: CanonicalVarValues { var_values: IndexVec::new() } .make_identity(tcx), diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index ca1e79fac73e9..6ef63bcbbbf21 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -465,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expected_arg_tys = self.expected_inputs_for_expected_output( call_expr.span, expected, - fn_sig.output().clone(), + fn_sig.output(), fn_sig.inputs(), ); diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 2faf128c491fd..48740e533da8e 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -711,7 +711,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let ret_ty = ret_coercion.borrow().expected_ty(); - let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty.clone()); + let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty); ret_coercion.borrow_mut().coerce( self, &self.cause(return_expr.span, ObligationCauseCode::ReturnValue(return_expr.hir_id)), diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index af2ad12dddf0a..a0cdf681f67e1 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -153,6 +153,7 @@ /// [`HashMap`]: ../../std/collections/struct.HashMap.html /// [`String`]: ../../std/string/struct.String.html #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "Borrow"] pub trait Borrow { /// Immutably borrows from an owned value. /// diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 12b4feeb14ae3..957769cdc5a62 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -104,6 +104,7 @@ /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "clone"] +#[rustc_diagnostic_item = "Clone"] pub trait Clone: Sized { /// Returns a copy of the value. /// diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index d503a30174191..10e3ce67448c8 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -60,6 +60,7 @@ #[doc(alias = "*")] #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "Deref"] pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/test/ui/issues/issue-11820.rs b/src/test/ui/issues/issue-11820.rs index 7ffe9652797cf..8a26624a05dc9 100644 --- a/src/test/ui/issues/issue-11820.rs +++ b/src/test/ui/issues/issue-11820.rs @@ -1,12 +1,14 @@ // run-pass // pretty-expanded FIXME #23616 +#![allow(noop_method_call)] + struct NoClone; fn main() { - let rnc = &NoClone; - let rsnc = &Some(NoClone); + let rnc = &NoClone; + let rsnc = &Some(NoClone); - let _: &NoClone = rnc.clone(); - let _: &Option = rsnc.clone(); + let _: &NoClone = rnc.clone(); + let _: &Option = rsnc.clone(); } diff --git a/src/test/ui/underscore-imports/cycle.rs b/src/test/ui/underscore-imports/cycle.rs index bacf9b2d5a96a..987410fa84b20 100644 --- a/src/test/ui/underscore-imports/cycle.rs +++ b/src/test/ui/underscore-imports/cycle.rs @@ -14,5 +14,6 @@ mod y { pub fn main() { use x::*; + #[allow(noop_method_call)] (&0).deref(); } diff --git a/src/test/ui/underscore-imports/hygiene-2.rs b/src/test/ui/underscore-imports/hygiene-2.rs index bea61eae6b51a..510d91d0d4624 100644 --- a/src/test/ui/underscore-imports/hygiene-2.rs +++ b/src/test/ui/underscore-imports/hygiene-2.rs @@ -29,5 +29,6 @@ m!(y); fn main() { use crate::y::*; + #[allow(noop_method_call)] (&()).deref(); } diff --git a/src/test/ui/underscore-imports/macro-expanded.rs b/src/test/ui/underscore-imports/macro-expanded.rs index 43f527bc9a408..55e86e848558d 100644 --- a/src/test/ui/underscore-imports/macro-expanded.rs +++ b/src/test/ui/underscore-imports/macro-expanded.rs @@ -3,6 +3,7 @@ // check-pass #![feature(decl_macro, rustc_attrs)] +#![allow(noop_method_call)] mod x { pub use std::ops::Not as _; From 618c395a8da1e284a2963b8da02450eb162e0478 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 6 Jan 2021 14:48:06 +0100 Subject: [PATCH 30/62] Bless test where order of error message changed --- src/test/ui/panic-handler/weak-lang-item.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/panic-handler/weak-lang-item.stderr b/src/test/ui/panic-handler/weak-lang-item.stderr index 68e3e21df3e08..b7c040c7a850b 100644 --- a/src/test/ui/panic-handler/weak-lang-item.stderr +++ b/src/test/ui/panic-handler/weak-lang-item.stderr @@ -10,10 +10,10 @@ help: you can use `as` to change the binding name of the import LL | extern crate core as other_core; | -error: language item required, but not found: `eh_personality` - error: `#[panic_handler]` function required, but not found +error: language item required, but not found: `eh_personality` + error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0259`. From 3a86184777e98935bef9b9662d090d6886c3c041 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 6 Jan 2021 17:56:34 +0100 Subject: [PATCH 31/62] Fix ui-full-deps suite --- library/alloc/src/collections/btree/map/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 4e48db7f49305..e636e490e1bf4 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -1801,11 +1801,11 @@ fn test_occupied_entry_key() { let key = "hello there"; let value = "value goes here"; assert!(a.is_empty()); - a.insert(key.clone(), value.clone()); + a.insert(key, value); assert_eq!(a.len(), 1); assert_eq!(a[key], value); - match a.entry(key.clone()) { + match a.entry(key) { Vacant(_) => panic!(), Occupied(e) => assert_eq!(key, *e.key()), } @@ -1821,11 +1821,11 @@ fn test_vacant_entry_key() { let value = "value goes here"; assert!(a.is_empty()); - match a.entry(key.clone()) { + match a.entry(key) { Occupied(_) => panic!(), Vacant(e) => { assert_eq!(key, *e.key()); - e.insert(value.clone()); + e.insert(value); } } assert_eq!(a.len(), 1); From ee65416f0d50331b5fef4360ce36538028edebf9 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 6 Jan 2021 21:11:08 +0100 Subject: [PATCH 32/62] Fix core tests --- library/core/src/clone.rs | 1 + library/core/tests/clone.rs | 2 ++ library/core/tests/iter/adapters/intersperse.rs | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 957769cdc5a62..51a2dc03de318 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -111,6 +111,7 @@ pub trait Clone: Sized { /// # Examples /// /// ``` + /// # #![allow(noop_method_call)] /// let hello = "Hello"; // &str implements Clone /// /// assert_eq!("Hello", hello.clone()); diff --git a/library/core/tests/clone.rs b/library/core/tests/clone.rs index c97a87aebce41..e5787d79c3226 100644 --- a/library/core/tests/clone.rs +++ b/library/core/tests/clone.rs @@ -1,3 +1,5 @@ +#![allow(noop_method_call)] + #[test] fn test_borrowed_clone() { let x = 5; diff --git a/library/core/tests/iter/adapters/intersperse.rs b/library/core/tests/iter/adapters/intersperse.rs index 9dbe232e4eec8..b336c03b5adbe 100644 --- a/library/core/tests/iter/adapters/intersperse.rs +++ b/library/core/tests/iter/adapters/intersperse.rs @@ -9,7 +9,7 @@ fn test_intersperse() { assert_eq!(v, vec![1]); let xs = ["a", "", "b", "c"]; - let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect(); + let v: Vec<&str> = xs.iter().map(|x| *x).intersperse(", ").collect(); let text: String = v.concat(); assert_eq!(text, "a, , b, c".to_string()); @@ -24,7 +24,7 @@ fn test_intersperse_size_hint() { assert_eq!(iter.size_hint(), (0, Some(0))); let xs = ["a", "", "b", "c"]; - let mut iter = xs.iter().map(|x| x.clone()).intersperse(", "); + let mut iter = xs.iter().map(|x| *x).intersperse(", "); assert_eq!(iter.size_hint(), (7, Some(7))); assert_eq!(iter.next(), Some("a")); From d3b49c2ed2c7bf35e6155ea25acbf657d8224eac Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 7 Jan 2021 11:24:32 +0100 Subject: [PATCH 33/62] Only allow new lint when not bootstrapping - since beta doesn't know about the lint --- library/core/tests/clone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/tests/clone.rs b/library/core/tests/clone.rs index e5787d79c3226..2f2aa9a20f912 100644 --- a/library/core/tests/clone.rs +++ b/library/core/tests/clone.rs @@ -1,4 +1,4 @@ -#![allow(noop_method_call)] +#![cfg_attr(not(bootstrap), allow(noop_method_call))] #[test] fn test_borrowed_clone() { From c5ff54cbdb6b0da742110b416cdbcf0ca0ff0fc4 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Thu, 7 Jan 2021 13:13:25 +0100 Subject: [PATCH 34/62] Fix std tests --- library/std/src/collections/hash/map/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 467968354e25d..819be14222752 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -774,11 +774,11 @@ fn test_occupied_entry_key() { let key = "hello there"; let value = "value goes here"; assert!(a.is_empty()); - a.insert(key.clone(), value.clone()); + a.insert(key, value); assert_eq!(a.len(), 1); assert_eq!(a[key], value); - match a.entry(key.clone()) { + match a.entry(key) { Vacant(_) => panic!(), Occupied(e) => assert_eq!(key, *e.key()), } @@ -793,11 +793,11 @@ fn test_vacant_entry_key() { let value = "value goes here"; assert!(a.is_empty()); - match a.entry(key.clone()) { + match a.entry(key) { Occupied(_) => panic!(), Vacant(e) => { assert_eq!(key, *e.key()); - e.insert(value.clone()); + e.insert(value); } } assert_eq!(a.len(), 1); From 217c88655b1155796739edbf415e7ce37d30830b Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 8 Jan 2021 11:37:52 +0100 Subject: [PATCH 35/62] Improve warning --- compiler/rustc_lint/src/noop_method_call.rs | 10 ++++++---- src/test/ui/lint/noop-method-call.rs | 8 ++++---- src/test/ui/lint/noop-method-call.stderr | 16 ++++++++-------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index dad557128f8b7..b9b5009d9dd95 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -70,11 +70,13 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { .iter() .any(|s| cx.tcx.is_diagnostic_item(*s, i.def_id())) { - let span = expr.span; + let expr_span = expr.span; - cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { - let message = "call to noop method"; - lint.build(&message).emit() + cx.struct_span_lint(NOOP_METHOD_CALL, expr_span, |lint| { + let message = "call to method that does nothing"; + lint.build(&message) + .span_label(expr_span, "unnecessary method call") + .emit() }); } } diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs index 4b81e04d3f7f9..b8ff75845bd7d 100644 --- a/src/test/ui/lint/noop-method-call.rs +++ b/src/test/ui/lint/noop-method-call.rs @@ -21,19 +21,19 @@ impl Deref for DerefExample { fn main() { let foo = &Foo(1u32); - let foo_clone: &Foo = foo.clone(); //~ WARNING call to noop method + let foo_clone: &Foo = foo.clone(); //~ WARNING call to method that does nothing [noop_method_call] let bar = &Bar(1u32); let bar_clone: Bar = bar.clone(); let deref = &&DerefExample(12u32); - let derefed: &DerefExample = deref.deref(); //~ WARNING call to noop method + let derefed: &DerefExample = deref.deref(); //~ WARNING call to method that does nothing [noop_method_call] let deref = &DerefExample(12u32); let derefed: &u32 = deref.deref(); let a = &&Foo(1u32); - let borrowed: &Foo = a.borrow(); //~ WARNING call to noop method + let borrowed: &Foo = a.borrow(); //~ WARNING call to method that does nothing [noop_method_call] } fn generic(foo: &Foo) { @@ -41,5 +41,5 @@ fn generic(foo: &Foo) { } fn non_generic(foo: &Foo) { - foo.clone(); //~ WARNING call to noop method + foo.clone(); //~ WARNING call to method that does nothing [noop_method_call] } diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr index 1120adee121b1..f5b766f42333f 100644 --- a/src/test/ui/lint/noop-method-call.stderr +++ b/src/test/ui/lint/noop-method-call.stderr @@ -1,28 +1,28 @@ -warning: call to noop method +warning: call to method that does nothing --> $DIR/noop-method-call.rs:24:32 | LL | let foo_clone: &Foo = foo.clone(); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ unnecessary method call | = note: `#[warn(noop_method_call)]` on by default -warning: call to noop method +warning: call to method that does nothing --> $DIR/noop-method-call.rs:30:39 | LL | let derefed: &DerefExample = deref.deref(); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ unnecessary method call -warning: call to noop method +warning: call to method that does nothing --> $DIR/noop-method-call.rs:36:31 | LL | let borrowed: &Foo = a.borrow(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ unnecessary method call -warning: call to noop method +warning: call to method that does nothing --> $DIR/noop-method-call.rs:44:5 | LL | foo.clone(); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ unnecessary method call warning: 4 warnings emitted From 16c4afbde4bcc0b2471cd48cf29378d557a8f4ab Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Fri, 8 Jan 2021 12:09:29 +0100 Subject: [PATCH 36/62] Fix tidy errors --- src/test/ui/lint/noop-method-call.rs | 9 ++++++--- src/test/ui/lint/noop-method-call.stderr | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs index b8ff75845bd7d..9f0ab3960f8dd 100644 --- a/src/test/ui/lint/noop-method-call.rs +++ b/src/test/ui/lint/noop-method-call.rs @@ -21,19 +21,22 @@ impl Deref for DerefExample { fn main() { let foo = &Foo(1u32); - let foo_clone: &Foo = foo.clone(); //~ WARNING call to method that does nothing [noop_method_call] + let foo_clone: &Foo = foo.clone(); + //~^ WARNING call to method that does nothing [noop_method_call] let bar = &Bar(1u32); let bar_clone: Bar = bar.clone(); let deref = &&DerefExample(12u32); - let derefed: &DerefExample = deref.deref(); //~ WARNING call to method that does nothing [noop_method_call] + let derefed: &DerefExample = deref.deref(); + //~^ WARNING call to method that does nothing [noop_method_call] let deref = &DerefExample(12u32); let derefed: &u32 = deref.deref(); let a = &&Foo(1u32); - let borrowed: &Foo = a.borrow(); //~ WARNING call to method that does nothing [noop_method_call] + let borrowed: &Foo = a.borrow(); + //~^ WARNING call to method that does nothing [noop_method_call] } fn generic(foo: &Foo) { diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr index f5b766f42333f..32acf1632336d 100644 --- a/src/test/ui/lint/noop-method-call.stderr +++ b/src/test/ui/lint/noop-method-call.stderr @@ -7,19 +7,19 @@ LL | let foo_clone: &Foo = foo.clone(); = note: `#[warn(noop_method_call)]` on by default warning: call to method that does nothing - --> $DIR/noop-method-call.rs:30:39 + --> $DIR/noop-method-call.rs:31:39 | LL | let derefed: &DerefExample = deref.deref(); | ^^^^^^^^^^^^^ unnecessary method call warning: call to method that does nothing - --> $DIR/noop-method-call.rs:36:31 + --> $DIR/noop-method-call.rs:38:31 | LL | let borrowed: &Foo = a.borrow(); | ^^^^^^^^^^ unnecessary method call warning: call to method that does nothing - --> $DIR/noop-method-call.rs:44:5 + --> $DIR/noop-method-call.rs:47:5 | LL | foo.clone(); | ^^^^^^^^^^^ unnecessary method call From 95e330bd0197a658096dfa1922ec3b3e6a4e6b78 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Mon, 11 Jan 2021 11:47:16 +0100 Subject: [PATCH 37/62] Update error message --- compiler/rustc_lint/src/noop_method_call.rs | 9 ++++++--- src/test/ui/issues/issue-11820.rs | 8 ++++---- src/test/ui/lint/noop-method-call.rs | 9 +++++---- src/test/ui/lint/noop-method-call.stderr | 19 +++++++++++++++---- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index b9b5009d9dd95..e91dd37d8aaae 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -39,7 +39,7 @@ declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // We only care about method calls - if let ExprKind::MethodCall(..) = expr.kind { + if let ExprKind::MethodCall(call, ..) = expr.kind { // Get the `DefId` only when dealing with an `AssocFn` if let Some((DefKind::AssocFn, did)) = cx.typeck_results().type_dependent_def(expr.hir_id) @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { } let substs = cx.typeck_results().node_substs(expr.hir_id); - // We can't resolve on types that recursively require monomorphization, + // We can't resolve on types that require monomorphization, // so check that we don't need to perfom substitution if !substs.needs_subst() { let param_env = cx.tcx.param_env(trait_id); @@ -73,9 +73,12 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { let expr_span = expr.span; cx.struct_span_lint(NOOP_METHOD_CALL, expr_span, |lint| { - let message = "call to method that does nothing"; + let method = &call.ident.name; + let message = format!("call to `.{}()` on a reference in this situation does nothing", &method); lint.build(&message) .span_label(expr_span, "unnecessary method call") + .note("the type the method is being called on and the return type are functionally equivalent.") + .note("therefore, the method call doesn't actually do anything and can be removed.") .emit() }); } diff --git a/src/test/ui/issues/issue-11820.rs b/src/test/ui/issues/issue-11820.rs index 8a26624a05dc9..dc6349b10ee58 100644 --- a/src/test/ui/issues/issue-11820.rs +++ b/src/test/ui/issues/issue-11820.rs @@ -6,9 +6,9 @@ struct NoClone; fn main() { - let rnc = &NoClone; - let rsnc = &Some(NoClone); + let rnc = &NoClone; + let rsnc = &Some(NoClone); - let _: &NoClone = rnc.clone(); - let _: &Option = rsnc.clone(); + let _: &NoClone = rnc.clone(); + let _: &Option = rsnc.clone(); } diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs index 9f0ab3960f8dd..b8aa55e1e1da6 100644 --- a/src/test/ui/lint/noop-method-call.rs +++ b/src/test/ui/lint/noop-method-call.rs @@ -22,21 +22,21 @@ impl Deref for DerefExample { fn main() { let foo = &Foo(1u32); let foo_clone: &Foo = foo.clone(); - //~^ WARNING call to method that does nothing [noop_method_call] + //~^ WARNING call to `.clone()` on a reference in this situation does nothing [noop_method_call] let bar = &Bar(1u32); let bar_clone: Bar = bar.clone(); let deref = &&DerefExample(12u32); let derefed: &DerefExample = deref.deref(); - //~^ WARNING call to method that does nothing [noop_method_call] + //~^ WARNING call to `.deref()` on a reference in this situation does nothing [noop_method_call] let deref = &DerefExample(12u32); let derefed: &u32 = deref.deref(); let a = &&Foo(1u32); let borrowed: &Foo = a.borrow(); - //~^ WARNING call to method that does nothing [noop_method_call] + //~^ WARNING call to `.borrow()` on a reference in this situation does nothing [noop_method_call] } fn generic(foo: &Foo) { @@ -44,5 +44,6 @@ fn generic(foo: &Foo) { } fn non_generic(foo: &Foo) { - foo.clone(); //~ WARNING call to method that does nothing [noop_method_call] + foo.clone(); + //~^ WARNING call to `.clone()` on a reference in this situation does nothing [noop_method_call] } diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr index 32acf1632336d..f9cc9735d54f6 100644 --- a/src/test/ui/lint/noop-method-call.stderr +++ b/src/test/ui/lint/noop-method-call.stderr @@ -1,28 +1,39 @@ -warning: call to method that does nothing +warning: call to `.clone()` on a reference in this situation does nothing --> $DIR/noop-method-call.rs:24:32 | LL | let foo_clone: &Foo = foo.clone(); | ^^^^^^^^^^^ unnecessary method call | = note: `#[warn(noop_method_call)]` on by default + = note: the type the method is being called on and the return type are functionally equivalent. + = note: therefore, the method call doesn't actually do anything and can be removed. -warning: call to method that does nothing +warning: call to `.deref()` on a reference in this situation does nothing --> $DIR/noop-method-call.rs:31:39 | LL | let derefed: &DerefExample = deref.deref(); | ^^^^^^^^^^^^^ unnecessary method call + | + = note: the type the method is being called on and the return type are functionally equivalent. + = note: therefore, the method call doesn't actually do anything and can be removed. -warning: call to method that does nothing +warning: call to `.borrow()` on a reference in this situation does nothing --> $DIR/noop-method-call.rs:38:31 | LL | let borrowed: &Foo = a.borrow(); | ^^^^^^^^^^ unnecessary method call + | + = note: the type the method is being called on and the return type are functionally equivalent. + = note: therefore, the method call doesn't actually do anything and can be removed. -warning: call to method that does nothing +warning: call to `.clone()` on a reference in this situation does nothing --> $DIR/noop-method-call.rs:47:5 | LL | foo.clone(); | ^^^^^^^^^^^ unnecessary method call + | + = note: the type the method is being called on and the return type are functionally equivalent. + = note: therefore, the method call doesn't actually do anything and can be removed. warning: 4 warnings emitted From 316e9db0cf86852b0150d8f0d475ba9a1dcc4774 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Mon, 11 Jan 2021 19:02:19 +0100 Subject: [PATCH 38/62] Fix tidy error --- src/test/ui/lint/noop-method-call.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs index b8aa55e1e1da6..bc61d619abe82 100644 --- a/src/test/ui/lint/noop-method-call.rs +++ b/src/test/ui/lint/noop-method-call.rs @@ -22,21 +22,21 @@ impl Deref for DerefExample { fn main() { let foo = &Foo(1u32); let foo_clone: &Foo = foo.clone(); - //~^ WARNING call to `.clone()` on a reference in this situation does nothing [noop_method_call] + //~^ WARNING call to `.clone()` on a reference in this situation does nothing let bar = &Bar(1u32); let bar_clone: Bar = bar.clone(); let deref = &&DerefExample(12u32); let derefed: &DerefExample = deref.deref(); - //~^ WARNING call to `.deref()` on a reference in this situation does nothing [noop_method_call] + //~^ WARNING call to `.deref()` on a reference in this situation does nothing let deref = &DerefExample(12u32); let derefed: &u32 = deref.deref(); let a = &&Foo(1u32); let borrowed: &Foo = a.borrow(); - //~^ WARNING call to `.borrow()` on a reference in this situation does nothing [noop_method_call] + //~^ WARNING call to `.borrow()` on a reference in this situation does nothing } fn generic(foo: &Foo) { @@ -45,5 +45,5 @@ fn generic(foo: &Foo) { fn non_generic(foo: &Foo) { foo.clone(); - //~^ WARNING call to `.clone()` on a reference in this situation does nothing [noop_method_call] + //~^ WARNING call to `.clone()` on a reference in this situation does nothing } From 49f32e0c8e78948210654299a8c19b2e0f0cbfa9 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 12 Jan 2021 14:58:51 +0100 Subject: [PATCH 39/62] Improve error messages --- compiler/rustc_lint/src/noop_method_call.rs | 18 +++++++++---- src/test/ui/lint/noop-method-call.stderr | 28 +++++++++------------ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index e91dd37d8aaae..1aa4a986cd1fe 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -39,7 +39,7 @@ declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // We only care about method calls - if let ExprKind::MethodCall(call, ..) = expr.kind { + if let ExprKind::MethodCall(call, _, elements, _) = expr.kind { // Get the `DefId` only when dealing with an `AssocFn` if let Some((DefKind::AssocFn, did)) = cx.typeck_results().type_dependent_def(expr.hir_id) @@ -70,15 +70,23 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { .iter() .any(|s| cx.tcx.is_diagnostic_item(*s, i.def_id())) { + let method = &call.ident.name; + let receiver = &elements[0]; + let receiver_ty = cx.typeck_results().expr_ty(receiver); let expr_span = expr.span; + let note = format!( + "the type `{:?}` which `{}` is being called on is the same as the type returned from `{}`, \ + so the method call does not do anything and can be removed.", + receiver_ty, method, method + ); - cx.struct_span_lint(NOOP_METHOD_CALL, expr_span, |lint| { + let span = expr_span.with_lo(receiver.span.hi()); + cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { let method = &call.ident.name; let message = format!("call to `.{}()` on a reference in this situation does nothing", &method); lint.build(&message) - .span_label(expr_span, "unnecessary method call") - .note("the type the method is being called on and the return type are functionally equivalent.") - .note("therefore, the method call doesn't actually do anything and can be removed.") + .span_label(span, "unnecessary method call") + .note(¬e) .emit() }); } diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr index f9cc9735d54f6..7e27bf3abf9f3 100644 --- a/src/test/ui/lint/noop-method-call.stderr +++ b/src/test/ui/lint/noop-method-call.stderr @@ -1,39 +1,35 @@ warning: call to `.clone()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:24:32 + --> $DIR/noop-method-call.rs:24:35 | LL | let foo_clone: &Foo = foo.clone(); - | ^^^^^^^^^^^ unnecessary method call + | ^^^^^^^^ unnecessary method call | = note: `#[warn(noop_method_call)]` on by default - = note: the type the method is being called on and the return type are functionally equivalent. - = note: therefore, the method call doesn't actually do anything and can be removed. + = note: the type `&Foo` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed. warning: call to `.deref()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:31:39 + --> $DIR/noop-method-call.rs:31:44 | LL | let derefed: &DerefExample = deref.deref(); - | ^^^^^^^^^^^^^ unnecessary method call + | ^^^^^^^^ unnecessary method call | - = note: the type the method is being called on and the return type are functionally equivalent. - = note: therefore, the method call doesn't actually do anything and can be removed. + = note: the type `&&DerefExample` which `deref` is being called on is the same as the type returned from `deref`, so the method call does not do anything and can be removed. warning: call to `.borrow()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:38:31 + --> $DIR/noop-method-call.rs:38:32 | LL | let borrowed: &Foo = a.borrow(); - | ^^^^^^^^^^ unnecessary method call + | ^^^^^^^^^ unnecessary method call | - = note: the type the method is being called on and the return type are functionally equivalent. - = note: therefore, the method call doesn't actually do anything and can be removed. + = note: the type `&&Foo` which `borrow` is being called on is the same as the type returned from `borrow`, so the method call does not do anything and can be removed. warning: call to `.clone()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:47:5 + --> $DIR/noop-method-call.rs:47:8 | LL | foo.clone(); - | ^^^^^^^^^^^ unnecessary method call + | ^^^^^^^^ unnecessary method call | - = note: the type the method is being called on and the return type are functionally equivalent. - = note: therefore, the method call doesn't actually do anything and can be removed. + = note: the type `&Foo` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed. warning: 4 warnings emitted From e48670c34a3f5ab3fe9defcc61861999f3f14e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 12 Jan 2021 18:37:32 -0800 Subject: [PATCH 40/62] Increase accuracy of lint trigger --- compiler/rustc_lint/src/noop_method_call.rs | 58 +++++++++++++-------- src/test/ui/lint/noop-method-call.rs | 3 ++ src/test/ui/lint/noop-method-call.stderr | 10 ++-- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 1aa4a986cd1fe..04d87dc959273 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -62,33 +62,45 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { // Resolve the trait method instance if let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) { // Check that it implements the noop diagnostic - if [ - sym::noop_method_borrow, - sym::noop_method_clone, - sym::noop_method_deref, + for (s, peel_ref) in [ + (sym::noop_method_borrow, true), + (sym::noop_method_clone, false), + (sym::noop_method_deref, true), ] .iter() - .any(|s| cx.tcx.is_diagnostic_item(*s, i.def_id())) { - let method = &call.ident.name; - let receiver = &elements[0]; - let receiver_ty = cx.typeck_results().expr_ty(receiver); - let expr_span = expr.span; - let note = format!( - "the type `{:?}` which `{}` is being called on is the same as the type returned from `{}`, \ - so the method call does not do anything and can be removed.", - receiver_ty, method, method - ); - - let span = expr_span.with_lo(receiver.span.hi()); - cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { + if cx.tcx.is_diagnostic_item(*s, i.def_id()) { let method = &call.ident.name; - let message = format!("call to `.{}()` on a reference in this situation does nothing", &method); - lint.build(&message) - .span_label(span, "unnecessary method call") - .note(¬e) - .emit() - }); + let receiver = &elements[0]; + let receiver_ty = cx.typeck_results().expr_ty(receiver); + let receiver_ty = match receiver_ty.kind() { + // Remove one borrow from the receiver as all the trait methods + // we care about here have a `&self` receiver. + ty::Ref(_, ty, _) if *peel_ref => ty, + _ => receiver_ty, + }; + let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); + if receiver_ty != expr_ty { + return; + } + let expr_span = expr.span; + let note = format!( + "the type `{:?}` which `{}` is being called on is the same as \ + the type returned from `{}`, so the method call does not do \ + anything and can be removed", + receiver_ty, method, method, + ); + + let span = expr_span.with_lo(receiver.span.hi()); + cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { + let method = &call.ident.name; + let message = format!("call to `.{}()` on a reference in this situation does nothing", &method); + lint.build(&message) + .span_label(span, "unnecessary method call") + .note(¬e) + .emit() + }); + } } } } diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs index bc61d619abe82..70363a191e9e9 100644 --- a/src/test/ui/lint/noop-method-call.rs +++ b/src/test/ui/lint/noop-method-call.rs @@ -37,6 +37,9 @@ fn main() { let a = &&Foo(1u32); let borrowed: &Foo = a.borrow(); //~^ WARNING call to `.borrow()` on a reference in this situation does nothing + + let xs = ["a", "b", "c"]; + let _v: Vec<&str> = xs.iter().map(|x| x.clone()).collect(); // ok, but could use `*x` instead } fn generic(foo: &Foo) { diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr index 7e27bf3abf9f3..316c47975e6de 100644 --- a/src/test/ui/lint/noop-method-call.stderr +++ b/src/test/ui/lint/noop-method-call.stderr @@ -5,7 +5,7 @@ LL | let foo_clone: &Foo = foo.clone(); | ^^^^^^^^ unnecessary method call | = note: `#[warn(noop_method_call)]` on by default - = note: the type `&Foo` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed. + = note: the type `&Foo` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed warning: call to `.deref()` on a reference in this situation does nothing --> $DIR/noop-method-call.rs:31:44 @@ -13,7 +13,7 @@ warning: call to `.deref()` on a reference in this situation does nothing LL | let derefed: &DerefExample = deref.deref(); | ^^^^^^^^ unnecessary method call | - = note: the type `&&DerefExample` which `deref` is being called on is the same as the type returned from `deref`, so the method call does not do anything and can be removed. + = note: the type `&DerefExample` which `deref` is being called on is the same as the type returned from `deref`, so the method call does not do anything and can be removed warning: call to `.borrow()` on a reference in this situation does nothing --> $DIR/noop-method-call.rs:38:32 @@ -21,15 +21,15 @@ warning: call to `.borrow()` on a reference in this situation does nothing LL | let borrowed: &Foo = a.borrow(); | ^^^^^^^^^ unnecessary method call | - = note: the type `&&Foo` which `borrow` is being called on is the same as the type returned from `borrow`, so the method call does not do anything and can be removed. + = note: the type `&Foo` which `borrow` is being called on is the same as the type returned from `borrow`, so the method call does not do anything and can be removed warning: call to `.clone()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:47:8 + --> $DIR/noop-method-call.rs:50:8 | LL | foo.clone(); | ^^^^^^^^ unnecessary method call | - = note: the type `&Foo` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed. + = note: the type `&Foo` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed warning: 4 warnings emitted From 055db16479937d0a567b2318c775a25ab6cb7d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Jan 2021 10:39:25 -0800 Subject: [PATCH 41/62] Clean up code rightward drift --- compiler/rustc_lint/src/noop_method_call.rs | 144 +++++++++++--------- 1 file changed, 79 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 04d87dc959273..b4ee1f9157c82 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -38,73 +38,87 @@ declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // We only care about method calls - if let ExprKind::MethodCall(call, _, elements, _) = expr.kind { - // Get the `DefId` only when dealing with an `AssocFn` - if let Some((DefKind::AssocFn, did)) = - cx.typeck_results().type_dependent_def(expr.hir_id) - { - // Check that we're dealing with a trait method - if let Some(trait_id) = cx.tcx.trait_of_item(did) { - // Check we're dealing with one of the traits we care about - if ![sym::Clone, sym::Deref, sym::Borrow] + // We only care about method calls. + let (call, elements) = match expr.kind { + ExprKind::MethodCall(call, _, elements, _) => (call, elements), + _ => return, + }; + // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow` + // traits and ignore any other method call. + let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) { + // Verify we are dealing with a method/associated function. + Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) { + // Check that we're dealing with a trait method for one of the traits we care about. + Some(trait_id) + if [sym::Clone, sym::Deref, sym::Borrow] .iter() - .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) - { - return; - } - - let substs = cx.typeck_results().node_substs(expr.hir_id); - // We can't resolve on types that require monomorphization, - // so check that we don't need to perfom substitution - if !substs.needs_subst() { - let param_env = cx.tcx.param_env(trait_id); - // Resolve the trait method instance - if let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) { - // Check that it implements the noop diagnostic - for (s, peel_ref) in [ - (sym::noop_method_borrow, true), - (sym::noop_method_clone, false), - (sym::noop_method_deref, true), - ] - .iter() - { - if cx.tcx.is_diagnostic_item(*s, i.def_id()) { - let method = &call.ident.name; - let receiver = &elements[0]; - let receiver_ty = cx.typeck_results().expr_ty(receiver); - let receiver_ty = match receiver_ty.kind() { - // Remove one borrow from the receiver as all the trait methods - // we care about here have a `&self` receiver. - ty::Ref(_, ty, _) if *peel_ref => ty, - _ => receiver_ty, - }; - let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); - if receiver_ty != expr_ty { - return; - } - let expr_span = expr.span; - let note = format!( - "the type `{:?}` which `{}` is being called on is the same as \ - the type returned from `{}`, so the method call does not do \ - anything and can be removed", - receiver_ty, method, method, - ); - - let span = expr_span.with_lo(receiver.span.hi()); - cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { - let method = &call.ident.name; - let message = format!("call to `.{}()` on a reference in this situation does nothing", &method); - lint.build(&message) - .span_label(span, "unnecessary method call") - .note(¬e) - .emit() - }); - } - } - } - } + .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) => + { + (trait_id, did) } + _ => return, + }, + _ => return, + }; + let substs = cx.typeck_results().node_substs(expr.hir_id); + if substs.needs_subst() { + // We can't resolve on types that require monomorphization, so we don't handle them if + // we need to perfom substitution. + return; + } + let param_env = cx.tcx.param_env(trait_id); + // Resolve the trait method instance. + let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) { + Ok(Some(i)) => i, + _ => return, + }; + // (Re)check that it implements the noop diagnostic. + for (s, peel_ref) in [ + (sym::noop_method_borrow, true), + (sym::noop_method_clone, false), + (sym::noop_method_deref, true), + ] + .iter() + { + if cx.tcx.is_diagnostic_item(*s, i.def_id()) { + let method = &call.ident.name; + let receiver = &elements[0]; + let receiver_ty = cx.typeck_results().expr_ty(receiver); + let receiver_ty = match receiver_ty.kind() { + // Remove one borrow from the receiver if appropriate to positively verify that + // the receiver `&self` type and the return type are the same, depending on the + // involved trait being checked. + ty::Ref(_, ty, _) if *peel_ref => ty, + // When it comes to `Clone` we need to check the `receiver_ty` directly. + // FIXME: we must come up with a better strategy for this. + _ => receiver_ty, + }; + let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); + if receiver_ty != expr_ty { + // This lint will only trigger if the receiver type and resulting expression \ + // type are the same, implying that the method call is unnecessary. + return; + } + let expr_span = expr.span; + let note = format!( + "the type `{:?}` which `{}` is being called on is the same as \ + the type returned from `{}`, so the method call does not do \ + anything and can be removed", + receiver_ty, method, method, + ); + + let span = expr_span.with_lo(receiver.span.hi()); + cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| { + let method = &call.ident.name; + let message = format!( + "call to `.{}()` on a reference in this situation does nothing", + &method, + ); + lint.build(&message) + .span_label(span, "unnecessary method call") + .note(¬e) + .emit() + }); } } } From 4be7052b1a81f8f39d9ad23781e19f30d071f572 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Mon, 18 Jan 2021 14:15:19 +0100 Subject: [PATCH 42/62] Allow noop_method_call in clippy ui test --- src/tools/clippy/tests/ui/unnecessary_clone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.rs b/src/tools/clippy/tests/ui/unnecessary_clone.rs index 6770a7fac90fd..ce26634a995d9 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clone.rs +++ b/src/tools/clippy/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wraps)] +#![allow(unused, noop_method_call, clippy::redundant_clone, clippy::unnecessary_wraps)] use std::cell::RefCell; use std::rc::{self, Rc}; From 6bf6652616ca75dde30cbdd021942433ae519730 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 10 Feb 2021 11:08:00 +0100 Subject: [PATCH 43/62] Move unrelated ui test back to what it was before --- src/test/ui/panic-handler/weak-lang-item.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/panic-handler/weak-lang-item.stderr b/src/test/ui/panic-handler/weak-lang-item.stderr index b7c040c7a850b..68e3e21df3e08 100644 --- a/src/test/ui/panic-handler/weak-lang-item.stderr +++ b/src/test/ui/panic-handler/weak-lang-item.stderr @@ -10,10 +10,10 @@ help: you can use `as` to change the binding name of the import LL | extern crate core as other_core; | -error: `#[panic_handler]` function required, but not found - error: language item required, but not found: `eh_personality` +error: `#[panic_handler]` function required, but not found + error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0259`. From da3995f0ec3085de42dcce9e91dbb5662b2c99d3 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 16 Feb 2021 15:12:19 +0100 Subject: [PATCH 44/62] Remove lint pass on borrow and deref --- compiler/rustc_lint/src/noop_method_call.rs | 12 +----- compiler/rustc_span/src/symbol.rs | 3 -- library/core/src/borrow.rs | 2 - library/core/src/ops/deref.rs | 2 - src/test/ui/lint/noop-method-call.rs | 46 +++++++-------------- src/test/ui/lint/noop-method-call.stderr | 34 ++++----------- 6 files changed, 25 insertions(+), 74 deletions(-) diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index b4ee1f9157c82..335c3c575e766 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -50,9 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) { // Check that we're dealing with a trait method for one of the traits we care about. Some(trait_id) - if [sym::Clone, sym::Deref, sym::Borrow] - .iter() - .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) => + if [sym::Clone].iter().any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) => { (trait_id, did) } @@ -73,13 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { _ => return, }; // (Re)check that it implements the noop diagnostic. - for (s, peel_ref) in [ - (sym::noop_method_borrow, true), - (sym::noop_method_clone, false), - (sym::noop_method_deref, true), - ] - .iter() - { + for (s, peel_ref) in [(sym::noop_method_clone, false)].iter() { if cx.tcx.is_diagnostic_item(*s, i.def_id()) { let method = &call.ident.name; let receiver = &elements[0]; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 61f9a080a5217..f43b180e06321 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -142,7 +142,6 @@ symbols! { Decodable, Decoder, Default, - Deref, Encodable, Encoder, Eq, @@ -791,9 +790,7 @@ symbols! { none_error, nontemporal_store, nontrapping_dash_fptoint: "nontrapping-fptoint", - noop_method_borrow, noop_method_clone, - noop_method_deref, noreturn, nostack, not, diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index a0cdf681f67e1..c9040cd0a1670 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -153,7 +153,6 @@ /// [`HashMap`]: ../../std/collections/struct.HashMap.html /// [`String`]: ../../std/string/struct.String.html #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_diagnostic_item = "Borrow"] pub trait Borrow { /// Immutably borrows from an owned value. /// @@ -220,7 +219,6 @@ impl BorrowMut for T { #[stable(feature = "rust1", since = "1.0.0")] impl Borrow for &T { - #[rustc_diagnostic_item = "noop_method_borrow"] fn borrow(&self) -> &T { &**self } diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 10e3ce67448c8..2419771eae212 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -60,7 +60,6 @@ #[doc(alias = "*")] #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_diagnostic_item = "Deref"] pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] @@ -79,7 +78,6 @@ pub trait Deref { impl Deref for &T { type Target = T; - #[rustc_diagnostic_item = "noop_method_deref"] fn deref(&self) -> &T { *self } diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs index 70363a191e9e9..8e4b5bf4d12c7 100644 --- a/src/test/ui/lint/noop-method-call.rs +++ b/src/test/ui/lint/noop-method-call.rs @@ -2,51 +2,33 @@ #![allow(unused)] -use std::borrow::Borrow; -use std::ops::Deref; - -struct Foo(T); +struct NonCloneType(T); #[derive(Clone)] -struct Bar(T); - -struct DerefExample(T); - -impl Deref for DerefExample { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.0 - } -} +struct CloneType(T); fn main() { - let foo = &Foo(1u32); - let foo_clone: &Foo = foo.clone(); + let non_clone_type_ref = &NonCloneType(1u32); + let non_clone_type_ref_clone: &NonCloneType = non_clone_type_ref.clone(); //~^ WARNING call to `.clone()` on a reference in this situation does nothing - let bar = &Bar(1u32); - let bar_clone: Bar = bar.clone(); - - let deref = &&DerefExample(12u32); - let derefed: &DerefExample = deref.deref(); - //~^ WARNING call to `.deref()` on a reference in this situation does nothing - - let deref = &DerefExample(12u32); - let derefed: &u32 = deref.deref(); + let clone_type_ref = &CloneType(1u32); + let clone_type_ref_clone: CloneType = clone_type_ref.clone(); - let a = &&Foo(1u32); - let borrowed: &Foo = a.borrow(); - //~^ WARNING call to `.borrow()` on a reference in this situation does nothing + // Calling clone on a double reference doesn't warn since the method call itself + // peels the outer reference off + let clone_type_ref = &&CloneType(1u32); + let clone_type_ref_clone: &CloneType = clone_type_ref.clone(); let xs = ["a", "b", "c"]; let _v: Vec<&str> = xs.iter().map(|x| x.clone()).collect(); // ok, but could use `*x` instead } -fn generic(foo: &Foo) { - foo.clone(); +fn generic(non_clone_type: &NonCloneType) { + non_clone_type.clone(); } -fn non_generic(foo: &Foo) { - foo.clone(); +fn non_generic(non_clone_type: &NonCloneType) { + non_clone_type.clone(); //~^ WARNING call to `.clone()` on a reference in this situation does nothing } diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr index 316c47975e6de..85a67f538407c 100644 --- a/src/test/ui/lint/noop-method-call.stderr +++ b/src/test/ui/lint/noop-method-call.stderr @@ -1,35 +1,19 @@ warning: call to `.clone()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:24:35 + --> $DIR/noop-method-call.rs:12:74 | -LL | let foo_clone: &Foo = foo.clone(); - | ^^^^^^^^ unnecessary method call +LL | let non_clone_type_ref_clone: &NonCloneType = non_clone_type_ref.clone(); + | ^^^^^^^^ unnecessary method call | = note: `#[warn(noop_method_call)]` on by default - = note: the type `&Foo` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed - -warning: call to `.deref()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:31:44 - | -LL | let derefed: &DerefExample = deref.deref(); - | ^^^^^^^^ unnecessary method call - | - = note: the type `&DerefExample` which `deref` is being called on is the same as the type returned from `deref`, so the method call does not do anything and can be removed - -warning: call to `.borrow()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:38:32 - | -LL | let borrowed: &Foo = a.borrow(); - | ^^^^^^^^^ unnecessary method call - | - = note: the type `&Foo` which `borrow` is being called on is the same as the type returned from `borrow`, so the method call does not do anything and can be removed + = note: the type `&NonCloneType` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed warning: call to `.clone()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:50:8 + --> $DIR/noop-method-call.rs:32:19 | -LL | foo.clone(); - | ^^^^^^^^ unnecessary method call +LL | non_clone_type.clone(); + | ^^^^^^^^ unnecessary method call | - = note: the type `&Foo` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed + = note: the type `&NonCloneType` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed -warning: 4 warnings emitted +warning: 2 warnings emitted From 1999a3147f5ab65cd556d45e631be5c18fbaebf4 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 16 Feb 2021 22:39:05 +0100 Subject: [PATCH 45/62] Fix borrow and deref --- compiler/rustc_lint/src/noop_method_call.rs | 18 ++++------ compiler/rustc_span/src/symbol.rs | 3 ++ library/core/src/borrow.rs | 2 ++ library/core/src/ops/deref.rs | 2 ++ library/core/tests/clone.rs | 2 -- src/test/ui/issues/issue-11820.rs | 2 -- src/test/ui/lint/noop-method-call.rs | 30 +++++++++++++--- src/test/ui/lint/noop-method-call.stderr | 36 ++++++++++++++----- src/test/ui/underscore-imports/cycle.rs | 1 - .../ui/underscore-imports/macro-expanded.rs | 1 - .../clippy/tests/ui/unnecessary_clone.rs | 2 +- 11 files changed, 67 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 335c3c575e766..3a1b9c85fd1bd 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -15,6 +15,7 @@ declare_lint! { /// /// ```rust /// # #![allow(unused)] + /// #![deny(noop_method_call)] /// struct Foo; /// let foo = &Foo; /// let clone: &Foo = foo.clone(); @@ -30,7 +31,7 @@ declare_lint! { /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything /// as references are copy. This lint detects these calls and warns the user about them. pub NOOP_METHOD_CALL, - Warn, + Allow, "detects the use of well-known noop methods" } @@ -50,7 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) { // Check that we're dealing with a trait method for one of the traits we care about. Some(trait_id) - if [sym::Clone].iter().any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) => + if [sym::Clone, sym::Deref, sym::Borrow] + .iter() + .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) => { (trait_id, did) } @@ -71,20 +74,11 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { _ => return, }; // (Re)check that it implements the noop diagnostic. - for (s, peel_ref) in [(sym::noop_method_clone, false)].iter() { + for s in [sym::noop_method_clone, sym::noop_method_deref, sym::noop_method_borrow].iter() { if cx.tcx.is_diagnostic_item(*s, i.def_id()) { let method = &call.ident.name; let receiver = &elements[0]; let receiver_ty = cx.typeck_results().expr_ty(receiver); - let receiver_ty = match receiver_ty.kind() { - // Remove one borrow from the receiver if appropriate to positively verify that - // the receiver `&self` type and the return type are the same, depending on the - // involved trait being checked. - ty::Ref(_, ty, _) if *peel_ref => ty, - // When it comes to `Clone` we need to check the `receiver_ty` directly. - // FIXME: we must come up with a better strategy for this. - _ => receiver_ty, - }; let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); if receiver_ty != expr_ty { // This lint will only trigger if the receiver type and resulting expression \ diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f43b180e06321..61f9a080a5217 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -142,6 +142,7 @@ symbols! { Decodable, Decoder, Default, + Deref, Encodable, Encoder, Eq, @@ -790,7 +791,9 @@ symbols! { none_error, nontemporal_store, nontrapping_dash_fptoint: "nontrapping-fptoint", + noop_method_borrow, noop_method_clone, + noop_method_deref, noreturn, nostack, not, diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index c9040cd0a1670..f28be20aaa1e6 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -153,6 +153,7 @@ /// [`HashMap`]: ../../std/collections/struct.HashMap.html /// [`String`]: ../../std/string/struct.String.html #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "Borrow"] pub trait Borrow { /// Immutably borrows from an owned value. /// @@ -205,6 +206,7 @@ pub trait BorrowMut: Borrow { #[stable(feature = "rust1", since = "1.0.0")] impl Borrow for T { + #[rustc_diagnostic_item = "noop_method_borrow"] fn borrow(&self) -> &T { self } diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 2419771eae212..10e3ce67448c8 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -60,6 +60,7 @@ #[doc(alias = "*")] #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "Deref"] pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] @@ -78,6 +79,7 @@ pub trait Deref { impl Deref for &T { type Target = T; + #[rustc_diagnostic_item = "noop_method_deref"] fn deref(&self) -> &T { *self } diff --git a/library/core/tests/clone.rs b/library/core/tests/clone.rs index 2f2aa9a20f912..c97a87aebce41 100644 --- a/library/core/tests/clone.rs +++ b/library/core/tests/clone.rs @@ -1,5 +1,3 @@ -#![cfg_attr(not(bootstrap), allow(noop_method_call))] - #[test] fn test_borrowed_clone() { let x = 5; diff --git a/src/test/ui/issues/issue-11820.rs b/src/test/ui/issues/issue-11820.rs index dc6349b10ee58..7ffe9652797cf 100644 --- a/src/test/ui/issues/issue-11820.rs +++ b/src/test/ui/issues/issue-11820.rs @@ -1,8 +1,6 @@ // run-pass // pretty-expanded FIXME #23616 -#![allow(noop_method_call)] - struct NoClone; fn main() { diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs index 8e4b5bf4d12c7..9870c813572e3 100644 --- a/src/test/ui/lint/noop-method-call.rs +++ b/src/test/ui/lint/noop-method-call.rs @@ -1,15 +1,19 @@ // check-pass #![allow(unused)] +#![warn(noop_method_call)] -struct NonCloneType(T); +use std::borrow::Borrow; +use std::ops::Deref; + +struct PlainType(T); #[derive(Clone)] struct CloneType(T); fn main() { - let non_clone_type_ref = &NonCloneType(1u32); - let non_clone_type_ref_clone: &NonCloneType = non_clone_type_ref.clone(); + let non_clone_type_ref = &PlainType(1u32); + let non_clone_type_ref_clone: &PlainType = non_clone_type_ref.clone(); //~^ WARNING call to `.clone()` on a reference in this situation does nothing let clone_type_ref = &CloneType(1u32); @@ -20,15 +24,31 @@ fn main() { let clone_type_ref = &&CloneType(1u32); let clone_type_ref_clone: &CloneType = clone_type_ref.clone(); + let non_deref_type = &PlainType(1u32); + let non_deref_type_deref: &PlainType = non_deref_type.deref(); + //~^ WARNING call to `.deref()` on a reference in this situation does nothing + + // Dereferencing a &&T does not warn since it has collapsed the double reference + let non_deref_type = &&PlainType(1u32); + let non_deref_type_deref: &PlainType = non_deref_type.deref(); + + let non_borrow_type = &PlainType(1u32); + let non_borrow_type_borrow: &PlainType = non_borrow_type.borrow(); + //~^ WARNING call to `.borrow()` on a reference in this situation does nothing + + // Borrowing a &&T does not warn since it has collapsed the double reference + let non_borrow_type = &&PlainType(1u32); + let non_borrow_type_borrow: &PlainType = non_borrow_type.borrow(); + let xs = ["a", "b", "c"]; let _v: Vec<&str> = xs.iter().map(|x| x.clone()).collect(); // ok, but could use `*x` instead } -fn generic(non_clone_type: &NonCloneType) { +fn generic(non_clone_type: &PlainType) { non_clone_type.clone(); } -fn non_generic(non_clone_type: &NonCloneType) { +fn non_generic(non_clone_type: &PlainType) { non_clone_type.clone(); //~^ WARNING call to `.clone()` on a reference in this situation does nothing } diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr index 85a67f538407c..7f6f96bf1d142 100644 --- a/src/test/ui/lint/noop-method-call.stderr +++ b/src/test/ui/lint/noop-method-call.stderr @@ -1,19 +1,39 @@ warning: call to `.clone()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:12:74 + --> $DIR/noop-method-call.rs:16:71 | -LL | let non_clone_type_ref_clone: &NonCloneType = non_clone_type_ref.clone(); - | ^^^^^^^^ unnecessary method call +LL | let non_clone_type_ref_clone: &PlainType = non_clone_type_ref.clone(); + | ^^^^^^^^ unnecessary method call | - = note: `#[warn(noop_method_call)]` on by default - = note: the type `&NonCloneType` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed +note: the lint level is defined here + --> $DIR/noop-method-call.rs:4:9 + | +LL | #![warn(noop_method_call)] + | ^^^^^^^^^^^^^^^^ + = note: the type `&PlainType` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed + +warning: call to `.deref()` on a reference in this situation does nothing + --> $DIR/noop-method-call.rs:28:63 + | +LL | let non_deref_type_deref: &PlainType = non_deref_type.deref(); + | ^^^^^^^^ unnecessary method call + | + = note: the type `&PlainType` which `deref` is being called on is the same as the type returned from `deref`, so the method call does not do anything and can be removed + +warning: call to `.borrow()` on a reference in this situation does nothing + --> $DIR/noop-method-call.rs:36:66 + | +LL | let non_borrow_type_borrow: &PlainType = non_borrow_type.borrow(); + | ^^^^^^^^^ unnecessary method call + | + = note: the type `&PlainType` which `borrow` is being called on is the same as the type returned from `borrow`, so the method call does not do anything and can be removed warning: call to `.clone()` on a reference in this situation does nothing - --> $DIR/noop-method-call.rs:32:19 + --> $DIR/noop-method-call.rs:52:19 | LL | non_clone_type.clone(); | ^^^^^^^^ unnecessary method call | - = note: the type `&NonCloneType` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed + = note: the type `&PlainType` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed -warning: 2 warnings emitted +warning: 4 warnings emitted diff --git a/src/test/ui/underscore-imports/cycle.rs b/src/test/ui/underscore-imports/cycle.rs index 987410fa84b20..bacf9b2d5a96a 100644 --- a/src/test/ui/underscore-imports/cycle.rs +++ b/src/test/ui/underscore-imports/cycle.rs @@ -14,6 +14,5 @@ mod y { pub fn main() { use x::*; - #[allow(noop_method_call)] (&0).deref(); } diff --git a/src/test/ui/underscore-imports/macro-expanded.rs b/src/test/ui/underscore-imports/macro-expanded.rs index 55e86e848558d..43f527bc9a408 100644 --- a/src/test/ui/underscore-imports/macro-expanded.rs +++ b/src/test/ui/underscore-imports/macro-expanded.rs @@ -3,7 +3,6 @@ // check-pass #![feature(decl_macro, rustc_attrs)] -#![allow(noop_method_call)] mod x { pub use std::ops::Not as _; diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.rs b/src/tools/clippy/tests/ui/unnecessary_clone.rs index ce26634a995d9..6770a7fac90fd 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clone.rs +++ b/src/tools/clippy/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, noop_method_call, clippy::redundant_clone, clippy::unnecessary_wraps)] +#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wraps)] use std::cell::RefCell; use std::rc::{self, Rc}; From 25637b228d68e9bdf2d3ce1ba421cbd115fcb81e Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 17 Feb 2021 10:06:23 +0100 Subject: [PATCH 46/62] Warn in doc test --- compiler/rustc_lint/src/noop_method_call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 3a1b9c85fd1bd..479cc00199f6a 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -15,7 +15,7 @@ declare_lint! { /// /// ```rust /// # #![allow(unused)] - /// #![deny(noop_method_call)] + /// #![warn(noop_method_call)] /// struct Foo; /// let foo = &Foo; /// let clone: &Foo = foo.clone(); From 5461ee0b2632ccd912d316a68dd63a5b45125f57 Mon Sep 17 00:00:00 2001 From: "katelyn a. martin" Date: Thu, 27 Aug 2020 11:49:18 -0400 Subject: [PATCH 47/62] rustc_target: add "unwind" payloads to `Abi` ### Overview This commit begins the implementation work for RFC 2945. For more information, see the rendered RFC [1] and tracking issue [2]. A boolean `unwind` payload is added to the `C`, `System`, `Stdcall`, and `Thiscall` variants, marking whether unwinding across FFI boundaries is acceptable. The cases where each of these variants' `unwind` member is true correspond with the `C-unwind`, `system-unwind`, `stdcall-unwind`, and `thiscall-unwind` ABI strings introduced in RFC 2945 [3]. ### Feature Gate and Unstable Book This commit adds a `c_unwind` feature gate for the new ABI strings. Tests for this feature gate are included in `src/test/ui/c-unwind/`, which ensure that this feature gate works correctly for each of the new ABIs. A new language features entry in the unstable book is added as well. ### Further Work To Be Done This commit does not proceed to implement the new unwinding ABIs, and is intentionally scoped specifically to *defining* the ABIs and their feature flag. ### One Note on Test Churn This will lead to some test churn, in re-blessing hash tests, as the deleted comment in `src/librustc_target/spec/abi.rs` mentioned, because we can no longer guarantee the ordering of the `Abi` variants. While this is a downside, this decision was made bearing in mind that RFC 2945 states the following, in the "Other `unwind` Strings" section [3]: > More unwind variants of existing ABI strings may be introduced, > with the same semantics, without an additional RFC. Adding a new variant for each of these cases, rather than specifying a payload for a given ABI, would quickly become untenable, and make working with the `Abi` enum prone to mistakes. This approach encodes the unwinding information *into* a given ABI, to account for the future possibility of other `-unwind` ABI strings. ### Ignore Directives `ignore-*` directives are used in two of our `*-unwind` ABI test cases. Specifically, the `stdcall-unwind` and `thiscall-unwind` test cases ignore architectures that do not support `stdcall` and `thiscall`, respectively. These directives are cribbed from `src/test/ui/c-variadic/variadic-ffi-1.rs` for `stdcall`, and `src/test/ui/extern/extern-thiscall.rs` for `thiscall`. This would otherwise fail on some targets, see: https://github.com/rust-lang-ci/rust/commit/fcf697f90206e9c87b39d494f94ab35d976bfc60 ### Footnotes [1]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md [2]: https://github.com/rust-lang/rust/issues/74990 [3]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md#other-unwind-abi-strings --- compiler/rustc_ast_lowering/src/item.rs | 8 +-- compiler/rustc_ast_passes/src/feature_gate.rs | 32 +++++++++ .../rustc_codegen_cranelift/src/abi/mod.rs | 2 +- compiler/rustc_feature/src/active.rs | 3 + compiler/rustc_middle/src/ty/layout.rs | 8 +-- .../rustc_mir/src/interpret/terminator.rs | 6 +- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_symbol_mangling/src/v0.rs | 2 +- compiler/rustc_target/src/spec/abi.rs | 65 +++++++++++++++---- compiler/rustc_target/src/spec/arm_base.rs | 11 +++- .../src/spec/mipsel_unknown_none.rs | 6 +- compiler/rustc_target/src/spec/mod.rs | 19 ++++-- .../src/spec/nvptx64_nvidia_cuda.rs | 6 +- compiler/rustc_target/src/spec/riscv_base.rs | 6 +- compiler/rustc_typeck/src/collect.rs | 2 +- compiler/rustc_typeck/src/lib.rs | 21 +++--- .../src/language-features/c-unwind.md | 14 ++++ src/test/ui/codemap_tests/unicode.stderr | 2 +- src/test/ui/parser/issue-8537.stderr | 2 +- src/test/ui/symbol-names/impl1.rs | 6 +- .../feature-gate-c-unwind-enabled.rs | 12 ++++ .../ui/unwind-abis/feature-gate-c-unwind.rs | 9 +++ .../unwind-abis/feature-gate-c-unwind.stderr | 12 ++++ .../feature-gate-stdcall-unwind.rs | 13 ++++ .../feature-gate-stdcall-unwind.stderr | 12 ++++ .../unwind-abis/feature-gate-system-unwind.rs | 9 +++ .../feature-gate-system-unwind.stderr | 12 ++++ .../feature-gate-thiscall-unwind.rs | 13 ++++ .../feature-gate-thiscall-unwind.stderr | 12 ++++ 29 files changed, 274 insertions(+), 52 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/c-unwind.md create mode 100644 src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs create mode 100644 src/test/ui/unwind-abis/feature-gate-c-unwind.rs create mode 100644 src/test/ui/unwind-abis/feature-gate-c-unwind.stderr create mode 100644 src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs create mode 100644 src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr create mode 100644 src/test/ui/unwind-abis/feature-gate-system-unwind.rs create mode 100644 src/test/ui/unwind-abis/feature-gate-system-unwind.stderr create mode 100644 src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs create mode 100644 src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 8b740b7774089..2267f09ec1f11 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -322,10 +322,10 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ItemKind::ForeignMod(ref fm) => { if fm.abi.is_none() { - self.maybe_lint_missing_abi(span, id, abi::Abi::C); + self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false }); } hir::ItemKind::ForeignMod { - abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)), + abi: fm.abi.map_or(abi::Abi::C { unwind: false }, |abi| self.lower_abi(abi)), items: self .arena .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))), @@ -1322,8 +1322,8 @@ impl<'hir> LoweringContext<'_, 'hir> { match ext { Extern::None => abi::Abi::Rust, Extern::Implicit => { - self.maybe_lint_missing_abi(span, id, abi::Abi::C); - abi::Abi::C + self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false }); + abi::Abi::C { unwind: false } } Extern::Explicit(abi) => self.lower_abi(abi), } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 474ec2b589b71..acfbec6fa21b5 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -164,6 +164,38 @@ impl<'a> PostExpansionVisitor<'a> { "C-cmse-nonsecure-call ABI is experimental and subject to change" ); } + "C-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "C-unwind ABI is experimental and subject to change" + ); + } + "stdcall-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "stdcall-unwind ABI is experimental and subject to change" + ); + } + "system-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "system-unwind ABI is experimental and subject to change" + ); + } + "thiscall-unwind" => { + gate_feature_post!( + &self, + c_unwind, + span, + "thiscall-unwind ABI is experimental and subject to change" + ); + } abi => self .sess .parse_sess diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index b2647e6c8d384..83bb1d0172fbb 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -542,7 +542,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( // FIXME find a cleaner way to support varargs if fn_sig.c_variadic { - if fn_sig.abi != Abi::C { + if !matches!(fn_sig.abi, Abi::C { .. }) { fx.tcx.sess.span_fatal( span, &format!("Variadic call for non-C abi {:?}", fn_sig.abi), diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 8ee995a59d80d..95ae6a1ac9a26 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -644,6 +644,9 @@ declare_features! ( /// Allows associated types in inherent impls. (active, inherent_associated_types, "1.52.0", Some(8995), None), + /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries. + (active, c_unwind, "1.52.0", Some(74990), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6d2ab0e5f5a80..2f5380861c495 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2654,14 +2654,14 @@ where RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust, // It's the ABI's job to select this, not ours. - System => bug!("system abi should be selected elsewhere"), + System { .. } => bug!("system abi should be selected elsewhere"), EfiApi => bug!("eficall abi should be selected elsewhere"), - Stdcall => Conv::X86Stdcall, + Stdcall { .. } => Conv::X86Stdcall, Fastcall => Conv::X86Fastcall, Vectorcall => Conv::X86VectorCall, - Thiscall => Conv::X86ThisCall, - C => Conv::C, + Thiscall { .. } => Conv::X86ThisCall, + C { .. } => Conv::C, Unadjusted => Conv::C, Win64 => Conv::X86_64Win64, SysV64 => Conv::X86_64SysV, diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index 0807949a2d91b..4aa1360d53539 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -248,9 +248,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; if normalize_abi(caller_abi) != normalize_abi(callee_abi) { throw_ub_format!( - "calling a function with ABI {:?} using caller ABI {:?}", - callee_abi, - caller_abi + "calling a function with ABI {} using caller ABI {}", + callee_abi.name(), + caller_abi.name() ) } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 27bb45bcc8512..6eb5a40fba92c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -328,6 +328,7 @@ symbols! { bridge, bswap, c_str, + c_unwind, c_variadic, call, call_mut, diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index bbf7ecc39cfd9..12c0a147990fa 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -440,7 +440,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } match sig.abi { Abi::Rust => {} - Abi::C => cx.push("KC"), + Abi::C { unwind: false } => cx.push("KC"), abi => { cx.push("K"); let name = abi.name(); diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs index 65e8a4e8db2ad..f7f9c30d3b7a6 100644 --- a/compiler/rustc_target/src/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -8,24 +8,21 @@ mod tests; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)] #[derive(HashStable_Generic, Encodable, Decodable)] pub enum Abi { - // N.B., this ordering MUST match the AbiDatas array below. - // (This is ensured by the test indices_are_correct().) - // Multiplatform / generic ABIs // // These ABIs come first because every time we add a new ABI, we // have to re-bless all the hashing tests. These are used in many // places, so giving them stable values reduces test churn. The // specific values are meaningless. - Rust = 0, - C = 1, + Rust, + C { unwind: bool }, // Single platform ABIs Cdecl, - Stdcall, + Stdcall { unwind: bool }, Fastcall, Vectorcall, - Thiscall, + Thiscall { unwind: bool }, Aapcs, Win64, SysV64, @@ -39,7 +36,7 @@ pub enum Abi { CCmseNonSecureCall, // Multiplatform / generic ABIs - System, + System { unwind: bool }, RustIntrinsic, RustCall, PlatformIntrinsic, @@ -61,13 +58,16 @@ pub struct AbiData { const AbiDatas: &[AbiData] = &[ // Cross-platform ABIs AbiData { abi: Abi::Rust, name: "Rust", generic: true }, - AbiData { abi: Abi::C, name: "C", generic: true }, + AbiData { abi: Abi::C { unwind: false }, name: "C", generic: true }, + AbiData { abi: Abi::C { unwind: true }, name: "C-unwind", generic: true }, // Platform-specific ABIs AbiData { abi: Abi::Cdecl, name: "cdecl", generic: false }, - AbiData { abi: Abi::Stdcall, name: "stdcall", generic: false }, + AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall", generic: false }, + AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind", generic: false }, AbiData { abi: Abi::Fastcall, name: "fastcall", generic: false }, AbiData { abi: Abi::Vectorcall, name: "vectorcall", generic: false }, - AbiData { abi: Abi::Thiscall, name: "thiscall", generic: false }, + AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall", generic: false }, + AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind", generic: false }, AbiData { abi: Abi::Aapcs, name: "aapcs", generic: false }, AbiData { abi: Abi::Win64, name: "win64", generic: false }, AbiData { abi: Abi::SysV64, name: "sysv64", generic: false }, @@ -84,7 +84,8 @@ const AbiDatas: &[AbiData] = &[ }, AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call", generic: false }, // Cross-platform ABIs - AbiData { abi: Abi::System, name: "system", generic: true }, + AbiData { abi: Abi::System { unwind: false }, name: "system", generic: true }, + AbiData { abi: Abi::System { unwind: true }, name: "system-unwind", generic: true }, AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true }, AbiData { abi: Abi::RustCall, name: "rust-call", generic: true }, AbiData { abi: Abi::PlatformIntrinsic, name: "platform-intrinsic", generic: true }, @@ -103,7 +104,41 @@ pub fn all_names() -> Vec<&'static str> { impl Abi { #[inline] pub fn index(self) -> usize { - self as usize + // N.B., this ordering MUST match the AbiDatas array above. + // (This is ensured by the test indices_are_correct().) + use Abi::*; + match self { + // Cross-platform ABIs + Rust => 0, + C { unwind: false } => 1, + C { unwind: true } => 2, + // Platform-specific ABIs + Cdecl => 3, + Stdcall { unwind: false } => 4, + Stdcall { unwind: true } => 5, + Fastcall => 6, + Vectorcall => 7, + Thiscall { unwind: false } => 8, + Thiscall { unwind: true } => 9, + Aapcs => 10, + Win64 => 11, + SysV64 => 12, + PtxKernel => 13, + Msp430Interrupt => 14, + X86Interrupt => 15, + AmdGpuKernel => 16, + EfiApi => 17, + AvrInterrupt => 18, + AvrNonBlockingInterrupt => 19, + CCmseNonSecureCall => 20, + // Cross-platform ABIs + System { unwind: false } => 21, + System { unwind: true } => 22, + RustIntrinsic => 23, + RustCall => 24, + PlatformIntrinsic => 25, + Unadjusted => 26, + } } #[inline] @@ -122,6 +157,8 @@ impl Abi { impl fmt::Display for Abi { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "\"{}\"", self.name()) + match self { + abi => write!(f, "\"{}\"", abi.name()), + } } } diff --git a/compiler/rustc_target/src/spec/arm_base.rs b/compiler/rustc_target/src/spec/arm_base.rs index b74d80dc6bb2b..01f573313c97f 100644 --- a/compiler/rustc_target/src/spec/arm_base.rs +++ b/compiler/rustc_target/src/spec/arm_base.rs @@ -2,5 +2,14 @@ use crate::spec::abi::Abi; // All the calling conventions trigger an assertion(Unsupported calling convention) in llvm on arm pub fn unsupported_abis() -> Vec { - vec![Abi::Stdcall, Abi::Fastcall, Abi::Vectorcall, Abi::Thiscall, Abi::Win64, Abi::SysV64] + vec![ + Abi::Stdcall { unwind: false }, + Abi::Stdcall { unwind: true }, + Abi::Fastcall, + Abi::Vectorcall, + Abi::Thiscall { unwind: false }, + Abi::Thiscall { unwind: true }, + Abi::Win64, + Abi::SysV64, + ] } diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs index 0f9d3c3de1543..110c8dd80ea77 100644 --- a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs +++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs @@ -23,10 +23,12 @@ pub fn target() -> Target { panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, unsupported_abis: vec![ - Abi::Stdcall, + Abi::Stdcall { unwind: false }, + Abi::Stdcall { unwind: true }, Abi::Fastcall, Abi::Vectorcall, - Abi::Thiscall, + Abi::Thiscall { unwind: false }, + Abi::Thiscall { unwind: true }, Abi::Win64, Abi::SysV64, ], diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 039e9a8b2745f..4e8c0c03696d9 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1282,24 +1282,31 @@ impl Target { /// Given a function ABI, turn it into the correct ABI for this target. pub fn adjust_abi(&self, abi: Abi) -> Abi { match abi { - Abi::System => { + Abi::System { unwind } => { if self.is_like_windows && self.arch == "x86" { - Abi::Stdcall + Abi::Stdcall { unwind } } else { - Abi::C + Abi::C { unwind } } } // These ABI kinds are ignored on non-x86 Windows targets. // See https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions // and the individual pages for __stdcall et al. - Abi::Stdcall | Abi::Fastcall | Abi::Vectorcall | Abi::Thiscall => { - if self.is_like_windows && self.arch != "x86" { Abi::C } else { abi } + Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => { + if self.is_like_windows && self.arch != "x86" { Abi::C { unwind } } else { abi } + } + Abi::Fastcall | Abi::Vectorcall => { + if self.is_like_windows && self.arch != "x86" { + Abi::C { unwind: false } + } else { + abi + } } Abi::EfiApi => { if self.arch == "x86_64" { Abi::Win64 } else { - Abi::C + Abi::C { unwind: false } } } abi => abi, diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs index 3c9c7d578fbd4..15d8e4843f976 100644 --- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs @@ -49,10 +49,12 @@ pub fn target() -> Target { // create the tests for this. unsupported_abis: vec![ Abi::Cdecl, - Abi::Stdcall, + Abi::Stdcall { unwind: false }, + Abi::Stdcall { unwind: true }, Abi::Fastcall, Abi::Vectorcall, - Abi::Thiscall, + Abi::Thiscall { unwind: false }, + Abi::Thiscall { unwind: true }, Abi::Aapcs, Abi::Win64, Abi::SysV64, diff --git a/compiler/rustc_target/src/spec/riscv_base.rs b/compiler/rustc_target/src/spec/riscv_base.rs index 64cf890037e51..5bcbb2e621bd0 100644 --- a/compiler/rustc_target/src/spec/riscv_base.rs +++ b/compiler/rustc_target/src/spec/riscv_base.rs @@ -5,10 +5,12 @@ use crate::spec::abi::Abi; pub fn unsupported_abis() -> Vec { vec![ Abi::Cdecl, - Abi::Stdcall, + Abi::Stdcall { unwind: false }, + Abi::Stdcall { unwind: true }, Abi::Fastcall, Abi::Vectorcall, - Abi::Thiscall, + Abi::Thiscall { unwind: false }, + Abi::Thiscall { unwind: true }, Abi::Aapcs, Abi::Win64, Abi::SysV64, diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 3881d55ef9169..a175da3270638 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -2666,7 +2666,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } else if tcx.sess.check_name(attr, sym::used) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; } else if tcx.sess.check_name(attr, sym::cmse_nonsecure_entry) { - if tcx.fn_sig(id).abi() != abi::Abi::C { + if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) { struct_span_err!( tcx.sess, attr.span, diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 6ddc26efeae35..fb864c5b4a7d7 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -118,14 +118,19 @@ use astconv::AstConv; use bounds::Bounds; fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { - if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0045, - "C-variadic function must have C or cdecl calling convention" - ); - err.span_label(span, "C-variadics require C or cdecl calling convention").emit(); + match (decl.c_variadic, abi) { + // The function has the correct calling convention, or isn't a "C-variadic" function. + (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl) => {} + // The function is a "C-variadic" function with an incorrect calling convention. + (true, _) => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0045, + "C-variadic function must have C or cdecl calling convention" + ); + err.span_label(span, "C-variadics require C or cdecl calling convention").emit(); + } } } diff --git a/src/doc/unstable-book/src/language-features/c-unwind.md b/src/doc/unstable-book/src/language-features/c-unwind.md new file mode 100644 index 0000000000000..c1705d59acc21 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/c-unwind.md @@ -0,0 +1,14 @@ +# `c_unwind` + +The tracking issue for this feature is: [#74990] + +[#74990]: https://github.com/rust-lang/rust/issues/74990 + +------------------------ + +Introduces a new ABI string, "C-unwind", to enable unwinding from other +languages (such as C++) into Rust frames and from Rust into other languages. + +See [RFC 2945] for more information. + +[RFC 2945]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md diff --git a/src/test/ui/codemap_tests/unicode.stderr b/src/test/ui/codemap_tests/unicode.stderr index 61c3f4f1c9847..b7ba4fa46d92b 100644 --- a/src/test/ui/codemap_tests/unicode.stderr +++ b/src/test/ui/codemap_tests/unicode.stderr @@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `路濫狼á́́` LL | extern "路濫狼á́́" fn foo() {} | ^^^^^^^^^ invalid ABI | - = help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted + = help: valid ABIs: Rust, C, C-unwind, cdecl, stdcall, stdcall-unwind, fastcall, vectorcall, thiscall, thiscall-unwind, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted error: aborting due to previous error diff --git a/src/test/ui/parser/issue-8537.stderr b/src/test/ui/parser/issue-8537.stderr index 3f63c08021098..85e2b77d867b9 100644 --- a/src/test/ui/parser/issue-8537.stderr +++ b/src/test/ui/parser/issue-8537.stderr @@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `invalid-ab_isize` LL | "invalid-ab_isize" | ^^^^^^^^^^^^^^^^^^ invalid ABI | - = help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted + = help: valid ABIs: Rust, C, C-unwind, cdecl, stdcall, stdcall-unwind, fastcall, vectorcall, thiscall, thiscall-unwind, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted error: aborting due to previous error diff --git a/src/test/ui/symbol-names/impl1.rs b/src/test/ui/symbol-names/impl1.rs index 24bdf6d669e88..7dd74bd229d3f 100644 --- a/src/test/ui/symbol-names/impl1.rs +++ b/src/test/ui/symbol-names/impl1.rs @@ -4,7 +4,7 @@ //[legacy]compile-flags: -Z symbol-mangling-version=legacy //[v0]compile-flags: -Z symbol-mangling-version=v0 //[legacy]normalize-stderr-32bit: "hee444285569b39c2" -> "SYMBOL_HASH" -//[legacy]normalize-stderr-64bit: "h310ea0259fc3d32d" -> "SYMBOL_HASH" +//[legacy]normalize-stderr-64bit: "hd949d7797008991f" -> "SYMBOL_HASH" #![feature(auto_traits, rustc_attrs)] #![allow(dead_code)] @@ -75,3 +75,7 @@ fn main() { } }; } + +// FIXME(katie): The 32-bit symbol hash probably needs updating as well, but I'm slightly unsure +// about how to do that. This comment is here so that we don't break the test due to error messages +// including incorrect line numbers. diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs b/src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs new file mode 100644 index 0000000000000..6ff5dbda2d560 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs @@ -0,0 +1,12 @@ +// Test that the "C-unwind" ABI is feature-gated, and *can* be used when the +// `c_unwind` feature gate is enabled. + +// check-pass + +#![feature(c_unwind)] + +extern "C-unwind" fn f() {} + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind.rs b/src/test/ui/unwind-abis/feature-gate-c-unwind.rs new file mode 100644 index 0000000000000..f02a368d4e097 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-c-unwind.rs @@ -0,0 +1,9 @@ +// Test that the "C-unwind" ABI is feature-gated, and cannot be used when the +// `c_unwind` feature gate is not used. + +extern "C-unwind" fn f() {} +//~^ ERROR C-unwind ABI is experimental and subject to change [E0658] + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr new file mode 100644 index 0000000000000..f4c785a235f67 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr @@ -0,0 +1,12 @@ +error[E0658]: C-unwind ABI is experimental and subject to change + --> $DIR/feature-gate-c-unwind.rs:4:8 + | +LL | extern "C-unwind" fn f() {} + | ^^^^^^^^^^ + | + = note: see issue #74990 for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs new file mode 100644 index 0000000000000..7d4dc8c9343f1 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs @@ -0,0 +1,13 @@ +// ignore-arm stdcall isn't supported +// ignore-aarch64 stdcall isn't supported +// ignore-riscv64 stdcall isn't supported + +// Test that the "stdcall-unwind" ABI is feature-gated, and cannot be used when +// the `c_unwind` feature gate is not used. + +extern "stdcall-unwind" fn f() {} +//~^ ERROR stdcall-unwind ABI is experimental and subject to change [E0658] + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr new file mode 100644 index 0000000000000..e3d569f464f87 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr @@ -0,0 +1,12 @@ +error[E0658]: stdcall-unwind ABI is experimental and subject to change + --> $DIR/feature-gate-stdcall-unwind.rs:8:8 + | +LL | extern "stdcall-unwind" fn f() {} + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #74990 for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/unwind-abis/feature-gate-system-unwind.rs b/src/test/ui/unwind-abis/feature-gate-system-unwind.rs new file mode 100644 index 0000000000000..26c2de4e81767 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-system-unwind.rs @@ -0,0 +1,9 @@ +// Test that the "system-unwind" ABI is feature-gated, and cannot be used when +// the `c_unwind` feature gate is not used. + +extern "system-unwind" fn f() {} +//~^ ERROR system-unwind ABI is experimental and subject to change [E0658] + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-system-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-system-unwind.stderr new file mode 100644 index 0000000000000..87877336475b4 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-system-unwind.stderr @@ -0,0 +1,12 @@ +error[E0658]: system-unwind ABI is experimental and subject to change + --> $DIR/feature-gate-system-unwind.rs:4:8 + | +LL | extern "system-unwind" fn f() {} + | ^^^^^^^^^^^^^^^ + | + = note: see issue #74990 for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs new file mode 100644 index 0000000000000..2f4cefccc1967 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs @@ -0,0 +1,13 @@ +// ignore-arm thiscall isn't supported +// ignore-aarch64 thiscall isn't supported +// ignore-riscv64 thiscall isn't supported + +// Test that the "thiscall-unwind" ABI is feature-gated, and cannot be used when +// the `c_unwind` feature gate is not used. + +extern "thiscall-unwind" fn f() {} +//~^ ERROR thiscall-unwind ABI is experimental and subject to change [E0658] + +fn main() { + f(); +} diff --git a/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr new file mode 100644 index 0000000000000..b103bb8d56585 --- /dev/null +++ b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr @@ -0,0 +1,12 @@ +error[E0658]: thiscall-unwind ABI is experimental and subject to change + --> $DIR/feature-gate-thiscall-unwind.rs:8:8 + | +LL | extern "thiscall-unwind" fn f() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #74990 for more information + = help: add `#![feature(c_unwind)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. From 9a007cfdc0857a4ad0601e2e130ef53d158493ee Mon Sep 17 00:00:00 2001 From: "katelyn a. martin" Date: Thu, 10 Sep 2020 13:38:39 -0400 Subject: [PATCH 48/62] implement unwinding abi's (RFC 2945) ### Changes This commit implements unwind ABI's, specified in RFC 2945. We adjust the `rustc_middle::ty::layout::fn_can_unwind` function, used to compute whether or not a `FnAbi` object represents a function that should be able to unwind when `panic=unwind` is in use. Changes are also made to `rustc_mir_build::build::should_abort_on_panic` so that the function ABI is used to determind whether it should abort, assuming that the `panic=unwind` strategy is being used, and no explicit unwind attribute was provided. ### Tests Unit tests, checking that the behavior is correct for `C-unwind`, `stdcall-unwind`, `system-unwind`, and `thiscall-unwind`, are included. These alternative `unwind` ABI strings are specified in RFC 2945, in the "_Other `unwind` ABI strings_" section. Additionally, a test case is included to assert that the LLVM IR generated for an external function defined with the `C-unwind` ABI will be appropriately labeled with the `nounwind` LLVM attribute when the `panic=abort` compilation flag is used. ### Ignore Directives This commit uses `ignore-*` directives in two of our `*-unwind` ABI test cases. Specifically, the `stdcall-unwind` and `thiscall-unwind` test cases ignore architectures that do not support `stdcall` and `thiscall`, respectively. These directives are cribbed from `src/test/ui/c-variadic/variadic-ffi-1.rs` for `stdcall`, and `src/test/ui/extern/extern-thiscall.rs` for `thiscall`. --- compiler/rustc_middle/src/ty/layout.rs | 29 +++++++++------- compiler/rustc_mir_build/src/build/mod.rs | 22 ++++++++++--- .../unwind-abis/c-unwind-abi-panic-abort.rs | 18 ++++++++++ src/test/codegen/unwind-abis/c-unwind-abi.rs | 29 ++++++++++++++++ .../codegen/unwind-abis/stdcall-unwind-abi.rs | 32 ++++++++++++++++++ .../codegen/unwind-abis/system-unwind-abi.rs | 29 ++++++++++++++++ .../unwind-abis/thiscall-unwind-abi.rs | 33 +++++++++++++++++++ 7 files changed, 176 insertions(+), 16 deletions(-) create mode 100644 src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs create mode 100644 src/test/codegen/unwind-abis/c-unwind-abi.rs create mode 100644 src/test/codegen/unwind-abis/stdcall-unwind-abi.rs create mode 100644 src/test/codegen/unwind-abis/system-unwind-abi.rs create mode 100644 src/test/codegen/unwind-abis/thiscall-unwind-abi.rs diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 2f5380861c495..ee2dffd8baeb9 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2562,6 +2562,7 @@ fn fn_can_unwind( panic_strategy: PanicStrategy, codegen_fn_attr_flags: CodegenFnAttrFlags, call_conv: Conv, + abi: SpecAbi, ) -> bool { if panic_strategy != PanicStrategy::Unwind { // In panic=abort mode we assume nothing can unwind anywhere, so @@ -2586,17 +2587,16 @@ fn fn_can_unwind( // // 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`). // - // Foreign items (case 1) are assumed to not unwind; it is - // UB otherwise. (At least for now; see also - // rust-lang/rust#63909 and Rust RFC 2753.) - // - // Items defined in Rust with non-Rust ABIs (case 2) are also - // not supposed to unwind. Whether this should be enforced - // (versus stating it is UB) and *how* it would be enforced - // is currently under discussion; see rust-lang/rust#58794. - // - // In either case, we mark item as explicitly nounwind. - false + // In both of these cases, we should refer to the ABI to determine whether or not we + // should unwind. See Rust RFC 2945 for more information on this behavior, here: + // https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md + use SpecAbi::*; + match abi { + C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => { + unwind + } + _ => false, + } } } } @@ -2823,7 +2823,12 @@ where c_variadic: sig.c_variadic, fixed_count: inputs.len(), conv, - can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv), + can_unwind: fn_can_unwind( + cx.tcx().sess.panic_strategy(), + codegen_fn_attr_flags, + conv, + sig.abi, + ), }; fn_abi.adjust_for_abi(cx, sig.abi); debug!("FnAbi::new_internal = {:?}", fn_abi); diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 5f6c8d26402ed..dec26278290d9 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -546,7 +546,7 @@ macro_rules! unpack { }}; } -fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool { +fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bool { // Validate `#[unwind]` syntax regardless of platform-specific panic strategy. let attrs = &tcx.get_attrs(fn_def_id.to_def_id()); let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs); @@ -556,12 +556,26 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> b return false; } - // This is a special case: some functions have a C abi but are meant to - // unwind anyway. Don't stop them. match unwind_attr { - None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)` + // If an `#[unwind]` attribute was found, we should adhere to it. Some(UnwindAttr::Allowed) => false, Some(UnwindAttr::Aborts) => true, + // If no attribute was found and the panic strategy is `unwind`, then we should examine + // the function's ABI string to determine whether it should abort upon panic. + None => { + use Abi::*; + match abi { + // In the case of ABI's that have an `-unwind` equivalent, check whether the ABI + // permits unwinding. If so, we should not abort. Otherwise, we should. + C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => { + !unwind + } + // Rust and `rust-call` functions are allowed to unwind, and should not abort. + Rust | RustCall => false, + // Other ABI's should abort. + _ => true, + } + } } } diff --git a/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs new file mode 100644 index 0000000000000..afd65ff6741a6 --- /dev/null +++ b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs @@ -0,0 +1,18 @@ +// compile-flags: -C panic=abort -C opt-level=0 + +// Test that `nounwind` atributes are applied to `C-unwind` extern functions when the +// code is compiled with `panic=abort`. We disable optimizations above to prevent LLVM from +// inferring the attribute. + +#![crate_type = "lib"] +#![feature(c_unwind)] + +// CHECK: @rust_item_that_can_unwind() unnamed_addr #0 { +#[no_mangle] +pub extern "C-unwind" fn rust_item_that_can_unwind() { +} + +// Now, make sure that the LLVM attributes for this functions are correct. First, make +// sure that the first item is correctly marked with the `nounwind` attribute: +// +// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} } diff --git a/src/test/codegen/unwind-abis/c-unwind-abi.rs b/src/test/codegen/unwind-abis/c-unwind-abi.rs new file mode 100644 index 0000000000000..f157653675322 --- /dev/null +++ b/src/test/codegen/unwind-abis/c-unwind-abi.rs @@ -0,0 +1,29 @@ +// compile-flags: -C opt-level=0 + +// Test that `nounwind` atributes are correctly applied to exported `C` and `C-unwind` extern +// functions. `C-unwind` functions MUST NOT have this attribute. We disable optimizations above +// to prevent LLVM from inferring the attribute. + +#![crate_type = "lib"] +#![feature(c_unwind)] + +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +#[no_mangle] +pub extern "C" fn rust_item_that_cannot_unwind() { +} + +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +#[no_mangle] +pub extern "C-unwind" fn rust_item_that_can_unwind() { +} + +// Now, make some assertions that the LLVM attributes for these functions are correct. First, make +// sure that the first item is correctly marked with the `nounwind` attribute: +// +// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} } +// +// Next, let's assert that the second item, which CAN unwind, does not have this attribute. +// +// CHECK: attributes #1 = { +// CHECK-NOT: nounwind +// CHECK: } diff --git a/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs b/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs new file mode 100644 index 0000000000000..ed804ca278d2d --- /dev/null +++ b/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs @@ -0,0 +1,32 @@ +// compile-flags: -C opt-level=0 +// ignore-arm stdcall isn't supported +// ignore-aarch64 stdcall isn't supported +// ignore-riscv64 stdcall isn't supported + +// Test that `nounwind` atributes are correctly applied to exported `stdcall` and `stdcall-unwind` +// extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable +// optimizations above to prevent LLVM from inferring the attribute. + +#![crate_type = "lib"] +#![feature(c_unwind)] + +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +#[no_mangle] +pub extern "stdcall" fn rust_item_that_cannot_unwind() { +} + +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +#[no_mangle] +pub extern "stdcall-unwind" fn rust_item_that_can_unwind() { +} + +// Now, make some assertions that the LLVM attributes for these functions are correct. First, make +// sure that the first item is correctly marked with the `nounwind` attribute: +// +// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} } +// +// Next, let's assert that the second item, which CAN unwind, does not have this attribute. +// +// CHECK: attributes #1 = { +// CHECK-NOT: nounwind +// CHECK: } diff --git a/src/test/codegen/unwind-abis/system-unwind-abi.rs b/src/test/codegen/unwind-abis/system-unwind-abi.rs new file mode 100644 index 0000000000000..c4d51328352c0 --- /dev/null +++ b/src/test/codegen/unwind-abis/system-unwind-abi.rs @@ -0,0 +1,29 @@ +// compile-flags: -C opt-level=0 + +// Test that `nounwind` atributes are correctly applied to exported `system` and `system-unwind` +// extern functions. `system-unwind` functions MUST NOT have this attribute. We disable +// optimizations above to prevent LLVM from inferring the attribute. + +#![crate_type = "lib"] +#![feature(c_unwind)] + +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +#[no_mangle] +pub extern "system" fn rust_item_that_cannot_unwind() { +} + +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +#[no_mangle] +pub extern "system-unwind" fn rust_item_that_can_unwind() { +} + +// Now, make some assertions that the LLVM attributes for these functions are correct. First, make +// sure that the first item is correctly marked with the `nounwind` attribute: +// +// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} } +// +// Next, let's assert that the second item, which CAN unwind, does not have this attribute. +// +// CHECK: attributes #1 = { +// CHECK-NOT: nounwind +// CHECK: } diff --git a/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs b/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs new file mode 100644 index 0000000000000..aaa63ae55c3ac --- /dev/null +++ b/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs @@ -0,0 +1,33 @@ +// compile-flags: -C opt-level=0 +// ignore-arm thiscall isn't supported +// ignore-aarch64 thiscall isn't supported +// ignore-riscv64 thiscall isn't supported + +// Test that `nounwind` atributes are correctly applied to exported `thiscall` and +// `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We +// disable optimizations above to prevent LLVM from inferring the attribute. + +#![crate_type = "lib"] +#![feature(abi_thiscall)] +#![feature(c_unwind)] + +// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { +#[no_mangle] +pub extern "thiscall" fn rust_item_that_cannot_unwind() { +} + +// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { +#[no_mangle] +pub extern "thiscall-unwind" fn rust_item_that_can_unwind() { +} + +// Now, make some assertions that the LLVM attributes for these functions are correct. First, make +// sure that the first item is correctly marked with the `nounwind` attribute: +// +// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} } +// +// Next, let's assert that the second item, which CAN unwind, does not have this attribute. +// +// CHECK: attributes #1 = { +// CHECK-NOT: nounwind +// CHECK: } From 0fd2fd92dcc0c255bb5945331b82022171ae4fa5 Mon Sep 17 00:00:00 2001 From: "katelyn a. martin" Date: Fri, 11 Dec 2020 20:54:47 -0500 Subject: [PATCH 49/62] add integration tests, unwind across FFI boundary ### Integration Tests This commit introduces some new fixtures to the `run-make-fulldeps` test suite. * c-unwind-abi-catch-panic: Exercise unwinding a panic. This catches a panic across an FFI boundary and downcasts it into an integer. * c-unwind-abi-catch-lib-panic: This is similar to the previous `*catch-panic` test, however in this case the Rust code that panics resides in a separate crate. ### Add `rust_eh_personality` to `#[no_std]` alloc tests This commit addresses some test failures that now occur in the following two tests: * no_std-alloc-error-handler-custom.rs * no_std-alloc-error-handler-default.rs Each test now defines a `rust_eh_personality` extern function, in the same manner as shown in the "Writing an executable without stdlib" section of the `lang_items` documentation here: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib Without this change, these tests would fail to compile due to a linking error explaining that there was an "undefined reference to `rust_eh_personality'." ### Updated hash * update 32-bit hash in `impl1` test ### Panics This commit uses `panic!` macro invocations that return a string, rather than using an integer as a panic payload. Doing so avoids the following warnings that were observed during rollup for the `*-msvc-1` targets: ``` warning: panic message is not a string literal --> panic.rs:10:16 | 10 | panic!(x); // That is too big! | ^ | = note: `#[warn(non_fmt_panic)]` on by default = note: this is no longer accepted in Rust 2021 help: add a "{}" format string to Display the message | 10 | panic!("{}", x); // That is too big! | ^^^^^ help: or use std::panic::panic_any instead | 10 | std::panic::panic_any(x); // That is too big! | ^^^^^^^^^^^^^^^^^^^^^ warning: 1 warning emitted ``` See: https://github.com/rust-lang-ci/rust/runs/1992118428 As these errors imply, panicking without a format string will be disallowed in Rust 2021, per #78500. --- .../c-unwind-abi-catch-lib-panic/Makefile | 30 +++++++++++++ .../c-unwind-abi-catch-lib-panic/add.c | 12 +++++ .../c-unwind-abi-catch-lib-panic/main.rs | 35 +++++++++++++++ .../c-unwind-abi-catch-lib-panic/panic.rs | 12 +++++ .../c-unwind-abi-catch-panic/Makefile | 5 +++ .../c-unwind-abi-catch-panic/add.c | 12 +++++ .../c-unwind-abi-catch-panic/main.rs | 44 +++++++++++++++++++ .../no_std-alloc-error-handler-custom.rs | 9 +++- .../no_std-alloc-error-handler-default.rs | 9 +++- src/test/ui/symbol-names/impl1.rs | 6 +-- 10 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/Makefile create mode 100644 src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/add.c create mode 100644 src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/main.rs create mode 100644 src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/panic.rs create mode 100644 src/test/run-make-fulldeps/c-unwind-abi-catch-panic/Makefile create mode 100644 src/test/run-make-fulldeps/c-unwind-abi-catch-panic/add.c create mode 100644 src/test/run-make-fulldeps/c-unwind-abi-catch-panic/main.rs diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/Makefile b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/Makefile new file mode 100644 index 0000000000000..a8515c533af57 --- /dev/null +++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/Makefile @@ -0,0 +1,30 @@ +-include ../tools.mk + +all: archive + # Compile `main.rs`, which will link into our library, and run it. + $(RUSTC) main.rs + $(call RUN,main) + +ifdef IS_MSVC +archive: add.o panic.o + # Now, create an archive using these two objects. + $(AR) crus $(TMPDIR)/add.lib $(TMPDIR)/add.o $(TMPDIR)/panic.o +else +archive: add.o panic.o + # Now, create an archive using these two objects. + $(AR) crus $(TMPDIR)/libadd.a $(TMPDIR)/add.o $(TMPDIR)/panic.o +endif + +# Compile `panic.rs` into an object file. +# +# Note that we invoke `rustc` directly, so we may emit an object rather +# than an archive. We'll do that later. +panic.o: + $(BARE_RUSTC) $(RUSTFLAGS) \ + --out-dir $(TMPDIR) \ + --emit=obj panic.rs + +# Compile `add.c` into an object file. +add.o: + $(call COMPILE_OBJ,$(TMPDIR)/add.o,add.c) + diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/add.c b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/add.c new file mode 100644 index 0000000000000..444359451f6ec --- /dev/null +++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/add.c @@ -0,0 +1,12 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif + +// An external function, defined in Rust. +extern void panic_if_greater_than_10(unsigned x); + +unsigned add_small_numbers(unsigned a, unsigned b) { + unsigned c = a + b; + panic_if_greater_than_10(c); + return c; +} diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/main.rs b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/main.rs new file mode 100644 index 0000000000000..78a71219c7811 --- /dev/null +++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/main.rs @@ -0,0 +1,35 @@ +//! A test for calling `C-unwind` functions across foreign function boundaries. +//! +//! This test triggers a panic in a Rust library that our foreign function invokes. This shows +//! that we can unwind through the C code in that library, and catch the underlying panic. +#![feature(c_unwind)] + +use std::panic::{catch_unwind, AssertUnwindSafe}; + +fn main() { + // Call `add_small_numbers`, passing arguments that will NOT trigger a panic. + let (a, b) = (9, 1); + let c = unsafe { add_small_numbers(a, b) }; + assert_eq!(c, 10); + + // Call `add_small_numbers`, passing arguments that will trigger a panic, and catch it. + let caught_unwind = catch_unwind(AssertUnwindSafe(|| { + let (a, b) = (10, 1); + let _c = unsafe { add_small_numbers(a, b) }; + unreachable!("should have unwound instead of returned"); + })); + + // Assert that we did indeed panic, then unwrap and downcast the panic into the sum. + assert!(caught_unwind.is_err()); + let panic_obj = caught_unwind.unwrap_err(); + let msg = panic_obj.downcast_ref::().unwrap(); + assert_eq!(msg, "11"); +} + +#[link(name = "add", kind = "static")] +extern "C-unwind" { + /// An external function, defined in C. + /// + /// Returns the sum of two numbers, or panics if the sum is greater than 10. + fn add_small_numbers(a: u32, b: u32) -> u32; +} diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/panic.rs b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/panic.rs new file mode 100644 index 0000000000000..a99a04d5c6f4b --- /dev/null +++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/panic.rs @@ -0,0 +1,12 @@ +#![crate_type = "staticlib"] +#![feature(c_unwind)] + +/// This function will panic if `x` is greater than 10. +/// +/// This function is called by `add_small_numbers`. +#[no_mangle] +pub extern "C-unwind" fn panic_if_greater_than_10(x: u32) { + if x > 10 { + panic!("{}", x); // That is too big! + } +} diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/Makefile b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/Makefile new file mode 100644 index 0000000000000..9553b7aeeb983 --- /dev/null +++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/Makefile @@ -0,0 +1,5 @@ +-include ../tools.mk + +all: $(call NATIVE_STATICLIB,add) + $(RUSTC) main.rs + $(call RUN,main) || exit 1 diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/add.c b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/add.c new file mode 100644 index 0000000000000..444359451f6ec --- /dev/null +++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/add.c @@ -0,0 +1,12 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif + +// An external function, defined in Rust. +extern void panic_if_greater_than_10(unsigned x); + +unsigned add_small_numbers(unsigned a, unsigned b) { + unsigned c = a + b; + panic_if_greater_than_10(c); + return c; +} diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/main.rs b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/main.rs new file mode 100644 index 0000000000000..15d38d7216058 --- /dev/null +++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/main.rs @@ -0,0 +1,44 @@ +//! A test for calling `C-unwind` functions across foreign function boundaries. +//! +//! This test triggers a panic when calling a foreign function that calls *back* into Rust. +#![feature(c_unwind)] + +use std::panic::{catch_unwind, AssertUnwindSafe}; + +fn main() { + // Call `add_small_numbers`, passing arguments that will NOT trigger a panic. + let (a, b) = (9, 1); + let c = unsafe { add_small_numbers(a, b) }; + assert_eq!(c, 10); + + // Call `add_small_numbers`, passing arguments that will trigger a panic, and catch it. + let caught_unwind = catch_unwind(AssertUnwindSafe(|| { + let (a, b) = (10, 1); + let _c = unsafe { add_small_numbers(a, b) }; + unreachable!("should have unwound instead of returned"); + })); + + // Assert that we did indeed panic, then unwrap and downcast the panic into the sum. + assert!(caught_unwind.is_err()); + let panic_obj = caught_unwind.unwrap_err(); + let msg = panic_obj.downcast_ref::().unwrap(); + assert_eq!(msg, "11"); +} + +#[link(name = "add", kind = "static")] +extern "C-unwind" { + /// An external function, defined in C. + /// + /// Returns the sum of two numbers, or panics if the sum is greater than 10. + fn add_small_numbers(a: u32, b: u32) -> u32; +} + +/// This function will panic if `x` is greater than 10. +/// +/// This function is called by `add_small_numbers`. +#[no_mangle] +pub extern "C-unwind" fn panic_if_greater_than_10(x: u32) { + if x > 10 { + panic!("{}", x); // That is too big! + } +} diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs index 4d40c7d0d2237..c9b4abbfd3fd3 100644 --- a/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs +++ b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs @@ -7,7 +7,7 @@ // compile-flags:-C panic=abort // aux-build:helper.rs -#![feature(start, rustc_private, new_uninit, panic_info_message)] +#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)] #![feature(alloc_error_handler)] #![no_std] @@ -84,6 +84,13 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! { } } +// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed. +// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions +// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't +// unwind. So, for this test case we will define the symbol. +#[lang = "eh_personality"] +extern fn rust_eh_personality() {} + #[derive(Debug)] struct Page([[u64; 32]; 16]); diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-default.rs b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs index 4f8c44f1763c8..d6cd4a6af855f 100644 --- a/src/test/ui/allocator/no_std-alloc-error-handler-default.rs +++ b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs @@ -8,7 +8,7 @@ // aux-build:helper.rs // gate-test-default_alloc_error_handler -#![feature(start, rustc_private, new_uninit, panic_info_message)] +#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)] #![feature(default_alloc_error_handler)] #![no_std] @@ -71,6 +71,13 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! { } } +// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed. +// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions +// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't +// unwind. So, for this test case we will define the symbol. +#[lang = "eh_personality"] +extern fn rust_eh_personality() {} + #[derive(Debug)] struct Page([[u64; 32]; 16]); diff --git a/src/test/ui/symbol-names/impl1.rs b/src/test/ui/symbol-names/impl1.rs index 7dd74bd229d3f..a352d255c782d 100644 --- a/src/test/ui/symbol-names/impl1.rs +++ b/src/test/ui/symbol-names/impl1.rs @@ -3,7 +3,7 @@ // revisions: legacy v0 //[legacy]compile-flags: -Z symbol-mangling-version=legacy //[v0]compile-flags: -Z symbol-mangling-version=v0 -//[legacy]normalize-stderr-32bit: "hee444285569b39c2" -> "SYMBOL_HASH" +//[legacy]normalize-stderr-32bit: "h13cfe136e83a9196" -> "SYMBOL_HASH" //[legacy]normalize-stderr-64bit: "hd949d7797008991f" -> "SYMBOL_HASH" #![feature(auto_traits, rustc_attrs)] @@ -75,7 +75,3 @@ fn main() { } }; } - -// FIXME(katie): The 32-bit symbol hash probably needs updating as well, but I'm slightly unsure -// about how to do that. This comment is here so that we don't break the test due to error messages -// including incorrect line numbers. From d8bfdc8ec4cc3b1aab0fed675bafa6d65aab1672 Mon Sep 17 00:00:00 2001 From: "katelyn a. martin" Date: Fri, 23 Oct 2020 18:49:34 -0400 Subject: [PATCH 50/62] address pr review comments ### Add debug assertion to check `AbiDatas` ordering This makes a small alteration to `Abi::index`, so that we include a debug assertion to check that the index we are returning corresponds with the same abi in our data array. This will help prevent ordering bugs in the future, which can manifest in rather strange errors. ### Using exhaustive ABI matches This slightly modifies the changes from our previous commits, favoring exhaustive matches in place of `_ => ...` fall-through arms. This should help with maintenance in the future, when additional ABI's are added, or when existing ABI's are modified. ### List all `-unwind` ABI's in unstable book This updates the `c-unwind` page in the unstable book to list _all_ of the other ABI strings that are introduced by this feature gate. Now, all of the ABI's specified by RFC 2945 are shown. Co-authored-by: Amanieu d'Antras Co-authored-by: Niko Matsakis --- compiler/rustc_middle/src/ty/layout.rs | 20 ++++++++++++++++++- compiler/rustc_mir_build/src/build/mod.rs | 18 ++++++++++++++++- compiler/rustc_target/src/spec/abi.rs | 15 ++++++++++++-- .../src/language-features/c-unwind.md | 3 ++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index ee2dffd8baeb9..814581a6cf171 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2595,7 +2595,25 @@ fn fn_can_unwind( C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => { unwind } - _ => false, + Cdecl + | Fastcall + | Vectorcall + | Aapcs + | Win64 + | SysV64 + | PtxKernel + | Msp430Interrupt + | X86Interrupt + | AmdGpuKernel + | EfiApi + | AvrInterrupt + | AvrNonBlockingInterrupt + | CCmseNonSecureCall + | RustIntrinsic + | PlatformIntrinsic + | Unadjusted => false, + // In the `if` above, we checked for functions with the Rust calling convention. + Rust | RustCall => unreachable!(), } } } diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index dec26278290d9..08a23d207015c 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -573,7 +573,23 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bo // Rust and `rust-call` functions are allowed to unwind, and should not abort. Rust | RustCall => false, // Other ABI's should abort. - _ => true, + Cdecl + | Fastcall + | Vectorcall + | Aapcs + | Win64 + | SysV64 + | PtxKernel + | Msp430Interrupt + | X86Interrupt + | AmdGpuKernel + | EfiApi + | AvrInterrupt + | AvrNonBlockingInterrupt + | CCmseNonSecureCall + | RustIntrinsic + | PlatformIntrinsic + | Unadjusted => true, } } } diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs index f7f9c30d3b7a6..17eb33b8f2eaa 100644 --- a/compiler/rustc_target/src/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -107,7 +107,7 @@ impl Abi { // N.B., this ordering MUST match the AbiDatas array above. // (This is ensured by the test indices_are_correct().) use Abi::*; - match self { + let i = match self { // Cross-platform ABIs Rust => 0, C { unwind: false } => 1, @@ -138,7 +138,18 @@ impl Abi { RustCall => 24, PlatformIntrinsic => 25, Unadjusted => 26, - } + }; + debug_assert!( + AbiDatas + .iter() + .enumerate() + .find(|(_, AbiData { abi, .. })| *abi == self) + .map(|(index, _)| index) + .expect("abi variant has associated data") + == i, + "Abi index did not match `AbiDatas` ordering" + ); + i } #[inline] diff --git a/src/doc/unstable-book/src/language-features/c-unwind.md b/src/doc/unstable-book/src/language-features/c-unwind.md index c1705d59acc21..2801d9b5e7778 100644 --- a/src/doc/unstable-book/src/language-features/c-unwind.md +++ b/src/doc/unstable-book/src/language-features/c-unwind.md @@ -6,7 +6,8 @@ The tracking issue for this feature is: [#74990] ------------------------ -Introduces a new ABI string, "C-unwind", to enable unwinding from other +Introduces four new ABI strings: "C-unwind", "stdcall-unwind", +"thiscall-unwind", and "system-unwind". These enable unwinding from other languages (such as C++) into Rust frames and from Rust into other languages. See [RFC 2945] for more information. From 48167c499aa8ad8d7b641f79e6ab9cc163ebc7dd Mon Sep 17 00:00:00 2001 From: Nicholas-Baron Date: Sat, 13 Feb 2021 21:28:34 -0800 Subject: [PATCH 51/62] Moved Context and its impls to a separate file --- src/librustdoc/html/render/context.rs | 611 ++++++++++++++++++++++++++ src/librustdoc/html/render/mod.rs | 600 +------------------------ 2 files changed, 618 insertions(+), 593 deletions(-) create mode 100644 src/librustdoc/html/render/context.rs diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs new file mode 100644 index 0000000000000..1198df04abb62 --- /dev/null +++ b/src/librustdoc/html/render/context.rs @@ -0,0 +1,611 @@ +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::mpsc::{channel, Receiver}; +use std::sync::Arc; + +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_middle::ty::TyCtxt; +use rustc_session::Session; +use rustc_span::edition::Edition; +use rustc_span::source_map::FileName; +use rustc_span::symbol::sym; + +use super::cache::{build_index, ExternalLocation}; +use super::{ + full_path, item_path, make_item_keywords, print_item, print_sidebar, settings, write_shared, + AllTypes, NameDoc, SharedContext, StylePath, BASIC_KEYWORDS, CURRENT_DEPTH, INITIAL_IDS, +}; + +use crate::clean::{self, AttributesExt}; +use crate::config::RenderOptions; +use crate::docfs::{DocFS, PathError}; +use crate::error::Error; +use crate::formats::cache::Cache; +use crate::formats::item_type::ItemType; +use crate::formats::FormatRenderer; +use crate::html::escape::Escape; +use crate::html::format::Buffer; +use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap}; +use crate::html::{layout, sources}; + +/// Major driving force in all rustdoc rendering. This contains information +/// about where in the tree-like hierarchy rendering is occurring and controls +/// how the current page is being rendered. +/// +/// It is intended that this context is a lightweight object which can be fairly +/// easily cloned because it is cloned per work-job (about once per item in the +/// rustdoc tree). +#[derive(Clone)] +crate struct Context<'tcx> { + /// Current hierarchy of components leading down to what's currently being + /// rendered + crate current: Vec, + /// The current destination folder of where HTML artifacts should be placed. + /// This changes as the context descends into the module hierarchy. + crate dst: PathBuf, + /// A flag, which when `true`, will render pages which redirect to the + /// real location of an item. This is used to allow external links to + /// publicly reused items to redirect to the right location. + crate render_redirect_pages: bool, + /// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set + /// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of + /// the crate. + crate redirections: Option>>>, + /// The map used to ensure all generated 'id=' attributes are unique. + pub(super) id_map: Rc>, + /// Tracks section IDs for `Deref` targets so they match in both the main + /// body and the sidebar. + pub(super) deref_id_map: Rc>>, + crate shared: Arc>, + all: Rc>, + /// Storage for the errors produced while generating documentation so they + /// can be printed together at the end. + crate errors: Rc>, + crate cache: Rc, +} + +impl<'tcx> Context<'tcx> { + pub(super) fn path(&self, filename: &str) -> PathBuf { + // We use splitn vs Path::extension here because we might get a filename + // like `style.min.css` and we want to process that into + // `style-suffix.min.css`. Path::extension would just return `css` + // which would result in `style.min-suffix.css` which isn't what we + // want. + let (base, ext) = filename.split_once('.').unwrap(); + let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext); + self.dst.join(&filename) + } + + pub(super) fn tcx(&self) -> TyCtxt<'tcx> { + self.shared.tcx + } + + fn sess(&self) -> &'tcx Session { + &self.shared.tcx.sess + } + + pub(super) fn derive_id(&self, id: String) -> String { + let mut map = self.id_map.borrow_mut(); + map.derive(id) + } + + /// String representation of how to get back to the root path of the 'doc/' + /// folder in terms of a relative URL. + pub(super) fn root_path(&self) -> String { + "../".repeat(self.current.len()) + } + + fn render_item(&self, it: &clean::Item, pushname: bool) -> String { + // A little unfortunate that this is done like this, but it sure + // does make formatting *a lot* nicer. + CURRENT_DEPTH.with(|slot| { + slot.set(self.current.len()); + }); + + let mut title = if it.is_primitive() || it.is_keyword() { + // No need to include the namespace for primitive types and keywords + String::new() + } else { + self.current.join("::") + }; + if pushname { + if !title.is_empty() { + title.push_str("::"); + } + title.push_str(&it.name.unwrap().as_str()); + } + title.push_str(" - Rust"); + let tyname = it.type_(); + let desc = it.doc_value().as_ref().map(|doc| plain_text_summary(&doc)); + let desc = if let Some(desc) = desc { + desc + } else if it.is_crate() { + format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate) + } else { + format!( + "API documentation for the Rust `{}` {} in crate `{}`.", + it.name.as_ref().unwrap(), + tyname, + self.shared.layout.krate + ) + }; + let keywords = make_item_keywords(it); + let page = layout::Page { + css_class: tyname.as_str(), + root_path: &self.root_path(), + static_root_path: self.shared.static_root_path.as_deref(), + title: &title, + description: &desc, + keywords: &keywords, + resource_suffix: &self.shared.resource_suffix, + extra_scripts: &[], + static_extra_scripts: &[], + }; + + { + self.id_map.borrow_mut().reset(); + self.id_map.borrow_mut().populate(&INITIAL_IDS); + } + + if !self.render_redirect_pages { + layout::render( + &self.shared.layout, + &page, + |buf: &mut _| print_sidebar(self, it, buf), + |buf: &mut _| print_item(self, it, buf), + &self.shared.style_files, + ) + } else { + if let Some(&(ref names, ty)) = self.cache.paths.get(&it.def_id) { + let mut path = String::new(); + for name in &names[..names.len() - 1] { + path.push_str(name); + path.push('/'); + } + path.push_str(&item_path(ty, names.last().unwrap())); + match self.redirections { + Some(ref redirections) => { + let mut current_path = String::new(); + for name in &self.current { + current_path.push_str(name); + current_path.push('/'); + } + current_path.push_str(&item_path(ty, names.last().unwrap())); + redirections.borrow_mut().insert(current_path, path); + } + None => return layout::redirect(&format!("{}{}", self.root_path(), path)), + } + } + String::new() + } + } + + /// Construct a map of items shown in the sidebar to a plain-text summary of their docs. + fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> { + // BTreeMap instead of HashMap to get a sorted output + let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); + for item in &m.items { + if item.is_stripped() { + continue; + } + + let short = item.type_(); + let myname = match item.name { + None => continue, + Some(ref s) => s.to_string(), + }; + let short = short.to_string(); + map.entry(short).or_default().push(( + myname, + Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))), + )); + } + + if self.shared.sort_modules_alphabetically { + for items in map.values_mut() { + items.sort(); + } + } + map + } + + /// Generates a url appropriate for an `href` attribute back to the source of + /// this item. + /// + /// The url generated, when clicked, will redirect the browser back to the + /// original source code. + /// + /// If `None` is returned, then a source link couldn't be generated. This + /// may happen, for example, with externally inlined items where the source + /// of their crate documentation isn't known. + pub(super) fn src_href(&self, item: &clean::Item) -> Option { + if item.source.is_dummy() { + return None; + } + let mut root = self.root_path(); + let mut path = String::new(); + let cnum = item.source.cnum(self.sess()); + + // We can safely ignore synthetic `SourceFile`s. + let file = match item.source.filename(self.sess()) { + FileName::Real(ref path) => path.local_path().to_path_buf(), + _ => return None, + }; + let file = &file; + + let symbol; + let (krate, path) = if cnum == LOCAL_CRATE { + if let Some(path) = self.shared.local_sources.get(file) { + (self.shared.layout.krate.as_str(), path) + } else { + return None; + } + } else { + let (krate, src_root) = match *self.cache.extern_locations.get(&cnum)? { + (name, ref src, ExternalLocation::Local) => (name, src), + (name, ref src, ExternalLocation::Remote(ref s)) => { + root = s.to_string(); + (name, src) + } + (_, _, ExternalLocation::Unknown) => return None, + }; + + sources::clean_path(&src_root, file, false, |component| { + path.push_str(&component.to_string_lossy()); + path.push('/'); + }); + let mut fname = file.file_name().expect("source has no filename").to_os_string(); + fname.push(".html"); + path.push_str(&fname.to_string_lossy()); + symbol = krate.as_str(); + (&*symbol, &path) + }; + + let loline = item.source.lo(self.sess()).line; + let hiline = item.source.hi(self.sess()).line; + let lines = + if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) }; + Some(format!( + "{root}src/{krate}/{path}#{lines}", + root = Escape(&root), + krate = krate, + path = path, + lines = lines + )) + } +} + +/// Generates the documentation for `crate` into the directory `dst` +impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { + fn descr() -> &'static str { + "html" + } + + fn init( + mut krate: clean::Crate, + options: RenderOptions, + edition: Edition, + mut cache: Cache, + tcx: TyCtxt<'tcx>, + ) -> Result<(Self, clean::Crate), Error> { + // need to save a copy of the options for rendering the index page + let md_opts = options.clone(); + let RenderOptions { + output, + external_html, + id_map, + playground_url, + sort_modules_alphabetically, + themes: style_files, + default_settings, + extension_css, + resource_suffix, + static_root_path, + generate_search_filter, + unstable_features, + generate_redirect_map, + .. + } = options; + + let src_root = match krate.src { + FileName::Real(ref p) => match p.local_path().parent() { + Some(p) => p.to_path_buf(), + None => PathBuf::new(), + }, + _ => PathBuf::new(), + }; + // If user passed in `--playground-url` arg, we fill in crate name here + let mut playground = None; + if let Some(url) = playground_url { + playground = + Some(markdown::Playground { crate_name: Some(krate.name.to_string()), url }); + } + let mut layout = layout::Layout { + logo: String::new(), + favicon: String::new(), + external_html, + default_settings, + krate: krate.name.to_string(), + css_file_extension: extension_css, + generate_search_filter, + }; + let mut issue_tracker_base_url = None; + let mut include_sources = true; + + // Crawl the crate attributes looking for attributes which control how we're + // going to emit HTML + if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) { + for attr in attrs.lists(sym::doc) { + match (attr.name_or_empty(), attr.value_str()) { + (sym::html_favicon_url, Some(s)) => { + layout.favicon = s.to_string(); + } + (sym::html_logo_url, Some(s)) => { + layout.logo = s.to_string(); + } + (sym::html_playground_url, Some(s)) => { + playground = Some(markdown::Playground { + crate_name: Some(krate.name.to_string()), + url: s.to_string(), + }); + } + (sym::issue_tracker_base_url, Some(s)) => { + issue_tracker_base_url = Some(s.to_string()); + } + (sym::html_no_source, None) if attr.is_word() => { + include_sources = false; + } + _ => {} + } + } + } + let (sender, receiver) = channel(); + let mut scx = SharedContext { + tcx, + collapsed: krate.collapsed, + src_root, + include_sources, + local_sources: Default::default(), + issue_tracker_base_url, + layout, + created_dirs: Default::default(), + sort_modules_alphabetically, + style_files, + resource_suffix, + static_root_path, + fs: DocFS::new(sender), + edition, + codes: ErrorCodes::from(unstable_features.is_nightly_build()), + playground, + }; + + // Add the default themes to the `Vec` of stylepaths + // + // Note that these must be added before `sources::render` is called + // so that the resulting source pages are styled + // + // `light.css` is not disabled because it is the stylesheet that stays loaded + // by the browser as the theme stylesheet. The theme system (hackily) works by + // changing the href to this stylesheet. All other themes are disabled to + // prevent rule conflicts + scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false }); + scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true }); + scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true }); + + let dst = output; + scx.ensure_dir(&dst)?; + krate = sources::render(&dst, &mut scx, krate)?; + + // Build our search index + let index = build_index(&krate, &mut cache, tcx); + + let mut cx = Context { + current: Vec::new(), + dst, + render_redirect_pages: false, + id_map: Rc::new(RefCell::new(id_map)), + deref_id_map: Rc::new(RefCell::new(FxHashMap::default())), + shared: Arc::new(scx), + all: Rc::new(RefCell::new(AllTypes::new())), + errors: Rc::new(receiver), + cache: Rc::new(cache), + redirections: if generate_redirect_map { Some(Default::default()) } else { None }, + }; + + CURRENT_DEPTH.with(|s| s.set(0)); + + // Write shared runs within a flock; disable thread dispatching of IO temporarily. + Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); + write_shared(&cx, &krate, index, &md_opts)?; + Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); + Ok((cx, krate)) + } + + fn after_krate( + &mut self, + krate: &clean::Crate, + diag: &rustc_errors::Handler, + ) -> Result<(), Error> { + let final_file = self.dst.join(&*krate.name.as_str()).join("all.html"); + let settings_file = self.dst.join("settings.html"); + let crate_name = krate.name; + + let mut root_path = self.dst.to_str().expect("invalid path").to_owned(); + if !root_path.ends_with('/') { + root_path.push('/'); + } + let mut page = layout::Page { + title: "List of all items in this crate", + css_class: "mod", + root_path: "../", + static_root_path: self.shared.static_root_path.as_deref(), + description: "List of all items in this crate", + keywords: BASIC_KEYWORDS, + resource_suffix: &self.shared.resource_suffix, + extra_scripts: &[], + static_extra_scripts: &[], + }; + let sidebar = if let Some(ref version) = self.cache.crate_version { + format!( + "

Crate {}

\ +
\ +

Version {}

\ +
\ +

Back to index

", + crate_name, + Escape(version), + ) + } else { + String::new() + }; + let all = self.all.replace(AllTypes::new()); + let v = layout::render( + &self.shared.layout, + &page, + sidebar, + |buf: &mut Buffer| all.print(buf), + &self.shared.style_files, + ); + self.shared.fs.write(&final_file, v.as_bytes())?; + + // Generating settings page. + page.title = "Rustdoc settings"; + page.description = "Settings of Rustdoc"; + page.root_path = "./"; + + let mut style_files = self.shared.style_files.clone(); + let sidebar = "

Settings

"; + style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false }); + let v = layout::render( + &self.shared.layout, + &page, + sidebar, + settings( + self.shared.static_root_path.as_deref().unwrap_or("./"), + &self.shared.resource_suffix, + &self.shared.style_files, + )?, + &style_files, + ); + self.shared.fs.write(&settings_file, v.as_bytes())?; + if let Some(redirections) = self.redirections.take() { + if !redirections.borrow().is_empty() { + let redirect_map_path = + self.dst.join(&*krate.name.as_str()).join("redirect-map.json"); + let paths = serde_json::to_string(&*redirections.borrow()).unwrap(); + self.shared.ensure_dir(&self.dst.join(&*krate.name.as_str()))?; + self.shared.fs.write(&redirect_map_path, paths.as_bytes())?; + } + } + + // Flush pending errors. + Arc::get_mut(&mut self.shared).unwrap().fs.close(); + let nb_errors = self.errors.iter().map(|err| diag.struct_err(&err).emit()).count(); + if nb_errors > 0 { + Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), "")) + } else { + Ok(()) + } + } + + fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error> { + // Stripped modules survive the rustdoc passes (i.e., `strip-private`) + // if they contain impls for public types. These modules can also + // contain items such as publicly re-exported structures. + // + // External crates will provide links to these structures, so + // these modules are recursed into, but not rendered normally + // (a flag on the context). + if !self.render_redirect_pages { + self.render_redirect_pages = item.is_stripped(); + } + let scx = &self.shared; + self.dst.push(item_name); + self.current.push(item_name.to_owned()); + + info!("Recursing into {}", self.dst.display()); + + let buf = self.render_item(item, false); + // buf will be empty if the module is stripped and there is no redirect for it + if !buf.is_empty() { + self.shared.ensure_dir(&self.dst)?; + let joint_dst = self.dst.join("index.html"); + scx.fs.write(&joint_dst, buf.as_bytes())?; + } + + // Render sidebar-items.js used throughout this module. + if !self.render_redirect_pages { + let module = match *item.kind { + clean::StrippedItem(box clean::ModuleItem(ref m)) | clean::ModuleItem(ref m) => m, + _ => unreachable!(), + }; + let items = self.build_sidebar_items(module); + let js_dst = self.dst.join("sidebar-items.js"); + let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap()); + scx.fs.write(&js_dst, &v)?; + } + Ok(()) + } + + fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> { + info!("Recursed; leaving {}", self.dst.display()); + + // Go back to where we were at + self.dst.pop(); + self.current.pop(); + Ok(()) + } + + fn item(&mut self, item: clean::Item) -> Result<(), Error> { + // Stripped modules survive the rustdoc passes (i.e., `strip-private`) + // if they contain impls for public types. These modules can also + // contain items such as publicly re-exported structures. + // + // External crates will provide links to these structures, so + // these modules are recursed into, but not rendered normally + // (a flag on the context). + if !self.render_redirect_pages { + self.render_redirect_pages = item.is_stripped(); + } + + let buf = self.render_item(&item, true); + // buf will be empty if the item is stripped and there is no redirect for it + if !buf.is_empty() { + let name = item.name.as_ref().unwrap(); + let item_type = item.type_(); + let file_name = &item_path(item_type, &name.as_str()); + self.shared.ensure_dir(&self.dst)?; + let joint_dst = self.dst.join(file_name); + self.shared.fs.write(&joint_dst, buf.as_bytes())?; + + if !self.render_redirect_pages { + self.all.borrow_mut().append(full_path(self, &item), &item_type); + } + // If the item is a macro, redirect from the old macro URL (with !) + // to the new one (without). + if item_type == ItemType::Macro { + let redir_name = format!("{}.{}!.html", item_type, name); + if let Some(ref redirections) = self.redirections { + let crate_name = &self.shared.layout.krate; + redirections.borrow_mut().insert( + format!("{}/{}", crate_name, redir_name), + format!("{}/{}", crate_name, file_name), + ); + } else { + let v = layout::redirect(file_name); + let redir_dst = self.dst.join(redir_name); + self.shared.fs.write(&redir_dst, v.as_bytes())?; + } + } + } + Ok(()) + } + + fn cache(&self) -> &Cache { + &self.cache + } +} diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index fbeeb2a6fa2f2..17c32969e049e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -30,9 +30,12 @@ crate mod cache; #[cfg(test)] mod tests; +mod context; +crate use context::*; + use std::cell::{Cell, RefCell}; use std::cmp::Ordering; -use std::collections::{BTreeMap, VecDeque}; +use std::collections::VecDeque; use std::default::Default; use std::ffi::OsStr; use std::fmt::{self, Write}; @@ -40,11 +43,8 @@ use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::{Component, Path, PathBuf}; -use std::rc::Rc; use std::str; use std::string::ToString; -use std::sync::mpsc::{channel, Receiver}; -use std::sync::Arc; use itertools::Itertools; use rustc_ast_pretty::pprust; @@ -53,19 +53,17 @@ use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::CtorKind; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::DefId; use rustc_hir::Mutability; use rustc_middle::middle::stability; use rustc_middle::ty::TyCtxt; -use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::FileName; use rustc_span::symbol::{kw, sym, Symbol}; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; -use crate::clean::{self, AttributesExt, GetDefId, RenderedLink, SelfTy, TypeKind}; +use crate::clean::{self, GetDefId, RenderedLink, SelfTy, TypeKind}; use crate::config::RenderOptions; use crate::docfs::{DocFS, PathError}; use crate::error::Error; @@ -76,12 +74,8 @@ use crate::html::escape::Escape; use crate::html::format::Function; use crate::html::format::{href, print_default_space, print_generic_bounds, WhereClause}; use crate::html::format::{print_abi_with_space, Buffer, PrintWithSpace}; -use crate::html::markdown::{ - self, plain_text_summary, ErrorCodes, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine, -}; -use crate::html::sources; +use crate::html::markdown::{self, ErrorCodes, Markdown, MarkdownHtml, MarkdownSummaryLine}; use crate::html::{highlight, layout, static_files}; -use cache::{build_index, ExternalLocation}; /// A pair of name and its optional document. crate type NameDoc = (String, Option); @@ -92,42 +86,6 @@ crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { }) } -/// Major driving force in all rustdoc rendering. This contains information -/// about where in the tree-like hierarchy rendering is occurring and controls -/// how the current page is being rendered. -/// -/// It is intended that this context is a lightweight object which can be fairly -/// easily cloned because it is cloned per work-job (about once per item in the -/// rustdoc tree). -#[derive(Clone)] -crate struct Context<'tcx> { - /// Current hierarchy of components leading down to what's currently being - /// rendered - crate current: Vec, - /// The current destination folder of where HTML artifacts should be placed. - /// This changes as the context descends into the module hierarchy. - crate dst: PathBuf, - /// A flag, which when `true`, will render pages which redirect to the - /// real location of an item. This is used to allow external links to - /// publicly reused items to redirect to the right location. - crate render_redirect_pages: bool, - /// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set - /// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of - /// the crate. - crate redirections: Option>>>, - /// The map used to ensure all generated 'id=' attributes are unique. - id_map: Rc>, - /// Tracks section IDs for `Deref` targets so they match in both the main - /// body and the sidebar. - deref_id_map: Rc>>, - crate shared: Arc>, - all: Rc>, - /// Storage for the errors produced while generating documentation so they - /// can be printed together at the end. - crate errors: Rc>, - crate cache: Rc, -} - crate struct SharedContext<'tcx> { crate tcx: TyCtxt<'tcx>, /// The path to the crate root source minus the file name. @@ -169,27 +127,6 @@ crate struct SharedContext<'tcx> { playground: Option, } -impl<'tcx> Context<'tcx> { - fn path(&self, filename: &str) -> PathBuf { - // We use splitn vs Path::extension here because we might get a filename - // like `style.min.css` and we want to process that into - // `style-suffix.min.css`. Path::extension would just return `css` - // which would result in `style.min-suffix.css` which isn't what we - // want. - let (base, ext) = filename.split_once('.').unwrap(); - let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext); - self.dst.join(&filename) - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.shared.tcx - } - - fn sess(&self) -> &'tcx Session { - &self.shared.tcx.sess - } -} - impl SharedContext<'_> { crate fn ensure_dir(&self, dst: &Path) -> Result<(), Error> { let mut dirs = self.created_dirs.borrow_mut(); @@ -380,337 +317,6 @@ crate const INITIAL_IDS: [&'static str; 15] = [ "implementations", ]; -/// Generates the documentation for `crate` into the directory `dst` -impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { - fn descr() -> &'static str { - "html" - } - - fn init( - mut krate: clean::Crate, - options: RenderOptions, - edition: Edition, - mut cache: Cache, - tcx: TyCtxt<'tcx>, - ) -> Result<(Self, clean::Crate), Error> { - // need to save a copy of the options for rendering the index page - let md_opts = options.clone(); - let RenderOptions { - output, - external_html, - id_map, - playground_url, - sort_modules_alphabetically, - themes: style_files, - default_settings, - extension_css, - resource_suffix, - static_root_path, - generate_search_filter, - unstable_features, - generate_redirect_map, - .. - } = options; - - let src_root = match krate.src { - FileName::Real(ref p) => match p.local_path().parent() { - Some(p) => p.to_path_buf(), - None => PathBuf::new(), - }, - _ => PathBuf::new(), - }; - // If user passed in `--playground-url` arg, we fill in crate name here - let mut playground = None; - if let Some(url) = playground_url { - playground = - Some(markdown::Playground { crate_name: Some(krate.name.to_string()), url }); - } - let mut layout = layout::Layout { - logo: String::new(), - favicon: String::new(), - external_html, - default_settings, - krate: krate.name.to_string(), - css_file_extension: extension_css, - generate_search_filter, - }; - let mut issue_tracker_base_url = None; - let mut include_sources = true; - - // Crawl the crate attributes looking for attributes which control how we're - // going to emit HTML - if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) { - for attr in attrs.lists(sym::doc) { - match (attr.name_or_empty(), attr.value_str()) { - (sym::html_favicon_url, Some(s)) => { - layout.favicon = s.to_string(); - } - (sym::html_logo_url, Some(s)) => { - layout.logo = s.to_string(); - } - (sym::html_playground_url, Some(s)) => { - playground = Some(markdown::Playground { - crate_name: Some(krate.name.to_string()), - url: s.to_string(), - }); - } - (sym::issue_tracker_base_url, Some(s)) => { - issue_tracker_base_url = Some(s.to_string()); - } - (sym::html_no_source, None) if attr.is_word() => { - include_sources = false; - } - _ => {} - } - } - } - let (sender, receiver) = channel(); - let mut scx = SharedContext { - tcx, - collapsed: krate.collapsed, - src_root, - include_sources, - local_sources: Default::default(), - issue_tracker_base_url, - layout, - created_dirs: Default::default(), - sort_modules_alphabetically, - style_files, - resource_suffix, - static_root_path, - fs: DocFS::new(sender), - edition, - codes: ErrorCodes::from(unstable_features.is_nightly_build()), - playground, - }; - - // Add the default themes to the `Vec` of stylepaths - // - // Note that these must be added before `sources::render` is called - // so that the resulting source pages are styled - // - // `light.css` is not disabled because it is the stylesheet that stays loaded - // by the browser as the theme stylesheet. The theme system (hackily) works by - // changing the href to this stylesheet. All other themes are disabled to - // prevent rule conflicts - scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false }); - scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true }); - scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true }); - - let dst = output; - scx.ensure_dir(&dst)?; - krate = sources::render(&dst, &mut scx, krate)?; - - // Build our search index - let index = build_index(&krate, &mut cache, tcx); - - let mut cx = Context { - current: Vec::new(), - dst, - render_redirect_pages: false, - id_map: Rc::new(RefCell::new(id_map)), - deref_id_map: Rc::new(RefCell::new(FxHashMap::default())), - shared: Arc::new(scx), - all: Rc::new(RefCell::new(AllTypes::new())), - errors: Rc::new(receiver), - cache: Rc::new(cache), - redirections: if generate_redirect_map { Some(Default::default()) } else { None }, - }; - - CURRENT_DEPTH.with(|s| s.set(0)); - - // Write shared runs within a flock; disable thread dispatching of IO temporarily. - Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); - write_shared(&cx, &krate, index, &md_opts)?; - Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); - Ok((cx, krate)) - } - - fn after_krate( - &mut self, - krate: &clean::Crate, - diag: &rustc_errors::Handler, - ) -> Result<(), Error> { - let final_file = self.dst.join(&*krate.name.as_str()).join("all.html"); - let settings_file = self.dst.join("settings.html"); - let crate_name = krate.name; - - let mut root_path = self.dst.to_str().expect("invalid path").to_owned(); - if !root_path.ends_with('/') { - root_path.push('/'); - } - let mut page = layout::Page { - title: "List of all items in this crate", - css_class: "mod", - root_path: "../", - static_root_path: self.shared.static_root_path.as_deref(), - description: "List of all items in this crate", - keywords: BASIC_KEYWORDS, - resource_suffix: &self.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - let sidebar = if let Some(ref version) = self.cache.crate_version { - format!( - "

Crate {}

\ -
\ -

Version {}

\ -
\ -

Back to index

", - crate_name, - Escape(version), - ) - } else { - String::new() - }; - let all = self.all.replace(AllTypes::new()); - let v = layout::render( - &self.shared.layout, - &page, - sidebar, - |buf: &mut Buffer| all.print(buf), - &self.shared.style_files, - ); - self.shared.fs.write(&final_file, v.as_bytes())?; - - // Generating settings page. - page.title = "Rustdoc settings"; - page.description = "Settings of Rustdoc"; - page.root_path = "./"; - - let mut style_files = self.shared.style_files.clone(); - let sidebar = "

Settings

"; - style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false }); - let v = layout::render( - &self.shared.layout, - &page, - sidebar, - settings( - self.shared.static_root_path.as_deref().unwrap_or("./"), - &self.shared.resource_suffix, - &self.shared.style_files, - )?, - &style_files, - ); - self.shared.fs.write(&settings_file, v.as_bytes())?; - if let Some(redirections) = self.redirections.take() { - if !redirections.borrow().is_empty() { - let redirect_map_path = - self.dst.join(&*krate.name.as_str()).join("redirect-map.json"); - let paths = serde_json::to_string(&*redirections.borrow()).unwrap(); - self.shared.ensure_dir(&self.dst.join(&*krate.name.as_str()))?; - self.shared.fs.write(&redirect_map_path, paths.as_bytes())?; - } - } - - // Flush pending errors. - Arc::get_mut(&mut self.shared).unwrap().fs.close(); - let nb_errors = self.errors.iter().map(|err| diag.struct_err(&err).emit()).count(); - if nb_errors > 0 { - Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), "")) - } else { - Ok(()) - } - } - - fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error> { - // Stripped modules survive the rustdoc passes (i.e., `strip-private`) - // if they contain impls for public types. These modules can also - // contain items such as publicly re-exported structures. - // - // External crates will provide links to these structures, so - // these modules are recursed into, but not rendered normally - // (a flag on the context). - if !self.render_redirect_pages { - self.render_redirect_pages = item.is_stripped(); - } - let scx = &self.shared; - self.dst.push(item_name); - self.current.push(item_name.to_owned()); - - info!("Recursing into {}", self.dst.display()); - - let buf = self.render_item(item, false); - // buf will be empty if the module is stripped and there is no redirect for it - if !buf.is_empty() { - self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join("index.html"); - scx.fs.write(&joint_dst, buf.as_bytes())?; - } - - // Render sidebar-items.js used throughout this module. - if !self.render_redirect_pages { - let module = match *item.kind { - clean::StrippedItem(box clean::ModuleItem(ref m)) | clean::ModuleItem(ref m) => m, - _ => unreachable!(), - }; - let items = self.build_sidebar_items(module); - let js_dst = self.dst.join("sidebar-items.js"); - let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap()); - scx.fs.write(&js_dst, &v)?; - } - Ok(()) - } - - fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> { - info!("Recursed; leaving {}", self.dst.display()); - - // Go back to where we were at - self.dst.pop(); - self.current.pop(); - Ok(()) - } - - fn item(&mut self, item: clean::Item) -> Result<(), Error> { - // Stripped modules survive the rustdoc passes (i.e., `strip-private`) - // if they contain impls for public types. These modules can also - // contain items such as publicly re-exported structures. - // - // External crates will provide links to these structures, so - // these modules are recursed into, but not rendered normally - // (a flag on the context). - if !self.render_redirect_pages { - self.render_redirect_pages = item.is_stripped(); - } - - let buf = self.render_item(&item, true); - // buf will be empty if the item is stripped and there is no redirect for it - if !buf.is_empty() { - let name = item.name.as_ref().unwrap(); - let item_type = item.type_(); - let file_name = &item_path(item_type, &name.as_str()); - self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join(file_name); - self.shared.fs.write(&joint_dst, buf.as_bytes())?; - - if !self.render_redirect_pages { - self.all.borrow_mut().append(full_path(self, &item), &item_type); - } - // If the item is a macro, redirect from the old macro URL (with !) - // to the new one (without). - if item_type == ItemType::Macro { - let redir_name = format!("{}.{}!.html", item_type, name); - if let Some(ref redirections) = self.redirections { - let crate_name = &self.shared.layout.krate; - redirections.borrow_mut().insert( - format!("{}/{}", crate_name, redir_name), - format!("{}/{}", crate_name, file_name), - ); - } else { - let v = layout::redirect(file_name); - let redir_dst = self.dst.join(redir_name); - self.shared.fs.write(&redir_dst, v.as_bytes())?; - } - } - } - Ok(()) - } - - fn cache(&self) -> &Cache { - &self.cache - } -} - fn write_shared( cx: &Context<'_>, krate: &clean::Crate, @@ -1541,198 +1147,6 @@ fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result { - fn derive_id(&self, id: String) -> String { - let mut map = self.id_map.borrow_mut(); - map.derive(id) - } - - /// String representation of how to get back to the root path of the 'doc/' - /// folder in terms of a relative URL. - fn root_path(&self) -> String { - "../".repeat(self.current.len()) - } - - fn render_item(&self, it: &clean::Item, pushname: bool) -> String { - // A little unfortunate that this is done like this, but it sure - // does make formatting *a lot* nicer. - CURRENT_DEPTH.with(|slot| { - slot.set(self.current.len()); - }); - - let mut title = if it.is_primitive() || it.is_keyword() { - // No need to include the namespace for primitive types and keywords - String::new() - } else { - self.current.join("::") - }; - if pushname { - if !title.is_empty() { - title.push_str("::"); - } - title.push_str(&it.name.unwrap().as_str()); - } - title.push_str(" - Rust"); - let tyname = it.type_(); - let desc = it.doc_value().as_ref().map(|doc| plain_text_summary(&doc)); - let desc = if let Some(desc) = desc { - desc - } else if it.is_crate() { - format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate) - } else { - format!( - "API documentation for the Rust `{}` {} in crate `{}`.", - it.name.as_ref().unwrap(), - tyname, - self.shared.layout.krate - ) - }; - let keywords = make_item_keywords(it); - let page = layout::Page { - css_class: tyname.as_str(), - root_path: &self.root_path(), - static_root_path: self.shared.static_root_path.as_deref(), - title: &title, - description: &desc, - keywords: &keywords, - resource_suffix: &self.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - - { - self.id_map.borrow_mut().reset(); - self.id_map.borrow_mut().populate(&INITIAL_IDS); - } - - if !self.render_redirect_pages { - layout::render( - &self.shared.layout, - &page, - |buf: &mut _| print_sidebar(self, it, buf), - |buf: &mut _| print_item(self, it, buf), - &self.shared.style_files, - ) - } else { - if let Some(&(ref names, ty)) = self.cache.paths.get(&it.def_id) { - let mut path = String::new(); - for name in &names[..names.len() - 1] { - path.push_str(name); - path.push('/'); - } - path.push_str(&item_path(ty, names.last().unwrap())); - match self.redirections { - Some(ref redirections) => { - let mut current_path = String::new(); - for name in &self.current { - current_path.push_str(name); - current_path.push('/'); - } - current_path.push_str(&item_path(ty, names.last().unwrap())); - redirections.borrow_mut().insert(current_path, path); - } - None => return layout::redirect(&format!("{}{}", self.root_path(), path)), - } - } - String::new() - } - } - - /// Construct a map of items shown in the sidebar to a plain-text summary of their docs. - fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> { - // BTreeMap instead of HashMap to get a sorted output - let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); - for item in &m.items { - if item.is_stripped() { - continue; - } - - let short = item.type_(); - let myname = match item.name { - None => continue, - Some(ref s) => s.to_string(), - }; - let short = short.to_string(); - map.entry(short).or_default().push(( - myname, - Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))), - )); - } - - if self.shared.sort_modules_alphabetically { - for items in map.values_mut() { - items.sort(); - } - } - map - } - - /// Generates a url appropriate for an `href` attribute back to the source of - /// this item. - /// - /// The url generated, when clicked, will redirect the browser back to the - /// original source code. - /// - /// If `None` is returned, then a source link couldn't be generated. This - /// may happen, for example, with externally inlined items where the source - /// of their crate documentation isn't known. - fn src_href(&self, item: &clean::Item) -> Option { - if item.source.is_dummy() { - return None; - } - let mut root = self.root_path(); - let mut path = String::new(); - let cnum = item.source.cnum(self.sess()); - - // We can safely ignore synthetic `SourceFile`s. - let file = match item.source.filename(self.sess()) { - FileName::Real(ref path) => path.local_path().to_path_buf(), - _ => return None, - }; - let file = &file; - - let symbol; - let (krate, path) = if cnum == LOCAL_CRATE { - if let Some(path) = self.shared.local_sources.get(file) { - (self.shared.layout.krate.as_str(), path) - } else { - return None; - } - } else { - let (krate, src_root) = match *self.cache.extern_locations.get(&cnum)? { - (name, ref src, ExternalLocation::Local) => (name, src), - (name, ref src, ExternalLocation::Remote(ref s)) => { - root = s.to_string(); - (name, src) - } - (_, _, ExternalLocation::Unknown) => return None, - }; - - sources::clean_path(&src_root, file, false, |component| { - path.push_str(&component.to_string_lossy()); - path.push('/'); - }); - let mut fname = file.file_name().expect("source has no filename").to_os_string(); - fname.push(".html"); - path.push_str(&fname.to_string_lossy()); - symbol = krate.as_str(); - (&*symbol, &path) - }; - - let loline = item.source.lo(self.sess()).line; - let hiline = item.source.hi(self.sess()).line; - let lines = - if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) }; - Some(format!( - "{root}src/{krate}/{path}#{lines}", - root = Escape(&root), - krate = krate, - path = path, - lines = lines - )) - } -} - fn wrap_into_docblock(w: &mut Buffer, f: F) where F: FnOnce(&mut Buffer), From 6c7d7a6bf4b8a3a653d8cba0acf5b20b088aff2e Mon Sep 17 00:00:00 2001 From: Nicholas-Baron Date: Sat, 13 Feb 2021 22:23:05 -0800 Subject: [PATCH 52/62] Moved `print_item` and helpers to a separate file --- src/librustdoc/html/render/context.rs | 5 +- src/librustdoc/html/render/mod.rs | 1409 +-------------------- src/librustdoc/html/render/print_item.rs | 1420 ++++++++++++++++++++++ 3 files changed, 1430 insertions(+), 1404 deletions(-) create mode 100644 src/librustdoc/html/render/print_item.rs diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 1198df04abb62..4322e2448341c 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -15,9 +15,10 @@ use rustc_span::source_map::FileName; use rustc_span::symbol::sym; use super::cache::{build_index, ExternalLocation}; +use super::print_item::{full_path, item_path, print_item}; use super::{ - full_path, item_path, make_item_keywords, print_item, print_sidebar, settings, write_shared, - AllTypes, NameDoc, SharedContext, StylePath, BASIC_KEYWORDS, CURRENT_DEPTH, INITIAL_IDS, + make_item_keywords, print_sidebar, settings, write_shared, AllTypes, NameDoc, SharedContext, + StylePath, BASIC_KEYWORDS, CURRENT_DEPTH, INITIAL_IDS, }; use crate::clean::{self, AttributesExt}; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 17c32969e049e..2a13d190318f5 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -33,8 +33,9 @@ mod tests; mod context; crate use context::*; +mod print_item; + use std::cell::{Cell, RefCell}; -use std::cmp::Ordering; use std::collections::VecDeque; use std::default::Default; use std::ffi::OsStr; @@ -58,7 +59,6 @@ use rustc_hir::Mutability; use rustc_middle::middle::stability; use rustc_middle::ty::TyCtxt; use rustc_span::edition::Edition; -use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; @@ -71,11 +71,12 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode}; use crate::html::escape::Escape; -use crate::html::format::Function; -use crate::html::format::{href, print_default_space, print_generic_bounds, WhereClause}; -use crate::html::format::{print_abi_with_space, Buffer, PrintWithSpace}; +use crate::html::format::{ + href, print_abi_with_space, print_default_space, print_generic_bounds, Buffer, Function, + PrintWithSpace, WhereClause, +}; use crate::html::markdown::{self, ErrorCodes, Markdown, MarkdownHtml, MarkdownSummaryLine}; -use crate::html::{highlight, layout, static_files}; +use crate::html::{layout, static_files}; /// A pair of name and its optional document. crate type NameDoc = (String, Option); @@ -1147,136 +1148,6 @@ fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result(w: &mut Buffer, f: F) -where - F: FnOnce(&mut Buffer), -{ - w.write_str("
"); - f(w); - w.write_str("
") -} - -fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { - debug_assert!(!item.is_stripped()); - // Write the breadcrumb trail header for the top - buf.write_str("

"); - let name = match *item.kind { - clean::ModuleItem(ref m) => { - if m.is_crate { - "Crate " - } else { - "Module " - } - } - clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", - clean::TraitItem(..) => "Trait ", - clean::StructItem(..) => "Struct ", - clean::UnionItem(..) => "Union ", - clean::EnumItem(..) => "Enum ", - clean::TypedefItem(..) => "Type Definition ", - clean::MacroItem(..) => "Macro ", - clean::ProcMacroItem(ref mac) => match mac.kind { - MacroKind::Bang => "Macro ", - MacroKind::Attr => "Attribute Macro ", - MacroKind::Derive => "Derive Macro ", - }, - clean::PrimitiveItem(..) => "Primitive Type ", - clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ", - clean::ConstantItem(..) => "Constant ", - clean::ForeignTypeItem => "Foreign Type ", - clean::KeywordItem(..) => "Keyword ", - clean::OpaqueTyItem(..) => "Opaque Type ", - clean::TraitAliasItem(..) => "Trait Alias ", - _ => { - // We don't generate pages for any other type. - unreachable!(); - } - }; - buf.write_str(name); - if !item.is_primitive() && !item.is_keyword() { - let cur = &cx.current; - let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() }; - for (i, component) in cur.iter().enumerate().take(amt) { - write!( - buf, - "{}::", - "../".repeat(cur.len() - i - 1), - component - ); - } - } - write!(buf, "{}", item.type_(), item.name.as_ref().unwrap()); - - buf.write_str(""); // in-band - buf.write_str(""); - render_stability_since_raw( - buf, - item.stable_since(cx.tcx()).as_deref(), - item.const_stable_since(cx.tcx()).as_deref(), - None, - None, - ); - buf.write_str( - "\ - \ - []\ - \ - ", - ); - - // Write `src` tag - // - // When this item is part of a `crate use` in a downstream crate, the - // [src] link in the downstream documentation will actually come back to - // this page, and this link will be auto-clicked. The `id` attribute is - // used to find the link to auto-click. - if cx.shared.include_sources && !item.is_primitive() { - write_srclink(cx, item, buf); - } - - buf.write_str("

"); // out-of-band - - match *item.kind { - clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), - clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { - item_function(buf, cx, item, f) - } - clean::TraitItem(ref t) => item_trait(buf, cx, item, t), - clean::StructItem(ref s) => item_struct(buf, cx, item, s), - clean::UnionItem(ref s) => item_union(buf, cx, item, s), - clean::EnumItem(ref e) => item_enum(buf, cx, item, e), - clean::TypedefItem(ref t, _) => item_typedef(buf, cx, item, t), - clean::MacroItem(ref m) => item_macro(buf, cx, item, m), - clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), - clean::PrimitiveItem(_) => item_primitive(buf, cx, item), - clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i), - clean::ConstantItem(ref c) => item_constant(buf, cx, item, c), - clean::ForeignTypeItem => item_foreign_type(buf, cx, item), - clean::KeywordItem(_) => item_keyword(buf, cx, item), - clean::OpaqueTyItem(ref e) => item_opaque_ty(buf, cx, item, e), - clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta), - _ => { - // We don't generate pages for any other type. - unreachable!(); - } - } -} - -fn item_path(ty: ItemType, name: &str) -> String { - match ty { - ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)), - _ => format!("{}.{}.html", ty, name), - } -} - -fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { - let mut s = cx.current.join("::"); - s.push_str("::"); - s.push_str(&item.name.unwrap().as_str()); - s -} - fn document(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, parent: Option<&clean::Item>) { if let Some(ref name) = item.name { info!("Documenting {}", name); @@ -1406,324 +1277,6 @@ fn document_item_info( } } -fn document_non_exhaustive_header(item: &clean::Item) -> &str { - if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" } -} - -fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { - if item.is_non_exhaustive() { - write!(w, "
", { - if item.is_struct() { - "struct" - } else if item.is_enum() { - "enum" - } else if item.is_variant() { - "variant" - } else { - "type" - } - }); - - if item.is_struct() { - w.write_str( - "Non-exhaustive structs could have additional fields added in future. \ - Therefore, non-exhaustive structs cannot be constructed in external crates \ - using the traditional Struct {{ .. }} syntax; cannot be \ - matched against without a wildcard ..; and \ - struct update syntax will not work.", - ); - } else if item.is_enum() { - w.write_str( - "Non-exhaustive enums could have additional variants added in future. \ - Therefore, when matching against variants of non-exhaustive enums, an \ - extra wildcard arm must be added to account for any future variants.", - ); - } else if item.is_variant() { - w.write_str( - "Non-exhaustive enum variants could have additional fields added in future. \ - Therefore, non-exhaustive enum variants cannot be constructed in external \ - crates and cannot be matched against.", - ); - } else { - w.write_str( - "This type will require a wildcard arm in any match statements or constructors.", - ); - } - - w.write_str("
"); - } -} - -/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order). -crate fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering { - /// Takes a non-numeric and a numeric part from the given &str. - fn take_parts<'a>(s: &mut &'a str) -> (&'a str, &'a str) { - let i = s.find(|c: char| c.is_ascii_digit()); - let (a, b) = s.split_at(i.unwrap_or(s.len())); - let i = b.find(|c: char| !c.is_ascii_digit()); - let (b, c) = b.split_at(i.unwrap_or(b.len())); - *s = c; - (a, b) - } - - while !lhs.is_empty() || !rhs.is_empty() { - let (la, lb) = take_parts(&mut lhs); - let (ra, rb) = take_parts(&mut rhs); - // First process the non-numeric part. - match la.cmp(ra) { - Ordering::Equal => (), - x => return x, - } - // Then process the numeric part, if both sides have one (and they fit in a u64). - if let (Ok(ln), Ok(rn)) = (lb.parse::(), rb.parse::()) { - match ln.cmp(&rn) { - Ordering::Equal => (), - x => return x, - } - } - // Then process the numeric part again, but this time as strings. - match lb.cmp(rb) { - Ordering::Equal => (), - x => return x, - } - } - - Ordering::Equal -} - -fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) { - document(w, cx, item, None); - - let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>(); - - // the order of item types in the listing - fn reorder(ty: ItemType) -> u8 { - match ty { - ItemType::ExternCrate => 0, - ItemType::Import => 1, - ItemType::Primitive => 2, - ItemType::Module => 3, - ItemType::Macro => 4, - ItemType::Struct => 5, - ItemType::Enum => 6, - ItemType::Constant => 7, - ItemType::Static => 8, - ItemType::Trait => 9, - ItemType::Function => 10, - ItemType::Typedef => 12, - ItemType::Union => 13, - _ => 14 + ty as u8, - } - } - - fn cmp( - i1: &clean::Item, - i2: &clean::Item, - idx1: usize, - idx2: usize, - tcx: TyCtxt<'_>, - ) -> Ordering { - let ty1 = i1.type_(); - let ty2 = i2.type_(); - if ty1 != ty2 { - return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)); - } - let s1 = i1.stability(tcx).as_ref().map(|s| s.level); - let s2 = i2.stability(tcx).as_ref().map(|s| s.level); - if let (Some(a), Some(b)) = (s1, s2) { - match (a.is_stable(), b.is_stable()) { - (true, true) | (false, false) => {} - (false, true) => return Ordering::Less, - (true, false) => return Ordering::Greater, - } - } - let lhs = i1.name.unwrap_or(kw::Empty).as_str(); - let rhs = i2.name.unwrap_or(kw::Empty).as_str(); - compare_names(&lhs, &rhs) - } - - if cx.shared.sort_modules_alphabetically { - indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2, cx.tcx())); - } - // This call is to remove re-export duplicates in cases such as: - // - // ``` - // crate mod foo { - // crate mod bar { - // crate trait Double { fn foo(); } - // } - // } - // - // crate use foo::bar::*; - // crate use foo::*; - // ``` - // - // `Double` will appear twice in the generated docs. - // - // FIXME: This code is quite ugly and could be improved. Small issue: DefId - // can be identical even if the elements are different (mostly in imports). - // So in case this is an import, we keep everything by adding a "unique id" - // (which is the position in the vector). - indices.dedup_by_key(|i| { - ( - items[*i].def_id, - if items[*i].name.as_ref().is_some() { Some(full_path(cx, &items[*i])) } else { None }, - items[*i].type_(), - if items[*i].is_import() { *i } else { 0 }, - ) - }); - - debug!("{:?}", indices); - let mut curty = None; - for &idx in &indices { - let myitem = &items[idx]; - if myitem.is_stripped() { - continue; - } - - let myty = Some(myitem.type_()); - if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) { - // Put `extern crate` and `use` re-exports in the same section. - curty = myty; - } else if myty != curty { - if curty.is_some() { - w.write_str(""); - } - curty = myty; - let (short, name) = item_ty_to_strs(&myty.unwrap()); - write!( - w, - "

\ - {name}

\n", - id = cx.derive_id(short.to_owned()), - name = name - ); - } - - match *myitem.kind { - clean::ExternCrateItem(ref name, ref src) => { - use crate::html::format::anchor; - - match *src { - Some(ref src) => write!( - w, - ""); - } - - clean::ImportItem(ref import) => { - write!( - w, - "", - myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), - import.print(cx.cache()) - ); - } - - _ => { - if myitem.name.is_none() { - continue; - } - - let unsafety_flag = match *myitem.kind { - clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func) - if func.header.unsafety == hir::Unsafety::Unsafe => - { - "" - } - _ => "", - }; - - let stab = myitem.stability_class(cx.tcx()); - let add = if stab.is_some() { " " } else { "" }; - - let doc_value = myitem.doc_value().unwrap_or_default(); - write!( - w, - "\ - \ - \ - ", - name = *myitem.name.as_ref().unwrap(), - stab_tags = extra_info_tags(myitem, item, cx.tcx()), - docs = MarkdownSummaryLine(&doc_value, &myitem.links(&cx.cache)).into_string(), - class = myitem.type_(), - add = add, - stab = stab.unwrap_or_else(String::new), - unsafety_flag = unsafety_flag, - href = item_path(myitem.type_(), &myitem.name.unwrap().as_str()), - title = [full_path(cx, myitem), myitem.type_().to_string()] - .iter() - .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None }) - .collect::>() - .join(" "), - ); - } - } - } - - if curty.is_some() { - w.write_str("
{}extern crate {} as {};", - myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), - anchor(myitem.def_id, &*src.as_str(), cx.cache()), - name - ), - None => write!( - w, - "
{}extern crate {};", - myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), - anchor(myitem.def_id, &*name.as_str(), cx.cache()) - ), - } - w.write_str("
{}{}
{name}{unsafety_flag}{stab_tags}{docs}
"); - } -} - -/// Render the stability, deprecation and portability tags that are displayed in the item's summary -/// at the module level. -fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> String { - let mut tags = String::new(); - - fn tag_html(class: &str, title: &str, contents: &str) -> String { - format!(r#"{}"#, class, Escape(title), contents) - } - - // The trailing space after each tag is to space it properly against the rest of the docs. - if let Some(depr) = &item.deprecation(tcx) { - let mut message = "Deprecated"; - if !stability::deprecation_in_effect( - depr.is_since_rustc_version, - depr.since.map(|s| s.as_str()).as_deref(), - ) { - message = "Deprecation planned"; - } - tags += &tag_html("deprecated", "", message); - } - - // The "rustc_private" crates are permanently unstable so it makes no sense - // to render "unstable" everywhere. - if item - .stability(tcx) - .as_ref() - .map(|s| s.level.is_unstable() && s.feature != sym::rustc_private) - == Some(true) - { - tags += &tag_html("unstable", "", "Experimental"); - } - - let cfg = match (&item.attrs.cfg, parent.attrs.cfg.as_ref()) { - (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), - (cfg, _) => cfg.as_deref().cloned(), - }; - - debug!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg); - if let Some(ref cfg) = cfg { - tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html()); - } - - tags -} - fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option { let cfg = match (&item.attrs.cfg, parent.and_then(|p| p.attrs.cfg.as_ref())) { (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), @@ -1836,124 +1389,6 @@ fn short_item_info( extra_info } -fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) { - w.write_str("
");
-    render_attributes(w, it, false);
-
-    write!(
-        w,
-        "{vis}const {name}: {typ}",
-        vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
-        name = it.name.as_ref().unwrap(),
-        typ = c.type_.print(cx.cache()),
-    );
-
-    if c.value.is_some() || c.is_literal {
-        write!(w, " = {expr};", expr = Escape(&c.expr));
-    } else {
-        w.write_str(";");
-    }
-
-    if let Some(value) = &c.value {
-        if !c.is_literal {
-            let value_lowercase = value.to_lowercase();
-            let expr_lowercase = c.expr.to_lowercase();
-
-            if value_lowercase != expr_lowercase
-                && value_lowercase.trim_end_matches("i32") != expr_lowercase
-            {
-                write!(w, " // {value}", value = Escape(value));
-            }
-        }
-    }
-
-    w.write_str("
"); - document(w, cx, it, None) -} - -fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) { - w.write_str("
");
-    render_attributes(w, it, false);
-    write!(
-        w,
-        "{vis}static {mutability}{name}: {typ}
", - vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), - mutability = s.mutability.print_with_space(), - name = it.name.as_ref().unwrap(), - typ = s.type_.print(cx.cache()) - ); - document(w, cx, it, None) -} - -fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) { - let header_len = format!( - "{}{}{}{}{:#}fn {}{:#}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), - f.header.constness.print_with_space(), - f.header.asyncness.print_with_space(), - f.header.unsafety.print_with_space(), - print_abi_with_space(f.header.abi), - it.name.as_ref().unwrap(), - f.generics.print(cx.cache()) - ) - .len(); - w.write_str("
");
-    render_attributes(w, it, false);
-    write!(
-        w,
-        "{vis}{constness}{asyncness}{unsafety}{abi}fn \
-         {name}{generics}{decl}{spotlight}{where_clause}
", - vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), - constness = f.header.constness.print_with_space(), - asyncness = f.header.asyncness.print_with_space(), - unsafety = f.header.unsafety.print_with_space(), - abi = print_abi_with_space(f.header.abi), - name = it.name.as_ref().unwrap(), - generics = f.generics.print(cx.cache()), - where_clause = - WhereClause { gens: &f.generics, indent: 0, end_newline: true }.print(cx.cache()), - decl = Function { decl: &f.decl, header_len, indent: 0, asyncness: f.header.asyncness } - .print(cx.cache()), - spotlight = spotlight_decl(&f.decl, cx.cache()), - ); - document(w, cx, it, None) -} - -fn render_implementor( - cx: &Context<'_>, - implementor: &Impl, - trait_: &clean::Item, - w: &mut Buffer, - implementor_dups: &FxHashMap, - aliases: &[String], -) { - // If there's already another implementor that has the same abbridged name, use the - // full path, for example in `std::iter::ExactSizeIterator` - let use_absolute = match implementor.inner_impl().for_ { - clean::ResolvedPath { ref path, is_generic: false, .. } - | clean::BorrowedRef { - type_: box clean::ResolvedPath { ref path, is_generic: false, .. }, - .. - } => implementor_dups[&path.last()].1, - _ => false, - }; - render_impl( - w, - cx, - implementor, - trait_, - AssocItemLink::Anchor(None), - RenderMode::Normal, - trait_.stable_since(cx.tcx()).as_deref(), - trait_.const_stable_since(cx.tcx()).as_deref(), - false, - Some(use_absolute), - false, - false, - aliases, - ); -} - fn render_impls( cx: &Context<'_>, w: &mut Buffer, @@ -1988,326 +1423,6 @@ fn render_impls( w.write_str(&impls.join("")); } -fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cache: &Cache) -> String { - let mut bounds = String::new(); - if !t_bounds.is_empty() { - if !trait_alias { - bounds.push_str(": "); - } - for (i, p) in t_bounds.iter().enumerate() { - if i > 0 { - bounds.push_str(" + "); - } - bounds.push_str(&p.print(cache).to_string()); - } - } - bounds -} - -fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cache: &Cache) -> Ordering { - let lhs = format!("{}", lhs.inner_impl().print(cache, false)); - let rhs = format!("{}", rhs.inner_impl().print(cache, false)); - - // lhs and rhs are formatted as HTML, which may be unnecessary - compare_names(&lhs, &rhs) -} - -fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) { - let bounds = bounds(&t.bounds, false, cx.cache()); - let types = t.items.iter().filter(|m| m.is_associated_type()).collect::>(); - let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::>(); - let required = t.items.iter().filter(|m| m.is_ty_method()).collect::>(); - let provided = t.items.iter().filter(|m| m.is_method()).collect::>(); - - // Output the trait definition - wrap_into_docblock(w, |w| { - w.write_str("
");
-        render_attributes(w, it, true);
-        write!(
-            w,
-            "{}{}{}trait {}{}{}",
-            it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
-            t.unsafety.print_with_space(),
-            if t.is_auto { "auto " } else { "" },
-            it.name.as_ref().unwrap(),
-            t.generics.print(cx.cache()),
-            bounds
-        );
-
-        if !t.generics.where_predicates.is_empty() {
-            let where_ = WhereClause { gens: &t.generics, indent: 0, end_newline: true };
-            write!(w, "{}", where_.print(cx.cache()));
-        } else {
-            w.write_str(" ");
-        }
-
-        if t.items.is_empty() {
-            w.write_str("{ }");
-        } else {
-            // FIXME: we should be using a derived_id for the Anchors here
-            w.write_str("{\n");
-            for t in &types {
-                render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
-                w.write_str(";\n");
-            }
-            if !types.is_empty() && !consts.is_empty() {
-                w.write_str("\n");
-            }
-            for t in &consts {
-                render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
-                w.write_str(";\n");
-            }
-            if !consts.is_empty() && !required.is_empty() {
-                w.write_str("\n");
-            }
-            for (pos, m) in required.iter().enumerate() {
-                render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
-                w.write_str(";\n");
-
-                if pos < required.len() - 1 {
-                    w.write_str("
"); - } - } - if !required.is_empty() && !provided.is_empty() { - w.write_str("\n"); - } - for (pos, m) in provided.iter().enumerate() { - render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx); - match *m.kind { - clean::MethodItem(ref inner, _) - if !inner.generics.where_predicates.is_empty() => - { - w.write_str(",\n { ... }\n"); - } - _ => { - w.write_str(" { ... }\n"); - } - } - if pos < provided.len() - 1 { - w.write_str("
"); - } - } - w.write_str("}"); - } - w.write_str("
") - }); - - // Trait documentation - document(w, cx, it, None); - - fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { - write!( - w, - "

\ - {1}\ -

{2}", - id, title, extra_content - ) - } - - fn write_loading_content(w: &mut Buffer, extra_content: &str) { - write!(w, "{}Loading content...", extra_content) - } - - fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) { - let name = m.name.as_ref().unwrap(); - info!("Documenting {} on {:?}", name, t.name); - let item_type = m.type_(); - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "

", id = id,); - render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx); - w.write_str(""); - render_stability_since(w, m, t, cx.tcx()); - write_srclink(cx, m, w); - w.write_str("

"); - document(w, cx, m, Some(t)); - } - - if !types.is_empty() { - write_small_section_header( - w, - "associated-types", - "Associated Types", - "
", - ); - for t in types { - trait_item(w, cx, t, it); - } - write_loading_content(w, "
"); - } - - if !consts.is_empty() { - write_small_section_header( - w, - "associated-const", - "Associated Constants", - "
", - ); - for t in consts { - trait_item(w, cx, t, it); - } - write_loading_content(w, "
"); - } - - // Output the documentation for each function individually - if !required.is_empty() { - write_small_section_header( - w, - "required-methods", - "Required methods", - "
", - ); - for m in required { - trait_item(w, cx, m, it); - } - write_loading_content(w, "
"); - } - if !provided.is_empty() { - write_small_section_header( - w, - "provided-methods", - "Provided methods", - "
", - ); - for m in provided { - trait_item(w, cx, m, it); - } - write_loading_content(w, "
"); - } - - // If there are methods directly on this trait object, render them here. - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All); - - if let Some(implementors) = cx.cache.implementors.get(&it.def_id) { - // The DefId is for the first Type found with that name. The bool is - // if any Types with the same name but different DefId have been found. - let mut implementor_dups: FxHashMap = FxHashMap::default(); - for implementor in implementors { - match implementor.inner_impl().for_ { - clean::ResolvedPath { ref path, did, is_generic: false, .. } - | clean::BorrowedRef { - type_: box clean::ResolvedPath { ref path, did, is_generic: false, .. }, - .. - } => { - let &mut (prev_did, ref mut has_duplicates) = - implementor_dups.entry(path.last()).or_insert((did, false)); - if prev_did != did { - *has_duplicates = true; - } - } - _ => {} - } - } - - let (local, foreign) = implementors.iter().partition::, _>(|i| { - i.inner_impl() - .for_ - .def_id_full(cx.cache()) - .map_or(true, |d| cx.cache.paths.contains_key(&d)) - }); - - let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = - local.iter().partition(|i| i.inner_impl().synthetic); - - synthetic.sort_by(|a, b| compare_impl(a, b, cx.cache())); - concrete.sort_by(|a, b| compare_impl(a, b, cx.cache())); - - if !foreign.is_empty() { - write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", ""); - - for implementor in foreign { - let assoc_link = AssocItemLink::GotoSource( - implementor.impl_item.def_id, - &implementor.inner_impl().provided_trait_methods, - ); - render_impl( - w, - cx, - &implementor, - it, - assoc_link, - RenderMode::Normal, - implementor.impl_item.stable_since(cx.tcx()).as_deref(), - implementor.impl_item.const_stable_since(cx.tcx()).as_deref(), - false, - None, - true, - false, - &[], - ); - } - write_loading_content(w, ""); - } - - write_small_section_header( - w, - "implementors", - "Implementors", - "
", - ); - for implementor in concrete { - render_implementor(cx, implementor, it, w, &implementor_dups, &[]); - } - write_loading_content(w, "
"); - - if t.is_auto { - write_small_section_header( - w, - "synthetic-implementors", - "Auto implementors", - "
", - ); - for implementor in synthetic { - render_implementor( - cx, - implementor, - it, - w, - &implementor_dups, - &collect_paths_for_type(implementor.inner_impl().for_.clone(), &cx.cache), - ); - } - write_loading_content(w, "
"); - } - } else { - // even without any implementations to write in, we still want the heading and list, so the - // implementors javascript file pulled in below has somewhere to write the impls into - write_small_section_header( - w, - "implementors", - "Implementors", - "
", - ); - write_loading_content(w, "
"); - - if t.is_auto { - write_small_section_header( - w, - "synthetic-implementors", - "Auto implementors", - "
", - ); - write_loading_content(w, "
"); - } - } - - write!( - w, - "", - root_path = vec![".."; cx.current.len()].join("/"), - path = if it.def_id.is_local() { - cx.current.join("/") - } else { - let (ref path, _) = cx.cache.external_paths[&it.def_id]; - path[..path.len() - 1].join("/") - }, - ty = it.type_(), - name = *it.name.as_ref().unwrap() - ); -} - fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cache: &Cache) -> String { use crate::formats::item_type::ItemType::*; @@ -2400,21 +1515,6 @@ fn render_stability_since_raw( } } -fn render_stability_since( - w: &mut Buffer, - item: &clean::Item, - containing_item: &clean::Item, - tcx: TyCtxt<'_>, -) { - render_stability_since_raw( - w, - item.stable_since(tcx).as_deref(), - item.const_stable_since(tcx).as_deref(), - containing_item.stable_since(tcx).as_deref(), - containing_item.const_stable_since(tcx).as_deref(), - ) -} - fn render_assoc_item( w: &mut Buffer, item: &clean::Item, @@ -2518,227 +1618,6 @@ fn render_assoc_item( } } -fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) { - wrap_into_docblock(w, |w| { - w.write_str("
");
-        render_attributes(w, it, true);
-        render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
-        w.write_str("
") - }); - - document(w, cx, it, None); - let mut fields = s - .fields - .iter() - .filter_map(|f| match *f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable(); - if let CtorKind::Fictive = s.struct_type { - if fields.peek().is_some() { - write!( - w, - "

- Fields{}

", - document_non_exhaustive_header(it) - ); - document_non_exhaustive(w, it); - for (field, ty) in fields { - let id = cx.derive_id(format!( - "{}.{}", - ItemType::StructField, - field.name.as_ref().unwrap() - )); - write!( - w, - "\ - \ - {name}: {ty}\ - ", - item_type = ItemType::StructField, - id = id, - name = field.name.as_ref().unwrap(), - ty = ty.print(cx.cache()) - ); - document(w, cx, field, Some(it)); - } - } - } - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) { - wrap_into_docblock(w, |w| { - w.write_str("
");
-        render_attributes(w, it, true);
-        render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
-        w.write_str("
") - }); - - document(w, cx, it, None); - let mut fields = s - .fields - .iter() - .filter_map(|f| match *f.kind { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable(); - if fields.peek().is_some() { - write!( - w, - "

- Fields

" - ); - for (field, ty) in fields { - let name = field.name.as_ref().expect("union field name"); - let id = format!("{}.{}", ItemType::StructField, name); - write!( - w, - "\ - \ - {name}: {ty}\ - ", - id = id, - name = name, - shortty = ItemType::StructField, - ty = ty.print(cx.cache()) - ); - if let Some(stability_class) = field.stability_class(cx.tcx()) { - write!(w, "", stab = stability_class); - } - document(w, cx, field, Some(it)); - } - } - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) { - wrap_into_docblock(w, |w| { - w.write_str("
");
-        render_attributes(w, it, true);
-        write!(
-            w,
-            "{}enum {}{}{}",
-            it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
-            it.name.as_ref().unwrap(),
-            e.generics.print(cx.cache()),
-            WhereClause { gens: &e.generics, indent: 0, end_newline: true }.print(cx.cache())
-        );
-        if e.variants.is_empty() && !e.variants_stripped {
-            w.write_str(" {}");
-        } else {
-            w.write_str(" {\n");
-            for v in &e.variants {
-                w.write_str("    ");
-                let name = v.name.as_ref().unwrap();
-                match *v.kind {
-                    clean::VariantItem(ref var) => match var {
-                        clean::Variant::CLike => write!(w, "{}", name),
-                        clean::Variant::Tuple(ref tys) => {
-                            write!(w, "{}(", name);
-                            for (i, ty) in tys.iter().enumerate() {
-                                if i > 0 {
-                                    w.write_str(", ")
-                                }
-                                write!(w, "{}", ty.print(cx.cache()));
-                            }
-                            w.write_str(")");
-                        }
-                        clean::Variant::Struct(ref s) => {
-                            render_struct(w, v, None, s.struct_type, &s.fields, "    ", false, cx);
-                        }
-                    },
-                    _ => unreachable!(),
-                }
-                w.write_str(",\n");
-            }
-
-            if e.variants_stripped {
-                w.write_str("    // some variants omitted\n");
-            }
-            w.write_str("}");
-        }
-        w.write_str("
") - }); - - document(w, cx, it, None); - if !e.variants.is_empty() { - write!( - w, - "

- Variants{}

\n", - document_non_exhaustive_header(it) - ); - document_non_exhaustive(w, it); - for variant in &e.variants { - let id = - cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap())); - write!( - w, - "
\ - \ - {name}", - id = id, - name = variant.name.as_ref().unwrap() - ); - if let clean::VariantItem(clean::Variant::Tuple(ref tys)) = *variant.kind { - w.write_str("("); - for (i, ty) in tys.iter().enumerate() { - if i > 0 { - w.write_str(", "); - } - write!(w, "{}", ty.print(cx.cache())); - } - w.write_str(")"); - } - w.write_str("
"); - document(w, cx, variant, Some(it)); - document_non_exhaustive(w, variant); - - use crate::clean::Variant; - if let clean::VariantItem(Variant::Struct(ref s)) = *variant.kind { - let variant_id = cx.derive_id(format!( - "{}.{}.fields", - ItemType::Variant, - variant.name.as_ref().unwrap() - )); - write!(w, "
", id = variant_id); - write!( - w, - "

Fields of {name}

", - name = variant.name.as_ref().unwrap() - ); - for field in &s.fields { - use crate::clean::StructFieldItem; - if let StructFieldItem(ref ty) = *field.kind { - let id = cx.derive_id(format!( - "variant.{}.field.{}", - variant.name.as_ref().unwrap(), - field.name.as_ref().unwrap() - )); - write!( - w, - "\ - \ - {f}: {t}\ - ", - id = id, - f = field.name.as_ref().unwrap(), - t = ty.print(cx.cache()) - ); - document(w, cx, field, Some(variant)); - } - } - w.write_str("
"); - } - render_stability_since(w, variant, it, cx.tcx()); - } - } - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - const ALLOWED_ATTRIBUTES: &[Symbol] = &[ sym::export_name, sym::lang, @@ -2781,147 +1660,6 @@ fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) { } } -fn render_struct( - w: &mut Buffer, - it: &clean::Item, - g: Option<&clean::Generics>, - ty: CtorKind, - fields: &[clean::Item], - tab: &str, - structhead: bool, - cx: &Context<'_>, -) { - write!( - w, - "{}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), - if structhead { "struct " } else { "" }, - it.name.as_ref().unwrap() - ); - if let Some(g) = g { - write!(w, "{}", g.print(cx.cache())) - } - match ty { - CtorKind::Fictive => { - if let Some(g) = g { - write!( - w, - "{}", - WhereClause { gens: g, indent: 0, end_newline: true }.print(cx.cache()) - ) - } - let mut has_visible_fields = false; - w.write_str(" {"); - for field in fields { - if let clean::StructFieldItem(ref ty) = *field.kind { - write!( - w, - "\n{} {}{}: {},", - tab, - field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), - field.name.as_ref().unwrap(), - ty.print(cx.cache()) - ); - has_visible_fields = true; - } - } - - if has_visible_fields { - if it.has_stripped_fields().unwrap() { - write!(w, "\n{} // some fields omitted", tab); - } - write!(w, "\n{}", tab); - } else if it.has_stripped_fields().unwrap() { - // If there are no visible fields we can just display - // `{ /* fields omitted */ }` to save space. - write!(w, " /* fields omitted */ "); - } - w.write_str("}"); - } - CtorKind::Fn => { - w.write_str("("); - for (i, field) in fields.iter().enumerate() { - if i > 0 { - w.write_str(", "); - } - match *field.kind { - clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), - clean::StructFieldItem(ref ty) => { - write!( - w, - "{}{}", - field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), - ty.print(cx.cache()) - ) - } - _ => unreachable!(), - } - } - w.write_str(")"); - if let Some(g) = g { - write!( - w, - "{}", - WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache()) - ) - } - w.write_str(";"); - } - CtorKind::Const => { - // Needed for PhantomData. - if let Some(g) = g { - write!( - w, - "{}", - WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache()) - ) - } - w.write_str(";"); - } - } -} - -fn render_union( - w: &mut Buffer, - it: &clean::Item, - g: Option<&clean::Generics>, - fields: &[clean::Item], - tab: &str, - structhead: bool, - cx: &Context<'_>, -) { - write!( - w, - "{}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), - if structhead { "union " } else { "" }, - it.name.as_ref().unwrap() - ); - if let Some(g) = g { - write!(w, "{}", g.print(cx.cache())); - write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }.print(cx.cache())); - } - - write!(w, " {{\n{}", tab); - for field in fields { - if let clean::StructFieldItem(ref ty) = *field.kind { - write!( - w, - " {}{}: {},\n{}", - field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), - field.name.as_ref().unwrap(), - ty.print(cx.cache()), - tab - ); - } - } - - if it.has_stripped_fields().unwrap() { - write!(w, " // some fields omitted\n{}", tab); - } - w.write_str("}"); -} - #[derive(Copy, Clone)] enum AssocItemLink<'a> { Anchor(Option<&'a str>), @@ -3492,86 +2230,6 @@ fn render_impl( w.write_str(""); } -fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { - w.write_str("
");
-    render_attributes(w, it, false);
-    write!(
-        w,
-        "type {}{}{where_clause} = impl {bounds};
", - it.name.as_ref().unwrap(), - t.generics.print(cx.cache()), - where_clause = - WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()), - bounds = bounds(&t.bounds, false, cx.cache()) - ); - - document(w, cx, it, None); - - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { - w.write_str("
");
-    render_attributes(w, it, false);
-    write!(
-        w,
-        "trait {}{}{} = {};
", - it.name.as_ref().unwrap(), - t.generics.print(cx.cache()), - WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()), - bounds(&t.bounds, true, cx.cache()) - ); - - document(w, cx, it, None); - - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { - w.write_str("
");
-    render_attributes(w, it, false);
-    write!(
-        w,
-        "type {}{}{where_clause} = {type_};
", - it.name.as_ref().unwrap(), - t.generics.print(cx.cache()), - where_clause = - WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()), - type_ = t.type_.print(cx.cache()) - ); - - document(w, cx, it, None); - - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { - w.write_str("
extern {\n");
-    render_attributes(w, it, false);
-    write!(
-        w,
-        "    {}type {};\n}}
", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), - it.name.as_ref().unwrap(), - ); - - document(w, cx, it, None); - - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { let parentlen = cx.current.len() - if it.is_mod() { 1 } else { 0 }; @@ -4264,59 +2922,6 @@ fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { } } -fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) { - wrap_into_docblock(w, |w| { - highlight::render_with_highlighting( - &t.source, - w, - Some("macro"), - None, - None, - it.source.span().edition(), - ); - }); - document(w, cx, it, None) -} - -fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { - let name = it.name.as_ref().expect("proc-macros always have names"); - match m.kind { - MacroKind::Bang => { - w.push_str("
");
-            write!(w, "{}!() {{ /* proc-macro */ }}", name);
-            w.push_str("
"); - } - MacroKind::Attr => { - w.push_str("
");
-            write!(w, "#[{}]", name);
-            w.push_str("
"); - } - MacroKind::Derive => { - w.push_str("
");
-            write!(w, "#[derive({})]", name);
-            if !m.helpers.is_empty() {
-                w.push_str("\n{\n");
-                w.push_str("    // Attributes available to this derive:\n");
-                for attr in &m.helpers {
-                    writeln!(w, "    #[{}]", attr);
-                }
-                w.push_str("}\n");
-            }
-            w.push_str("
"); - } - } - document(w, cx, it, None) -} - -fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { - document(w, cx, it, None); - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { - document(w, cx, it, None) -} - crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; fn make_item_keywords(it: &clean::Item) -> String { diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs new file mode 100644 index 0000000000000..6d61248f28ef0 --- /dev/null +++ b/src/librustdoc/html/render/print_item.rs @@ -0,0 +1,1420 @@ +use std::cmp::Ordering; + +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_hir::def::CtorKind; +use rustc_hir::def_id::DefId; +use rustc_middle::middle::stability; +use rustc_middle::ty::TyCtxt; +use rustc_span::hygiene::MacroKind; +use rustc_span::symbol::{kw, sym, Symbol}; + +use super::{ + collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_strs, render_assoc_item, + render_assoc_items, render_attributes, render_impl, render_stability_since_raw, spotlight_decl, + write_srclink, AssocItemLink, Context, +}; +use crate::clean::{self, GetDefId}; +use crate::formats::cache::Cache; +use crate::formats::item_type::ItemType; +use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode}; +use crate::html::escape::Escape; +use crate::html::format::{print_abi_with_space, Buffer, Function, PrintWithSpace, WhereClause}; +use crate::html::highlight; +use crate::html::markdown::MarkdownSummaryLine; + +pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { + debug_assert!(!item.is_stripped()); + // Write the breadcrumb trail header for the top + buf.write_str("

"); + let name = match *item.kind { + clean::ModuleItem(ref m) => { + if m.is_crate { + "Crate " + } else { + "Module " + } + } + clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", + clean::TraitItem(..) => "Trait ", + clean::StructItem(..) => "Struct ", + clean::UnionItem(..) => "Union ", + clean::EnumItem(..) => "Enum ", + clean::TypedefItem(..) => "Type Definition ", + clean::MacroItem(..) => "Macro ", + clean::ProcMacroItem(ref mac) => match mac.kind { + MacroKind::Bang => "Macro ", + MacroKind::Attr => "Attribute Macro ", + MacroKind::Derive => "Derive Macro ", + }, + clean::PrimitiveItem(..) => "Primitive Type ", + clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ", + clean::ConstantItem(..) => "Constant ", + clean::ForeignTypeItem => "Foreign Type ", + clean::KeywordItem(..) => "Keyword ", + clean::OpaqueTyItem(..) => "Opaque Type ", + clean::TraitAliasItem(..) => "Trait Alias ", + _ => { + // We don't generate pages for any other type. + unreachable!(); + } + }; + buf.write_str(name); + if !item.is_primitive() && !item.is_keyword() { + let cur = &cx.current; + let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() }; + for (i, component) in cur.iter().enumerate().take(amt) { + write!( + buf, + "{}::", + "../".repeat(cur.len() - i - 1), + component + ); + } + } + write!(buf, "{}", item.type_(), item.name.as_ref().unwrap()); + + buf.write_str(""); // in-band + buf.write_str(""); + render_stability_since_raw( + buf, + item.stable_since(cx.tcx()).as_deref(), + item.const_stable_since(cx.tcx()).as_deref(), + None, + None, + ); + buf.write_str( + "\ + \ + []\ + \ + ", + ); + + // Write `src` tag + // + // When this item is part of a `crate use` in a downstream crate, the + // [src] link in the downstream documentation will actually come back to + // this page, and this link will be auto-clicked. The `id` attribute is + // used to find the link to auto-click. + if cx.shared.include_sources && !item.is_primitive() { + write_srclink(cx, item, buf); + } + + buf.write_str("

"); // out-of-band + + match *item.kind { + clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), + clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { + item_function(buf, cx, item, f) + } + clean::TraitItem(ref t) => item_trait(buf, cx, item, t), + clean::StructItem(ref s) => item_struct(buf, cx, item, s), + clean::UnionItem(ref s) => item_union(buf, cx, item, s), + clean::EnumItem(ref e) => item_enum(buf, cx, item, e), + clean::TypedefItem(ref t, _) => item_typedef(buf, cx, item, t), + clean::MacroItem(ref m) => item_macro(buf, cx, item, m), + clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), + clean::PrimitiveItem(_) => item_primitive(buf, cx, item), + clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i), + clean::ConstantItem(ref c) => item_constant(buf, cx, item, c), + clean::ForeignTypeItem => item_foreign_type(buf, cx, item), + clean::KeywordItem(_) => item_keyword(buf, cx, item), + clean::OpaqueTyItem(ref e) => item_opaque_ty(buf, cx, item, e), + clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta), + _ => { + // We don't generate pages for any other type. + unreachable!(); + } + } +} + +fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) { + document(w, cx, item, None); + + let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>(); + + // the order of item types in the listing + fn reorder(ty: ItemType) -> u8 { + match ty { + ItemType::ExternCrate => 0, + ItemType::Import => 1, + ItemType::Primitive => 2, + ItemType::Module => 3, + ItemType::Macro => 4, + ItemType::Struct => 5, + ItemType::Enum => 6, + ItemType::Constant => 7, + ItemType::Static => 8, + ItemType::Trait => 9, + ItemType::Function => 10, + ItemType::Typedef => 12, + ItemType::Union => 13, + _ => 14 + ty as u8, + } + } + + fn cmp( + i1: &clean::Item, + i2: &clean::Item, + idx1: usize, + idx2: usize, + tcx: TyCtxt<'_>, + ) -> Ordering { + let ty1 = i1.type_(); + let ty2 = i2.type_(); + if ty1 != ty2 { + return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)); + } + let s1 = i1.stability(tcx).as_ref().map(|s| s.level); + let s2 = i2.stability(tcx).as_ref().map(|s| s.level); + if let (Some(a), Some(b)) = (s1, s2) { + match (a.is_stable(), b.is_stable()) { + (true, true) | (false, false) => {} + (false, true) => return Ordering::Less, + (true, false) => return Ordering::Greater, + } + } + let lhs = i1.name.unwrap_or(kw::Empty).as_str(); + let rhs = i2.name.unwrap_or(kw::Empty).as_str(); + compare_names(&lhs, &rhs) + } + + if cx.shared.sort_modules_alphabetically { + indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2, cx.tcx())); + } + // This call is to remove re-export duplicates in cases such as: + // + // ``` + // crate mod foo { + // crate mod bar { + // crate trait Double { fn foo(); } + // } + // } + // + // crate use foo::bar::*; + // crate use foo::*; + // ``` + // + // `Double` will appear twice in the generated docs. + // + // FIXME: This code is quite ugly and could be improved. Small issue: DefId + // can be identical even if the elements are different (mostly in imports). + // So in case this is an import, we keep everything by adding a "unique id" + // (which is the position in the vector). + indices.dedup_by_key(|i| { + ( + items[*i].def_id, + if items[*i].name.as_ref().is_some() { Some(full_path(cx, &items[*i])) } else { None }, + items[*i].type_(), + if items[*i].is_import() { *i } else { 0 }, + ) + }); + + debug!("{:?}", indices); + let mut curty = None; + for &idx in &indices { + let myitem = &items[idx]; + if myitem.is_stripped() { + continue; + } + + let myty = Some(myitem.type_()); + if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) { + // Put `extern crate` and `use` re-exports in the same section. + curty = myty; + } else if myty != curty { + if curty.is_some() { + w.write_str(""); + } + curty = myty; + let (short, name) = item_ty_to_strs(&myty.unwrap()); + write!( + w, + "

\ + {name}

\n", + id = cx.derive_id(short.to_owned()), + name = name + ); + } + + match *myitem.kind { + clean::ExternCrateItem(ref name, ref src) => { + use crate::html::format::anchor; + + match *src { + Some(ref src) => write!( + w, + ""); + } + + clean::ImportItem(ref import) => { + write!( + w, + "", + myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), + import.print(cx.cache()) + ); + } + + _ => { + if myitem.name.is_none() { + continue; + } + + let unsafety_flag = match *myitem.kind { + clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func) + if func.header.unsafety == hir::Unsafety::Unsafe => + { + "" + } + _ => "", + }; + + let stab = myitem.stability_class(cx.tcx()); + let add = if stab.is_some() { " " } else { "" }; + + let doc_value = myitem.doc_value().unwrap_or_default(); + write!( + w, + "\ + \ + \ + ", + name = *myitem.name.as_ref().unwrap(), + stab_tags = extra_info_tags(myitem, item, cx.tcx()), + docs = MarkdownSummaryLine(&doc_value, &myitem.links(&cx.cache)).into_string(), + class = myitem.type_(), + add = add, + stab = stab.unwrap_or_else(String::new), + unsafety_flag = unsafety_flag, + href = item_path(myitem.type_(), &myitem.name.unwrap().as_str()), + title = [full_path(cx, myitem), myitem.type_().to_string()] + .iter() + .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None }) + .collect::>() + .join(" "), + ); + } + } + } + + if curty.is_some() { + w.write_str("
{}extern crate {} as {};", + myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), + anchor(myitem.def_id, &*src.as_str(), cx.cache()), + name + ), + None => write!( + w, + "
{}extern crate {};", + myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), + anchor(myitem.def_id, &*name.as_str(), cx.cache()) + ), + } + w.write_str("
{}{}
{name}{unsafety_flag}{stab_tags}{docs}
"); + } +} + +/// Render the stability, deprecation and portability tags that are displayed in the item's summary +/// at the module level. +fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> String { + let mut tags = String::new(); + + fn tag_html(class: &str, title: &str, contents: &str) -> String { + format!(r#"{}"#, class, Escape(title), contents) + } + + // The trailing space after each tag is to space it properly against the rest of the docs. + if let Some(depr) = &item.deprecation(tcx) { + let mut message = "Deprecated"; + if !stability::deprecation_in_effect( + depr.is_since_rustc_version, + depr.since.map(|s| s.as_str()).as_deref(), + ) { + message = "Deprecation planned"; + } + tags += &tag_html("deprecated", "", message); + } + + // The "rustc_private" crates are permanently unstable so it makes no sense + // to render "unstable" everywhere. + if item + .stability(tcx) + .as_ref() + .map(|s| s.level.is_unstable() && s.feature != sym::rustc_private) + == Some(true) + { + tags += &tag_html("unstable", "", "Experimental"); + } + + let cfg = match (&item.attrs.cfg, parent.attrs.cfg.as_ref()) { + (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), + (cfg, _) => cfg.as_deref().cloned(), + }; + + debug!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg); + if let Some(ref cfg) = cfg { + tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html()); + } + + tags +} + +fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) { + let header_len = format!( + "{}{}{}{}{:#}fn {}{:#}", + it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + f.header.constness.print_with_space(), + f.header.asyncness.print_with_space(), + f.header.unsafety.print_with_space(), + print_abi_with_space(f.header.abi), + it.name.as_ref().unwrap(), + f.generics.print(cx.cache()) + ) + .len(); + w.write_str("
");
+    render_attributes(w, it, false);
+    write!(
+        w,
+        "{vis}{constness}{asyncness}{unsafety}{abi}fn \
+         {name}{generics}{decl}{spotlight}{where_clause}
", + vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + constness = f.header.constness.print_with_space(), + asyncness = f.header.asyncness.print_with_space(), + unsafety = f.header.unsafety.print_with_space(), + abi = print_abi_with_space(f.header.abi), + name = it.name.as_ref().unwrap(), + generics = f.generics.print(cx.cache()), + where_clause = + WhereClause { gens: &f.generics, indent: 0, end_newline: true }.print(cx.cache()), + decl = Function { decl: &f.decl, header_len, indent: 0, asyncness: f.header.asyncness } + .print(cx.cache()), + spotlight = spotlight_decl(&f.decl, cx.cache()), + ); + document(w, cx, it, None) +} + +fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) { + let bounds = bounds(&t.bounds, false, cx.cache()); + let types = t.items.iter().filter(|m| m.is_associated_type()).collect::>(); + let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::>(); + let required = t.items.iter().filter(|m| m.is_ty_method()).collect::>(); + let provided = t.items.iter().filter(|m| m.is_method()).collect::>(); + + // Output the trait definition + wrap_into_docblock(w, |w| { + w.write_str("
");
+        render_attributes(w, it, true);
+        write!(
+            w,
+            "{}{}{}trait {}{}{}",
+            it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+            t.unsafety.print_with_space(),
+            if t.is_auto { "auto " } else { "" },
+            it.name.as_ref().unwrap(),
+            t.generics.print(cx.cache()),
+            bounds
+        );
+
+        if !t.generics.where_predicates.is_empty() {
+            let where_ = WhereClause { gens: &t.generics, indent: 0, end_newline: true };
+            write!(w, "{}", where_.print(cx.cache()));
+        } else {
+            w.write_str(" ");
+        }
+
+        if t.items.is_empty() {
+            w.write_str("{ }");
+        } else {
+            // FIXME: we should be using a derived_id for the Anchors here
+            w.write_str("{\n");
+            for t in &types {
+                render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
+                w.write_str(";\n");
+            }
+            if !types.is_empty() && !consts.is_empty() {
+                w.write_str("\n");
+            }
+            for t in &consts {
+                render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
+                w.write_str(";\n");
+            }
+            if !consts.is_empty() && !required.is_empty() {
+                w.write_str("\n");
+            }
+            for (pos, m) in required.iter().enumerate() {
+                render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
+                w.write_str(";\n");
+
+                if pos < required.len() - 1 {
+                    w.write_str("
"); + } + } + if !required.is_empty() && !provided.is_empty() { + w.write_str("\n"); + } + for (pos, m) in provided.iter().enumerate() { + render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx); + match *m.kind { + clean::MethodItem(ref inner, _) + if !inner.generics.where_predicates.is_empty() => + { + w.write_str(",\n { ... }\n"); + } + _ => { + w.write_str(" { ... }\n"); + } + } + if pos < provided.len() - 1 { + w.write_str("
"); + } + } + w.write_str("}"); + } + w.write_str("
") + }); + + // Trait documentation + document(w, cx, it, None); + + fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { + write!( + w, + "

\ + {1}\ +

{2}", + id, title, extra_content + ) + } + + fn write_loading_content(w: &mut Buffer, extra_content: &str) { + write!(w, "{}Loading content...", extra_content) + } + + fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) { + let name = m.name.as_ref().unwrap(); + info!("Documenting {} on {:?}", name, t.name); + let item_type = m.type_(); + let id = cx.derive_id(format!("{}.{}", item_type, name)); + write!(w, "

", id = id,); + render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx); + w.write_str(""); + render_stability_since(w, m, t, cx.tcx()); + write_srclink(cx, m, w); + w.write_str("

"); + document(w, cx, m, Some(t)); + } + + if !types.is_empty() { + write_small_section_header( + w, + "associated-types", + "Associated Types", + "
", + ); + for t in types { + trait_item(w, cx, t, it); + } + write_loading_content(w, "
"); + } + + if !consts.is_empty() { + write_small_section_header( + w, + "associated-const", + "Associated Constants", + "
", + ); + for t in consts { + trait_item(w, cx, t, it); + } + write_loading_content(w, "
"); + } + + // Output the documentation for each function individually + if !required.is_empty() { + write_small_section_header( + w, + "required-methods", + "Required methods", + "
", + ); + for m in required { + trait_item(w, cx, m, it); + } + write_loading_content(w, "
"); + } + if !provided.is_empty() { + write_small_section_header( + w, + "provided-methods", + "Provided methods", + "
", + ); + for m in provided { + trait_item(w, cx, m, it); + } + write_loading_content(w, "
"); + } + + // If there are methods directly on this trait object, render them here. + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All); + + if let Some(implementors) = cx.cache.implementors.get(&it.def_id) { + // The DefId is for the first Type found with that name. The bool is + // if any Types with the same name but different DefId have been found. + let mut implementor_dups: FxHashMap = FxHashMap::default(); + for implementor in implementors { + match implementor.inner_impl().for_ { + clean::ResolvedPath { ref path, did, is_generic: false, .. } + | clean::BorrowedRef { + type_: box clean::ResolvedPath { ref path, did, is_generic: false, .. }, + .. + } => { + let &mut (prev_did, ref mut has_duplicates) = + implementor_dups.entry(path.last()).or_insert((did, false)); + if prev_did != did { + *has_duplicates = true; + } + } + _ => {} + } + } + + let (local, foreign) = implementors.iter().partition::, _>(|i| { + i.inner_impl() + .for_ + .def_id_full(cx.cache()) + .map_or(true, |d| cx.cache.paths.contains_key(&d)) + }); + + let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = + local.iter().partition(|i| i.inner_impl().synthetic); + + synthetic.sort_by(|a, b| compare_impl(a, b, cx.cache())); + concrete.sort_by(|a, b| compare_impl(a, b, cx.cache())); + + if !foreign.is_empty() { + write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", ""); + + for implementor in foreign { + let assoc_link = AssocItemLink::GotoSource( + implementor.impl_item.def_id, + &implementor.inner_impl().provided_trait_methods, + ); + render_impl( + w, + cx, + &implementor, + it, + assoc_link, + RenderMode::Normal, + implementor.impl_item.stable_since(cx.tcx()).as_deref(), + implementor.impl_item.const_stable_since(cx.tcx()).as_deref(), + false, + None, + true, + false, + &[], + ); + } + write_loading_content(w, ""); + } + + write_small_section_header( + w, + "implementors", + "Implementors", + "
", + ); + for implementor in concrete { + render_implementor(cx, implementor, it, w, &implementor_dups, &[]); + } + write_loading_content(w, "
"); + + if t.is_auto { + write_small_section_header( + w, + "synthetic-implementors", + "Auto implementors", + "
", + ); + for implementor in synthetic { + render_implementor( + cx, + implementor, + it, + w, + &implementor_dups, + &collect_paths_for_type(implementor.inner_impl().for_.clone(), &cx.cache), + ); + } + write_loading_content(w, "
"); + } + } else { + // even without any implementations to write in, we still want the heading and list, so the + // implementors javascript file pulled in below has somewhere to write the impls into + write_small_section_header( + w, + "implementors", + "Implementors", + "
", + ); + write_loading_content(w, "
"); + + if t.is_auto { + write_small_section_header( + w, + "synthetic-implementors", + "Auto implementors", + "
", + ); + write_loading_content(w, "
"); + } + } + + write!( + w, + "", + root_path = vec![".."; cx.current.len()].join("/"), + path = if it.def_id.is_local() { + cx.current.join("/") + } else { + let (ref path, _) = cx.cache.external_paths[&it.def_id]; + path[..path.len() - 1].join("/") + }, + ty = it.type_(), + name = *it.name.as_ref().unwrap() + ); +} + +fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { + w.write_str("
");
+    render_attributes(w, it, false);
+    write!(
+        w,
+        "trait {}{}{} = {};
", + it.name.as_ref().unwrap(), + t.generics.print(cx.cache()), + WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()), + bounds(&t.bounds, true, cx.cache()) + ); + + document(w, cx, it, None); + + // Render any items associated directly to this alias, as otherwise they + // won't be visible anywhere in the docs. It would be nice to also show + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) +} + +fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { + w.write_str("
");
+    render_attributes(w, it, false);
+    write!(
+        w,
+        "type {}{}{where_clause} = impl {bounds};
", + it.name.as_ref().unwrap(), + t.generics.print(cx.cache()), + where_clause = + WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()), + bounds = bounds(&t.bounds, false, cx.cache()) + ); + + document(w, cx, it, None); + + // Render any items associated directly to this alias, as otherwise they + // won't be visible anywhere in the docs. It would be nice to also show + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) +} + +fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { + w.write_str("
");
+    render_attributes(w, it, false);
+    write!(
+        w,
+        "type {}{}{where_clause} = {type_};
", + it.name.as_ref().unwrap(), + t.generics.print(cx.cache()), + where_clause = + WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()), + type_ = t.type_.print(cx.cache()) + ); + + document(w, cx, it, None); + + // Render any items associated directly to this alias, as otherwise they + // won't be visible anywhere in the docs. It would be nice to also show + // associated items from the aliased type (see discussion in #32077), but + // we need #14072 to make sense of the generics. + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) +} + +fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) { + wrap_into_docblock(w, |w| { + w.write_str("
");
+        render_attributes(w, it, true);
+        render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
+        w.write_str("
") + }); + + document(w, cx, it, None); + let mut fields = s + .fields + .iter() + .filter_map(|f| match *f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + .peekable(); + if fields.peek().is_some() { + write!( + w, + "

+ Fields

" + ); + for (field, ty) in fields { + let name = field.name.as_ref().expect("union field name"); + let id = format!("{}.{}", ItemType::StructField, name); + write!( + w, + "\ + \ + {name}: {ty}\ + ", + id = id, + name = name, + shortty = ItemType::StructField, + ty = ty.print(cx.cache()) + ); + if let Some(stability_class) = field.stability_class(cx.tcx()) { + write!(w, "", stab = stability_class); + } + document(w, cx, field, Some(it)); + } + } + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) +} + +fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) { + wrap_into_docblock(w, |w| { + w.write_str("
");
+        render_attributes(w, it, true);
+        write!(
+            w,
+            "{}enum {}{}{}",
+            it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+            it.name.as_ref().unwrap(),
+            e.generics.print(cx.cache()),
+            WhereClause { gens: &e.generics, indent: 0, end_newline: true }.print(cx.cache())
+        );
+        if e.variants.is_empty() && !e.variants_stripped {
+            w.write_str(" {}");
+        } else {
+            w.write_str(" {\n");
+            for v in &e.variants {
+                w.write_str("    ");
+                let name = v.name.as_ref().unwrap();
+                match *v.kind {
+                    clean::VariantItem(ref var) => match var {
+                        clean::Variant::CLike => write!(w, "{}", name),
+                        clean::Variant::Tuple(ref tys) => {
+                            write!(w, "{}(", name);
+                            for (i, ty) in tys.iter().enumerate() {
+                                if i > 0 {
+                                    w.write_str(", ")
+                                }
+                                write!(w, "{}", ty.print(cx.cache()));
+                            }
+                            w.write_str(")");
+                        }
+                        clean::Variant::Struct(ref s) => {
+                            render_struct(w, v, None, s.struct_type, &s.fields, "    ", false, cx);
+                        }
+                    },
+                    _ => unreachable!(),
+                }
+                w.write_str(",\n");
+            }
+
+            if e.variants_stripped {
+                w.write_str("    // some variants omitted\n");
+            }
+            w.write_str("}");
+        }
+        w.write_str("
") + }); + + document(w, cx, it, None); + if !e.variants.is_empty() { + write!( + w, + "

+ Variants{}

\n", + document_non_exhaustive_header(it) + ); + document_non_exhaustive(w, it); + for variant in &e.variants { + let id = + cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap())); + write!( + w, + "
\ + \ + {name}", + id = id, + name = variant.name.as_ref().unwrap() + ); + if let clean::VariantItem(clean::Variant::Tuple(ref tys)) = *variant.kind { + w.write_str("("); + for (i, ty) in tys.iter().enumerate() { + if i > 0 { + w.write_str(", "); + } + write!(w, "{}", ty.print(cx.cache())); + } + w.write_str(")"); + } + w.write_str("
"); + document(w, cx, variant, Some(it)); + document_non_exhaustive(w, variant); + + use crate::clean::Variant; + if let clean::VariantItem(Variant::Struct(ref s)) = *variant.kind { + let variant_id = cx.derive_id(format!( + "{}.{}.fields", + ItemType::Variant, + variant.name.as_ref().unwrap() + )); + write!(w, "
", id = variant_id); + write!( + w, + "

Fields of {name}

", + name = variant.name.as_ref().unwrap() + ); + for field in &s.fields { + use crate::clean::StructFieldItem; + if let StructFieldItem(ref ty) = *field.kind { + let id = cx.derive_id(format!( + "variant.{}.field.{}", + variant.name.as_ref().unwrap(), + field.name.as_ref().unwrap() + )); + write!( + w, + "\ + \ + {f}: {t}\ + ", + id = id, + f = field.name.as_ref().unwrap(), + t = ty.print(cx.cache()) + ); + document(w, cx, field, Some(variant)); + } + } + w.write_str("
"); + } + render_stability_since(w, variant, it, cx.tcx()); + } + } + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) +} + +fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) { + wrap_into_docblock(w, |w| { + highlight::render_with_highlighting( + &t.source, + w, + Some("macro"), + None, + None, + it.source.span().edition(), + ); + }); + document(w, cx, it, None) +} + +fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { + let name = it.name.as_ref().expect("proc-macros always have names"); + match m.kind { + MacroKind::Bang => { + w.push_str("
");
+            write!(w, "{}!() {{ /* proc-macro */ }}", name);
+            w.push_str("
"); + } + MacroKind::Attr => { + w.push_str("
");
+            write!(w, "#[{}]", name);
+            w.push_str("
"); + } + MacroKind::Derive => { + w.push_str("
");
+            write!(w, "#[derive({})]", name);
+            if !m.helpers.is_empty() {
+                w.push_str("\n{\n");
+                w.push_str("    // Attributes available to this derive:\n");
+                for attr in &m.helpers {
+                    writeln!(w, "    #[{}]", attr);
+                }
+                w.push_str("}\n");
+            }
+            w.push_str("
"); + } + } + document(w, cx, it, None) +} + +fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { + document(w, cx, it, None); + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) +} + +fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) { + w.write_str("
");
+    render_attributes(w, it, false);
+
+    write!(
+        w,
+        "{vis}const {name}: {typ}",
+        vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+        name = it.name.as_ref().unwrap(),
+        typ = c.type_.print(cx.cache()),
+    );
+
+    if c.value.is_some() || c.is_literal {
+        write!(w, " = {expr};", expr = Escape(&c.expr));
+    } else {
+        w.write_str(";");
+    }
+
+    if let Some(value) = &c.value {
+        if !c.is_literal {
+            let value_lowercase = value.to_lowercase();
+            let expr_lowercase = c.expr.to_lowercase();
+
+            if value_lowercase != expr_lowercase
+                && value_lowercase.trim_end_matches("i32") != expr_lowercase
+            {
+                write!(w, " // {value}", value = Escape(value));
+            }
+        }
+    }
+
+    w.write_str("
"); + document(w, cx, it, None) +} + +fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) { + wrap_into_docblock(w, |w| { + w.write_str("
");
+        render_attributes(w, it, true);
+        render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
+        w.write_str("
") + }); + + document(w, cx, it, None); + let mut fields = s + .fields + .iter() + .filter_map(|f| match *f.kind { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + }) + .peekable(); + if let CtorKind::Fictive = s.struct_type { + if fields.peek().is_some() { + write!( + w, + "

+ Fields{}

", + document_non_exhaustive_header(it) + ); + document_non_exhaustive(w, it); + for (field, ty) in fields { + let id = cx.derive_id(format!( + "{}.{}", + ItemType::StructField, + field.name.as_ref().unwrap() + )); + write!( + w, + "\ + \ + {name}: {ty}\ + ", + item_type = ItemType::StructField, + id = id, + name = field.name.as_ref().unwrap(), + ty = ty.print(cx.cache()) + ); + document(w, cx, field, Some(it)); + } + } + } + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) +} + +fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) { + w.write_str("
");
+    render_attributes(w, it, false);
+    write!(
+        w,
+        "{vis}static {mutability}{name}: {typ}
", + vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + mutability = s.mutability.print_with_space(), + name = it.name.as_ref().unwrap(), + typ = s.type_.print(cx.cache()) + ); + document(w, cx, it, None) +} + +fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { + w.write_str("
extern {\n");
+    render_attributes(w, it, false);
+    write!(
+        w,
+        "    {}type {};\n}}
", + it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.name.as_ref().unwrap(), + ); + + document(w, cx, it, None); + + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) +} + +fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { + document(w, cx, it, None) +} + +/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order). +crate fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering { + /// Takes a non-numeric and a numeric part from the given &str. + fn take_parts<'a>(s: &mut &'a str) -> (&'a str, &'a str) { + let i = s.find(|c: char| c.is_ascii_digit()); + let (a, b) = s.split_at(i.unwrap_or(s.len())); + let i = b.find(|c: char| !c.is_ascii_digit()); + let (b, c) = b.split_at(i.unwrap_or(b.len())); + *s = c; + (a, b) + } + + while !lhs.is_empty() || !rhs.is_empty() { + let (la, lb) = take_parts(&mut lhs); + let (ra, rb) = take_parts(&mut rhs); + // First process the non-numeric part. + match la.cmp(ra) { + Ordering::Equal => (), + x => return x, + } + // Then process the numeric part, if both sides have one (and they fit in a u64). + if let (Ok(ln), Ok(rn)) = (lb.parse::(), rb.parse::()) { + match ln.cmp(&rn) { + Ordering::Equal => (), + x => return x, + } + } + // Then process the numeric part again, but this time as strings. + match lb.cmp(rb) { + Ordering::Equal => (), + x => return x, + } + } + + Ordering::Equal +} + +pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String { + let mut s = cx.current.join("::"); + s.push_str("::"); + s.push_str(&item.name.unwrap().as_str()); + s +} + +pub(super) fn item_path(ty: ItemType, name: &str) -> String { + match ty { + ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)), + _ => format!("{}.{}.html", ty, name), + } +} + +fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cache: &Cache) -> String { + let mut bounds = String::new(); + if !t_bounds.is_empty() { + if !trait_alias { + bounds.push_str(": "); + } + for (i, p) in t_bounds.iter().enumerate() { + if i > 0 { + bounds.push_str(" + "); + } + bounds.push_str(&p.print(cache).to_string()); + } + } + bounds +} + +fn wrap_into_docblock(w: &mut Buffer, f: F) +where + F: FnOnce(&mut Buffer), +{ + w.write_str("
"); + f(w); + w.write_str("
") +} + +fn render_stability_since( + w: &mut Buffer, + item: &clean::Item, + containing_item: &clean::Item, + tcx: TyCtxt<'_>, +) { + render_stability_since_raw( + w, + item.stable_since(tcx).as_deref(), + item.const_stable_since(tcx).as_deref(), + containing_item.stable_since(tcx).as_deref(), + containing_item.const_stable_since(tcx).as_deref(), + ) +} + +fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cache: &Cache) -> Ordering { + let lhs = format!("{}", lhs.inner_impl().print(cache, false)); + let rhs = format!("{}", rhs.inner_impl().print(cache, false)); + + // lhs and rhs are formatted as HTML, which may be unnecessary + compare_names(&lhs, &rhs) +} + +fn render_implementor( + cx: &Context<'_>, + implementor: &Impl, + trait_: &clean::Item, + w: &mut Buffer, + implementor_dups: &FxHashMap, + aliases: &[String], +) { + // If there's already another implementor that has the same abbridged name, use the + // full path, for example in `std::iter::ExactSizeIterator` + let use_absolute = match implementor.inner_impl().for_ { + clean::ResolvedPath { ref path, is_generic: false, .. } + | clean::BorrowedRef { + type_: box clean::ResolvedPath { ref path, is_generic: false, .. }, + .. + } => implementor_dups[&path.last()].1, + _ => false, + }; + render_impl( + w, + cx, + implementor, + trait_, + AssocItemLink::Anchor(None), + RenderMode::Normal, + trait_.stable_since(cx.tcx()).as_deref(), + trait_.const_stable_since(cx.tcx()).as_deref(), + false, + Some(use_absolute), + false, + false, + aliases, + ); +} + +fn render_union( + w: &mut Buffer, + it: &clean::Item, + g: Option<&clean::Generics>, + fields: &[clean::Item], + tab: &str, + structhead: bool, + cx: &Context<'_>, +) { + write!( + w, + "{}{}{}", + it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + if structhead { "union " } else { "" }, + it.name.as_ref().unwrap() + ); + if let Some(g) = g { + write!(w, "{}", g.print(cx.cache())); + write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }.print(cx.cache())); + } + + write!(w, " {{\n{}", tab); + for field in fields { + if let clean::StructFieldItem(ref ty) = *field.kind { + write!( + w, + " {}{}: {},\n{}", + field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), + field.name.as_ref().unwrap(), + ty.print(cx.cache()), + tab + ); + } + } + + if it.has_stripped_fields().unwrap() { + write!(w, " // some fields omitted\n{}", tab); + } + w.write_str("}"); +} + +fn render_struct( + w: &mut Buffer, + it: &clean::Item, + g: Option<&clean::Generics>, + ty: CtorKind, + fields: &[clean::Item], + tab: &str, + structhead: bool, + cx: &Context<'_>, +) { + write!( + w, + "{}{}{}", + it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + if structhead { "struct " } else { "" }, + it.name.as_ref().unwrap() + ); + if let Some(g) = g { + write!(w, "{}", g.print(cx.cache())) + } + match ty { + CtorKind::Fictive => { + if let Some(g) = g { + write!( + w, + "{}", + WhereClause { gens: g, indent: 0, end_newline: true }.print(cx.cache()) + ) + } + let mut has_visible_fields = false; + w.write_str(" {"); + for field in fields { + if let clean::StructFieldItem(ref ty) = *field.kind { + write!( + w, + "\n{} {}{}: {},", + tab, + field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), + field.name.as_ref().unwrap(), + ty.print(cx.cache()) + ); + has_visible_fields = true; + } + } + + if has_visible_fields { + if it.has_stripped_fields().unwrap() { + write!(w, "\n{} // some fields omitted", tab); + } + write!(w, "\n{}", tab); + } else if it.has_stripped_fields().unwrap() { + // If there are no visible fields we can just display + // `{ /* fields omitted */ }` to save space. + write!(w, " /* fields omitted */ "); + } + w.write_str("}"); + } + CtorKind::Fn => { + w.write_str("("); + for (i, field) in fields.iter().enumerate() { + if i > 0 { + w.write_str(", "); + } + match *field.kind { + clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), + clean::StructFieldItem(ref ty) => { + write!( + w, + "{}{}", + field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), + ty.print(cx.cache()) + ) + } + _ => unreachable!(), + } + } + w.write_str(")"); + if let Some(g) = g { + write!( + w, + "{}", + WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache()) + ) + } + w.write_str(";"); + } + CtorKind::Const => { + // Needed for PhantomData. + if let Some(g) = g { + write!( + w, + "{}", + WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache()) + ) + } + w.write_str(";"); + } + } +} + +fn document_non_exhaustive_header(item: &clean::Item) -> &str { + if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" } +} + +fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { + if item.is_non_exhaustive() { + write!(w, "
", { + if item.is_struct() { + "struct" + } else if item.is_enum() { + "enum" + } else if item.is_variant() { + "variant" + } else { + "type" + } + }); + + if item.is_struct() { + w.write_str( + "Non-exhaustive structs could have additional fields added in future. \ + Therefore, non-exhaustive structs cannot be constructed in external crates \ + using the traditional Struct {{ .. }} syntax; cannot be \ + matched against without a wildcard ..; and \ + struct update syntax will not work.", + ); + } else if item.is_enum() { + w.write_str( + "Non-exhaustive enums could have additional variants added in future. \ + Therefore, when matching against variants of non-exhaustive enums, an \ + extra wildcard arm must be added to account for any future variants.", + ); + } else if item.is_variant() { + w.write_str( + "Non-exhaustive enum variants could have additional fields added in future. \ + Therefore, non-exhaustive enum variants cannot be constructed in external \ + crates and cannot be matched against.", + ); + } else { + w.write_str( + "This type will require a wildcard arm in any match statements or constructors.", + ); + } + + w.write_str("
"); + } +} From 14983b9812e67a283587b651c0a14e1d4e1ab723 Mon Sep 17 00:00:00 2001 From: Nicholas-Baron Date: Sat, 13 Feb 2021 23:03:07 -0800 Subject: [PATCH 53/62] Moved the `make_item_keywords` function to `context.rs` as it is only used there --- src/librustdoc/html/render/context.rs | 8 ++++++-- src/librustdoc/html/render/mod.rs | 3 --- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 4322e2448341c..bd1c17185ea84 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -17,8 +17,8 @@ use rustc_span::symbol::sym; use super::cache::{build_index, ExternalLocation}; use super::print_item::{full_path, item_path, print_item}; use super::{ - make_item_keywords, print_sidebar, settings, write_shared, AllTypes, NameDoc, SharedContext, - StylePath, BASIC_KEYWORDS, CURRENT_DEPTH, INITIAL_IDS, + print_sidebar, settings, AllTypes, NameDoc, SharedContext, StylePath, BASIC_KEYWORDS, + CURRENT_DEPTH, INITIAL_IDS, }; use crate::clean::{self, AttributesExt}; @@ -610,3 +610,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { &self.cache } } + +fn make_item_keywords(it: &clean::Item) -> String { + format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) +} diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 2a13d190318f5..fc184bd4dea14 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2924,9 +2924,6 @@ fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; -fn make_item_keywords(it: &clean::Item) -> String { - format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) -} /// Returns a list of all paths used in the type. /// This is used to help deduplicate imported impls From fd14e386123f96f27f9d2cf12f64a6e060b6a079 Mon Sep 17 00:00:00 2001 From: Nicholas-Baron Date: Sat, 13 Feb 2021 23:17:38 -0800 Subject: [PATCH 54/62] Moved `write_shared` to its own file --- src/librustdoc/html/render/context.rs | 1 + src/librustdoc/html/render/mod.rs | 541 +------------------- src/librustdoc/html/render/write_shared.rs | 542 +++++++++++++++++++++ 3 files changed, 549 insertions(+), 535 deletions(-) create mode 100644 src/librustdoc/html/render/write_shared.rs diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index bd1c17185ea84..976168a9ac407 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -16,6 +16,7 @@ use rustc_span::symbol::sym; use super::cache::{build_index, ExternalLocation}; use super::print_item::{full_path, item_path, print_item}; +use super::write_shared::write_shared; use super::{ print_sidebar, settings, AllTypes, NameDoc, SharedContext, StylePath, BASIC_KEYWORDS, CURRENT_DEPTH, INITIAL_IDS, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index fc184bd4dea14..1db081b181f9b 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -31,26 +31,22 @@ crate mod cache; mod tests; mod context; -crate use context::*; - mod print_item; +mod write_shared; + +crate use context::*; use std::cell::{Cell, RefCell}; use std::collections::VecDeque; use std::default::Default; -use std::ffi::OsStr; -use std::fmt::{self, Write}; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::io::{self, BufReader}; -use std::path::{Component, Path, PathBuf}; +use std::fmt; +use std::path::{Path, PathBuf}; use std::str; use std::string::ToString; use itertools::Itertools; use rustc_ast_pretty::pprust; use rustc_attr::{Deprecation, StabilityLevel}; -use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::CtorKind; @@ -64,7 +60,6 @@ use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; use crate::clean::{self, GetDefId, RenderedLink, SelfTy, TypeKind}; -use crate::config::RenderOptions; use crate::docfs::{DocFS, PathError}; use crate::error::Error; use crate::formats::cache::Cache; @@ -75,8 +70,8 @@ use crate::html::format::{ href, print_abi_with_space, print_default_space, print_generic_bounds, Buffer, Function, PrintWithSpace, WhereClause, }; +use crate::html::layout; use crate::html::markdown::{self, ErrorCodes, Markdown, MarkdownHtml, MarkdownSummaryLine}; -use crate::html::{layout, static_files}; /// A pair of name and its optional document. crate type NameDoc = (String, Option); @@ -318,529 +313,6 @@ crate const INITIAL_IDS: [&'static str; 15] = [ "implementations", ]; -fn write_shared( - cx: &Context<'_>, - krate: &clean::Crate, - search_index: String, - options: &RenderOptions, -) -> Result<(), Error> { - // Write out the shared files. Note that these are shared among all rustdoc - // docs placed in the output directory, so this needs to be a synchronized - // operation with respect to all other rustdocs running around. - let lock_file = cx.dst.join(".lock"); - let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); - - // Add all the static files. These may already exist, but we just - // overwrite them anyway to make sure that they're fresh and up-to-date. - - write_minify( - &cx.shared.fs, - cx.path("rustdoc.css"), - static_files::RUSTDOC_CSS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("settings.css"), - static_files::SETTINGS_CSS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("noscript.css"), - static_files::NOSCRIPT_CSS, - options.enable_minification, - )?; - - // To avoid "light.css" to be overwritten, we'll first run over the received themes and only - // then we'll run over the "official" styles. - let mut themes: FxHashSet = FxHashSet::default(); - - for entry in &cx.shared.style_files { - let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path); - let extension = - try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path); - - // Handle the official themes - match theme { - "light" => write_minify( - &cx.shared.fs, - cx.path("light.css"), - static_files::themes::LIGHT, - options.enable_minification, - )?, - "dark" => write_minify( - &cx.shared.fs, - cx.path("dark.css"), - static_files::themes::DARK, - options.enable_minification, - )?, - "ayu" => write_minify( - &cx.shared.fs, - cx.path("ayu.css"), - static_files::themes::AYU, - options.enable_minification, - )?, - _ => { - // Handle added third-party themes - let content = try_err!(fs::read(&entry.path), &entry.path); - cx.shared - .fs - .write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?; - } - }; - - themes.insert(theme.to_owned()); - } - - let write = |p, c| cx.shared.fs.write(p, c); - if (*cx.shared).layout.logo.is_empty() { - write(cx.path("rust-logo.png"), static_files::RUST_LOGO)?; - } - if (*cx.shared).layout.favicon.is_empty() { - write(cx.path("favicon.svg"), static_files::RUST_FAVICON_SVG)?; - write(cx.path("favicon-16x16.png"), static_files::RUST_FAVICON_PNG_16)?; - write(cx.path("favicon-32x32.png"), static_files::RUST_FAVICON_PNG_32)?; - } - write(cx.path("brush.svg"), static_files::BRUSH_SVG)?; - write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?; - write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?; - - let mut themes: Vec<&String> = themes.iter().collect(); - themes.sort(); - // To avoid theme switch latencies as much as possible, we put everything theme related - // at the beginning of the html files into another js file. - let theme_js = format!( - r#"var themes = document.getElementById("theme-choices"); -var themePicker = document.getElementById("theme-picker"); - -function showThemeButtonState() {{ - themes.style.display = "block"; - themePicker.style.borderBottomRightRadius = "0"; - themePicker.style.borderBottomLeftRadius = "0"; -}} - -function hideThemeButtonState() {{ - themes.style.display = "none"; - themePicker.style.borderBottomRightRadius = "3px"; - themePicker.style.borderBottomLeftRadius = "3px"; -}} - -function switchThemeButtonState() {{ - if (themes.style.display === "block") {{ - hideThemeButtonState(); - }} else {{ - showThemeButtonState(); - }} -}}; - -function handleThemeButtonsBlur(e) {{ - var active = document.activeElement; - var related = e.relatedTarget; - - if (active.id !== "theme-picker" && - (!active.parentNode || active.parentNode.id !== "theme-choices") && - (!related || - (related.id !== "theme-picker" && - (!related.parentNode || related.parentNode.id !== "theme-choices")))) {{ - hideThemeButtonState(); - }} -}} - -themePicker.onclick = switchThemeButtonState; -themePicker.onblur = handleThemeButtonsBlur; -{}.forEach(function(item) {{ - var but = document.createElement("button"); - but.textContent = item; - but.onclick = function(el) {{ - switchTheme(currentTheme, mainTheme, item, true); - useSystemTheme(false); - }}; - but.onblur = handleThemeButtonsBlur; - themes.appendChild(but); -}});"#, - serde_json::to_string(&themes).unwrap() - ); - - write_minify(&cx.shared.fs, cx.path("theme.js"), &theme_js, options.enable_minification)?; - write_minify( - &cx.shared.fs, - cx.path("main.js"), - static_files::MAIN_JS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("settings.js"), - static_files::SETTINGS_JS, - options.enable_minification, - )?; - if cx.shared.include_sources { - write_minify( - &cx.shared.fs, - cx.path("source-script.js"), - static_files::sidebar::SOURCE_SCRIPT, - options.enable_minification, - )?; - } - - { - write_minify( - &cx.shared.fs, - cx.path("storage.js"), - &format!( - "var resourcesSuffix = \"{}\";{}", - cx.shared.resource_suffix, - static_files::STORAGE_JS - ), - options.enable_minification, - )?; - } - - if let Some(ref css) = cx.shared.layout.css_file_extension { - let out = cx.path("theme.css"); - let buffer = try_err!(fs::read_to_string(css), css); - if !options.enable_minification { - cx.shared.fs.write(&out, &buffer)?; - } else { - write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?; - } - } - write_minify( - &cx.shared.fs, - cx.path("normalize.css"), - static_files::NORMALIZE_CSS, - options.enable_minification, - )?; - write(cx.dst.join("FiraSans-Regular.woff2"), static_files::fira_sans::REGULAR2)?; - write(cx.dst.join("FiraSans-Medium.woff2"), static_files::fira_sans::MEDIUM2)?; - write(cx.dst.join("FiraSans-Regular.woff"), static_files::fira_sans::REGULAR)?; - write(cx.dst.join("FiraSans-Medium.woff"), static_files::fira_sans::MEDIUM)?; - write(cx.dst.join("FiraSans-LICENSE.txt"), static_files::fira_sans::LICENSE)?; - write(cx.dst.join("SourceSerifPro-Regular.ttf.woff"), static_files::source_serif_pro::REGULAR)?; - write(cx.dst.join("SourceSerifPro-Bold.ttf.woff"), static_files::source_serif_pro::BOLD)?; - write(cx.dst.join("SourceSerifPro-It.ttf.woff"), static_files::source_serif_pro::ITALIC)?; - write(cx.dst.join("SourceSerifPro-LICENSE.md"), static_files::source_serif_pro::LICENSE)?; - write(cx.dst.join("SourceCodePro-Regular.woff"), static_files::source_code_pro::REGULAR)?; - write(cx.dst.join("SourceCodePro-Semibold.woff"), static_files::source_code_pro::SEMIBOLD)?; - write(cx.dst.join("SourceCodePro-LICENSE.txt"), static_files::source_code_pro::LICENSE)?; - write(cx.dst.join("LICENSE-MIT.txt"), static_files::LICENSE_MIT)?; - write(cx.dst.join("LICENSE-APACHE.txt"), static_files::LICENSE_APACHE)?; - write(cx.dst.join("COPYRIGHT.txt"), static_files::COPYRIGHT)?; - - fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec, Vec)> { - let mut ret = Vec::new(); - let mut krates = Vec::new(); - - if path.exists() { - let prefix = format!(r#"{}["{}"]"#, key, krate); - for line in BufReader::new(File::open(path)?).lines() { - let line = line?; - if !line.starts_with(key) { - continue; - } - if line.starts_with(&prefix) { - continue; - } - ret.push(line.to_string()); - krates.push( - line[key.len() + 2..] - .split('"') - .next() - .map(|s| s.to_owned()) - .unwrap_or_else(String::new), - ); - } - } - Ok((ret, krates)) - } - - fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec, Vec)> { - let mut ret = Vec::new(); - let mut krates = Vec::new(); - - if path.exists() { - let prefix = format!("\"{}\"", krate); - for line in BufReader::new(File::open(path)?).lines() { - let line = line?; - if !line.starts_with('"') { - continue; - } - if line.starts_with(&prefix) { - continue; - } - if line.ends_with(",\\") { - ret.push(line[..line.len() - 2].to_string()); - } else { - // Ends with "\\" (it's the case for the last added crate line) - ret.push(line[..line.len() - 1].to_string()); - } - krates.push( - line.split('"') - .find(|s| !s.is_empty()) - .map(|s| s.to_owned()) - .unwrap_or_else(String::new), - ); - } - } - Ok((ret, krates)) - } - - use std::ffi::OsString; - - #[derive(Debug)] - struct Hierarchy { - elem: OsString, - children: FxHashMap, - elems: FxHashSet, - } - - impl Hierarchy { - fn new(elem: OsString) -> Hierarchy { - Hierarchy { elem, children: FxHashMap::default(), elems: FxHashSet::default() } - } - - fn to_json_string(&self) -> String { - let mut subs: Vec<&Hierarchy> = self.children.values().collect(); - subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem)); - let mut files = self - .elems - .iter() - .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion"))) - .collect::>(); - files.sort_unstable(); - let subs = subs.iter().map(|s| s.to_json_string()).collect::>().join(","); - let dirs = - if subs.is_empty() { String::new() } else { format!(",\"dirs\":[{}]", subs) }; - let files = files.join(","); - let files = - if files.is_empty() { String::new() } else { format!(",\"files\":[{}]", files) }; - format!( - "{{\"name\":\"{name}\"{dirs}{files}}}", - name = self.elem.to_str().expect("invalid osstring conversion"), - dirs = dirs, - files = files - ) - } - } - - if cx.shared.include_sources { - let mut hierarchy = Hierarchy::new(OsString::new()); - for source in cx - .shared - .local_sources - .iter() - .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok()) - { - let mut h = &mut hierarchy; - let mut elems = source - .components() - .filter_map(|s| match s { - Component::Normal(s) => Some(s.to_owned()), - _ => None, - }) - .peekable(); - loop { - let cur_elem = elems.next().expect("empty file path"); - if elems.peek().is_none() { - h.elems.insert(cur_elem); - break; - } else { - let e = cur_elem.clone(); - h = h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e)); - } - } - } - - let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix)); - let (mut all_sources, _krates) = - try_err!(collect(&dst, &krate.name.as_str(), "sourcesIndex"), &dst); - all_sources.push(format!( - "sourcesIndex[\"{}\"] = {};", - &krate.name, - hierarchy.to_json_string() - )); - all_sources.sort(); - let v = format!( - "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n", - all_sources.join("\n") - ); - cx.shared.fs.write(&dst, v.as_bytes())?; - } - - // Update the search index and crate list. - let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); - let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name.as_str()), &dst); - all_indexes.push(search_index); - krates.push(krate.name.to_string()); - krates.sort(); - - // Sort the indexes by crate so the file will be generated identically even - // with rustdoc running in parallel. - all_indexes.sort(); - { - let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); - v.push_str(&all_indexes.join(",\\\n")); - v.push_str("\\\n}');\ninitSearch(searchIndex);"); - cx.shared.fs.write(&dst, &v)?; - } - - let crate_list_dst = cx.dst.join(&format!("crates{}.js", cx.shared.resource_suffix)); - let crate_list = - format!("window.ALL_CRATES = [{}];", krates.iter().map(|k| format!("\"{}\"", k)).join(",")); - cx.shared.fs.write(&crate_list_dst, &crate_list)?; - - if options.enable_index_page { - if let Some(index_page) = options.index_page.clone() { - let mut md_opts = options.clone(); - md_opts.output = cx.dst.clone(); - md_opts.external_html = (*cx.shared).layout.external_html.clone(); - - crate::markdown::render(&index_page, md_opts, cx.shared.edition) - .map_err(|e| Error::new(e, &index_page))?; - } else { - let dst = cx.dst.join("index.html"); - let page = layout::Page { - title: "Index of crates", - css_class: "mod", - root_path: "./", - static_root_path: cx.shared.static_root_path.as_deref(), - description: "List of crates", - keywords: BASIC_KEYWORDS, - resource_suffix: &cx.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - - let content = format!( - "

\ - List of all crates\ -

    {}
", - krates - .iter() - .map(|s| { - format!( - "
  • {}
  • ", - ensure_trailing_slash(s), - s - ) - }) - .collect::() - ); - let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files); - cx.shared.fs.write(&dst, v.as_bytes())?; - } - } - - // Update the list of all implementors for traits - let dst = cx.dst.join("implementors"); - for (&did, imps) in &cx.cache.implementors { - // Private modules can leak through to this phase of rustdoc, which - // could contain implementations for otherwise private types. In some - // rare cases we could find an implementation for an item which wasn't - // indexed, so we just skip this step in that case. - // - // FIXME: this is a vague explanation for why this can't be a `get`, in - // theory it should be... - let &(ref remote_path, remote_item_type) = match cx.cache.paths.get(&did) { - Some(p) => p, - None => match cx.cache.external_paths.get(&did) { - Some(p) => p, - None => continue, - }, - }; - - #[derive(Serialize)] - struct Implementor { - text: String, - synthetic: bool, - types: Vec, - } - - let implementors = imps - .iter() - .filter_map(|imp| { - // If the trait and implementation are in the same crate, then - // there's no need to emit information about it (there's inlining - // going on). If they're in different crates then the crate defining - // the trait will be interested in our implementation. - // - // If the implementation is from another crate then that crate - // should add it. - if imp.impl_item.def_id.krate == did.krate || !imp.impl_item.def_id.is_local() { - None - } else { - Some(Implementor { - text: imp.inner_impl().print(cx.cache(), false).to_string(), - synthetic: imp.inner_impl().synthetic, - types: collect_paths_for_type(imp.inner_impl().for_.clone(), cx.cache()), - }) - } - }) - .collect::>(); - - // Only create a js file if we have impls to add to it. If the trait is - // documented locally though we always create the file to avoid dead - // links. - if implementors.is_empty() && !cx.cache.paths.contains_key(&did) { - continue; - } - - let implementors = format!( - r#"implementors["{}"] = {};"#, - krate.name, - serde_json::to_string(&implementors).unwrap() - ); - - let mut mydst = dst.clone(); - for part in &remote_path[..remote_path.len() - 1] { - mydst.push(part); - } - cx.shared.ensure_dir(&mydst)?; - mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1])); - - let (mut all_implementors, _) = - try_err!(collect(&mydst, &krate.name.as_str(), "implementors"), &mydst); - all_implementors.push(implementors); - // Sort the implementors by crate so the file will be generated - // identically even with rustdoc running in parallel. - all_implementors.sort(); - - let mut v = String::from("(function() {var implementors = {};\n"); - for implementor in &all_implementors { - writeln!(v, "{}", *implementor).unwrap(); - } - v.push_str( - "if (window.register_implementors) {\ - window.register_implementors(implementors);\ - } else {\ - window.pending_implementors = implementors;\ - }", - ); - v.push_str("})()"); - cx.shared.fs.write(&mydst, &v)?; - } - Ok(()) -} - -fn write_minify( - fs: &DocFS, - dst: PathBuf, - contents: &str, - enable_minification: bool, -) -> Result<(), Error> { - if enable_minification { - if dst.extension() == Some(&OsStr::new("css")) { - let res = try_none!(minifier::css::minify(contents).ok(), &dst); - fs.write(dst, res.as_bytes()) - } else { - fs.write(dst, minifier::js::minify(contents).as_bytes()) - } - } else { - fs.write(dst, contents.as_bytes()) - } -} - fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { if let Some(l) = cx.src_href(item) { write!(buf, "[src]", l) @@ -2924,7 +2396,6 @@ fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; - /// Returns a list of all paths used in the type. /// This is used to help deduplicate imported impls /// for reexported types. If any of the contained diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs new file mode 100644 index 0000000000000..cbf0f9a4927c6 --- /dev/null +++ b/src/librustdoc/html/render/write_shared.rs @@ -0,0 +1,542 @@ +use std::ffi::OsStr; +use std::fmt::Write; +use std::fs::{self, File}; +use std::io::prelude::*; +use std::io::{self, BufReader}; +use std::path::{Component, Path, PathBuf}; + +use itertools::Itertools; +use rustc_data_structures::flock; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use serde::Serialize; + +use super::{collect_paths_for_type, ensure_trailing_slash, Context, BASIC_KEYWORDS}; +use crate::clean::Crate; +use crate::config::RenderOptions; +use crate::docfs::{DocFS, PathError}; +use crate::error::Error; +use crate::formats::FormatRenderer; +use crate::html::{layout, static_files}; + +pub(super) fn write_shared( + cx: &Context<'_>, + krate: &Crate, + search_index: String, + options: &RenderOptions, +) -> Result<(), Error> { + // Write out the shared files. Note that these are shared among all rustdoc + // docs placed in the output directory, so this needs to be a synchronized + // operation with respect to all other rustdocs running around. + let lock_file = cx.dst.join(".lock"); + let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); + + // Add all the static files. These may already exist, but we just + // overwrite them anyway to make sure that they're fresh and up-to-date. + + write_minify( + &cx.shared.fs, + cx.path("rustdoc.css"), + static_files::RUSTDOC_CSS, + options.enable_minification, + )?; + write_minify( + &cx.shared.fs, + cx.path("settings.css"), + static_files::SETTINGS_CSS, + options.enable_minification, + )?; + write_minify( + &cx.shared.fs, + cx.path("noscript.css"), + static_files::NOSCRIPT_CSS, + options.enable_minification, + )?; + + // To avoid "light.css" to be overwritten, we'll first run over the received themes and only + // then we'll run over the "official" styles. + let mut themes: FxHashSet = FxHashSet::default(); + + for entry in &cx.shared.style_files { + let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path); + let extension = + try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path); + + // Handle the official themes + match theme { + "light" => write_minify( + &cx.shared.fs, + cx.path("light.css"), + static_files::themes::LIGHT, + options.enable_minification, + )?, + "dark" => write_minify( + &cx.shared.fs, + cx.path("dark.css"), + static_files::themes::DARK, + options.enable_minification, + )?, + "ayu" => write_minify( + &cx.shared.fs, + cx.path("ayu.css"), + static_files::themes::AYU, + options.enable_minification, + )?, + _ => { + // Handle added third-party themes + let content = try_err!(fs::read(&entry.path), &entry.path); + cx.shared + .fs + .write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?; + } + }; + + themes.insert(theme.to_owned()); + } + + let write = |p, c| cx.shared.fs.write(p, c); + if (*cx.shared).layout.logo.is_empty() { + write(cx.path("rust-logo.png"), static_files::RUST_LOGO)?; + } + if (*cx.shared).layout.favicon.is_empty() { + write(cx.path("favicon.svg"), static_files::RUST_FAVICON_SVG)?; + write(cx.path("favicon-16x16.png"), static_files::RUST_FAVICON_PNG_16)?; + write(cx.path("favicon-32x32.png"), static_files::RUST_FAVICON_PNG_32)?; + } + write(cx.path("brush.svg"), static_files::BRUSH_SVG)?; + write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?; + write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?; + + let mut themes: Vec<&String> = themes.iter().collect(); + themes.sort(); + // To avoid theme switch latencies as much as possible, we put everything theme related + // at the beginning of the html files into another js file. + let theme_js = format!( + r#"var themes = document.getElementById("theme-choices"); +var themePicker = document.getElementById("theme-picker"); + +function showThemeButtonState() {{ + themes.style.display = "block"; + themePicker.style.borderBottomRightRadius = "0"; + themePicker.style.borderBottomLeftRadius = "0"; +}} + +function hideThemeButtonState() {{ + themes.style.display = "none"; + themePicker.style.borderBottomRightRadius = "3px"; + themePicker.style.borderBottomLeftRadius = "3px"; +}} + +function switchThemeButtonState() {{ + if (themes.style.display === "block") {{ + hideThemeButtonState(); + }} else {{ + showThemeButtonState(); + }} +}}; + +function handleThemeButtonsBlur(e) {{ + var active = document.activeElement; + var related = e.relatedTarget; + + if (active.id !== "theme-picker" && + (!active.parentNode || active.parentNode.id !== "theme-choices") && + (!related || + (related.id !== "theme-picker" && + (!related.parentNode || related.parentNode.id !== "theme-choices")))) {{ + hideThemeButtonState(); + }} +}} + +themePicker.onclick = switchThemeButtonState; +themePicker.onblur = handleThemeButtonsBlur; +{}.forEach(function(item) {{ + var but = document.createElement("button"); + but.textContent = item; + but.onclick = function(el) {{ + switchTheme(currentTheme, mainTheme, item, true); + useSystemTheme(false); + }}; + but.onblur = handleThemeButtonsBlur; + themes.appendChild(but); +}});"#, + serde_json::to_string(&themes).unwrap() + ); + + write_minify(&cx.shared.fs, cx.path("theme.js"), &theme_js, options.enable_minification)?; + write_minify( + &cx.shared.fs, + cx.path("main.js"), + static_files::MAIN_JS, + options.enable_minification, + )?; + write_minify( + &cx.shared.fs, + cx.path("settings.js"), + static_files::SETTINGS_JS, + options.enable_minification, + )?; + if cx.shared.include_sources { + write_minify( + &cx.shared.fs, + cx.path("source-script.js"), + static_files::sidebar::SOURCE_SCRIPT, + options.enable_minification, + )?; + } + + { + write_minify( + &cx.shared.fs, + cx.path("storage.js"), + &format!( + "var resourcesSuffix = \"{}\";{}", + cx.shared.resource_suffix, + static_files::STORAGE_JS + ), + options.enable_minification, + )?; + } + + if let Some(ref css) = cx.shared.layout.css_file_extension { + let out = cx.path("theme.css"); + let buffer = try_err!(fs::read_to_string(css), css); + if !options.enable_minification { + cx.shared.fs.write(&out, &buffer)?; + } else { + write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?; + } + } + write_minify( + &cx.shared.fs, + cx.path("normalize.css"), + static_files::NORMALIZE_CSS, + options.enable_minification, + )?; + write(cx.dst.join("FiraSans-Regular.woff2"), static_files::fira_sans::REGULAR2)?; + write(cx.dst.join("FiraSans-Medium.woff2"), static_files::fira_sans::MEDIUM2)?; + write(cx.dst.join("FiraSans-Regular.woff"), static_files::fira_sans::REGULAR)?; + write(cx.dst.join("FiraSans-Medium.woff"), static_files::fira_sans::MEDIUM)?; + write(cx.dst.join("FiraSans-LICENSE.txt"), static_files::fira_sans::LICENSE)?; + write(cx.dst.join("SourceSerifPro-Regular.ttf.woff"), static_files::source_serif_pro::REGULAR)?; + write(cx.dst.join("SourceSerifPro-Bold.ttf.woff"), static_files::source_serif_pro::BOLD)?; + write(cx.dst.join("SourceSerifPro-It.ttf.woff"), static_files::source_serif_pro::ITALIC)?; + write(cx.dst.join("SourceSerifPro-LICENSE.md"), static_files::source_serif_pro::LICENSE)?; + write(cx.dst.join("SourceCodePro-Regular.woff"), static_files::source_code_pro::REGULAR)?; + write(cx.dst.join("SourceCodePro-Semibold.woff"), static_files::source_code_pro::SEMIBOLD)?; + write(cx.dst.join("SourceCodePro-LICENSE.txt"), static_files::source_code_pro::LICENSE)?; + write(cx.dst.join("LICENSE-MIT.txt"), static_files::LICENSE_MIT)?; + write(cx.dst.join("LICENSE-APACHE.txt"), static_files::LICENSE_APACHE)?; + write(cx.dst.join("COPYRIGHT.txt"), static_files::COPYRIGHT)?; + + fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec, Vec)> { + let mut ret = Vec::new(); + let mut krates = Vec::new(); + + if path.exists() { + let prefix = format!(r#"{}["{}"]"#, key, krate); + for line in BufReader::new(File::open(path)?).lines() { + let line = line?; + if !line.starts_with(key) { + continue; + } + if line.starts_with(&prefix) { + continue; + } + ret.push(line.to_string()); + krates.push( + line[key.len() + 2..] + .split('"') + .next() + .map(|s| s.to_owned()) + .unwrap_or_else(String::new), + ); + } + } + Ok((ret, krates)) + } + + fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec, Vec)> { + let mut ret = Vec::new(); + let mut krates = Vec::new(); + + if path.exists() { + let prefix = format!("\"{}\"", krate); + for line in BufReader::new(File::open(path)?).lines() { + let line = line?; + if !line.starts_with('"') { + continue; + } + if line.starts_with(&prefix) { + continue; + } + if line.ends_with(",\\") { + ret.push(line[..line.len() - 2].to_string()); + } else { + // Ends with "\\" (it's the case for the last added crate line) + ret.push(line[..line.len() - 1].to_string()); + } + krates.push( + line.split('"') + .find(|s| !s.is_empty()) + .map(|s| s.to_owned()) + .unwrap_or_else(String::new), + ); + } + } + Ok((ret, krates)) + } + + use std::ffi::OsString; + + #[derive(Debug)] + struct Hierarchy { + elem: OsString, + children: FxHashMap, + elems: FxHashSet, + } + + impl Hierarchy { + fn new(elem: OsString) -> Hierarchy { + Hierarchy { elem, children: FxHashMap::default(), elems: FxHashSet::default() } + } + + fn to_json_string(&self) -> String { + let mut subs: Vec<&Hierarchy> = self.children.values().collect(); + subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem)); + let mut files = self + .elems + .iter() + .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion"))) + .collect::>(); + files.sort_unstable(); + let subs = subs.iter().map(|s| s.to_json_string()).collect::>().join(","); + let dirs = + if subs.is_empty() { String::new() } else { format!(",\"dirs\":[{}]", subs) }; + let files = files.join(","); + let files = + if files.is_empty() { String::new() } else { format!(",\"files\":[{}]", files) }; + format!( + "{{\"name\":\"{name}\"{dirs}{files}}}", + name = self.elem.to_str().expect("invalid osstring conversion"), + dirs = dirs, + files = files + ) + } + } + + if cx.shared.include_sources { + let mut hierarchy = Hierarchy::new(OsString::new()); + for source in cx + .shared + .local_sources + .iter() + .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok()) + { + let mut h = &mut hierarchy; + let mut elems = source + .components() + .filter_map(|s| match s { + Component::Normal(s) => Some(s.to_owned()), + _ => None, + }) + .peekable(); + loop { + let cur_elem = elems.next().expect("empty file path"); + if elems.peek().is_none() { + h.elems.insert(cur_elem); + break; + } else { + let e = cur_elem.clone(); + h = h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e)); + } + } + } + + let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix)); + let (mut all_sources, _krates) = + try_err!(collect(&dst, &krate.name.as_str(), "sourcesIndex"), &dst); + all_sources.push(format!( + "sourcesIndex[\"{}\"] = {};", + &krate.name, + hierarchy.to_json_string() + )); + all_sources.sort(); + let v = format!( + "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n", + all_sources.join("\n") + ); + cx.shared.fs.write(&dst, v.as_bytes())?; + } + + // Update the search index and crate list. + let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); + let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name.as_str()), &dst); + all_indexes.push(search_index); + krates.push(krate.name.to_string()); + krates.sort(); + + // Sort the indexes by crate so the file will be generated identically even + // with rustdoc running in parallel. + all_indexes.sort(); + { + let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); + v.push_str(&all_indexes.join(",\\\n")); + v.push_str("\\\n}');\ninitSearch(searchIndex);"); + cx.shared.fs.write(&dst, &v)?; + } + + let crate_list_dst = cx.dst.join(&format!("crates{}.js", cx.shared.resource_suffix)); + let crate_list = + format!("window.ALL_CRATES = [{}];", krates.iter().map(|k| format!("\"{}\"", k)).join(",")); + cx.shared.fs.write(&crate_list_dst, &crate_list)?; + + if options.enable_index_page { + if let Some(index_page) = options.index_page.clone() { + let mut md_opts = options.clone(); + md_opts.output = cx.dst.clone(); + md_opts.external_html = (*cx.shared).layout.external_html.clone(); + + crate::markdown::render(&index_page, md_opts, cx.shared.edition) + .map_err(|e| Error::new(e, &index_page))?; + } else { + let dst = cx.dst.join("index.html"); + let page = layout::Page { + title: "Index of crates", + css_class: "mod", + root_path: "./", + static_root_path: cx.shared.static_root_path.as_deref(), + description: "List of crates", + keywords: BASIC_KEYWORDS, + resource_suffix: &cx.shared.resource_suffix, + extra_scripts: &[], + static_extra_scripts: &[], + }; + + let content = format!( + "

    \ + List of all crates\ +

      {}
    ", + krates + .iter() + .map(|s| { + format!( + "
  • {}
  • ", + ensure_trailing_slash(s), + s + ) + }) + .collect::() + ); + let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files); + cx.shared.fs.write(&dst, v.as_bytes())?; + } + } + + // Update the list of all implementors for traits + let dst = cx.dst.join("implementors"); + for (&did, imps) in &cx.cache.implementors { + // Private modules can leak through to this phase of rustdoc, which + // could contain implementations for otherwise private types. In some + // rare cases we could find an implementation for an item which wasn't + // indexed, so we just skip this step in that case. + // + // FIXME: this is a vague explanation for why this can't be a `get`, in + // theory it should be... + let &(ref remote_path, remote_item_type) = match cx.cache.paths.get(&did) { + Some(p) => p, + None => match cx.cache.external_paths.get(&did) { + Some(p) => p, + None => continue, + }, + }; + + #[derive(Serialize)] + struct Implementor { + text: String, + synthetic: bool, + types: Vec, + } + + let implementors = imps + .iter() + .filter_map(|imp| { + // If the trait and implementation are in the same crate, then + // there's no need to emit information about it (there's inlining + // going on). If they're in different crates then the crate defining + // the trait will be interested in our implementation. + // + // If the implementation is from another crate then that crate + // should add it. + if imp.impl_item.def_id.krate == did.krate || !imp.impl_item.def_id.is_local() { + None + } else { + Some(Implementor { + text: imp.inner_impl().print(cx.cache(), false).to_string(), + synthetic: imp.inner_impl().synthetic, + types: collect_paths_for_type(imp.inner_impl().for_.clone(), cx.cache()), + }) + } + }) + .collect::>(); + + // Only create a js file if we have impls to add to it. If the trait is + // documented locally though we always create the file to avoid dead + // links. + if implementors.is_empty() && !cx.cache.paths.contains_key(&did) { + continue; + } + + let implementors = format!( + r#"implementors["{}"] = {};"#, + krate.name, + serde_json::to_string(&implementors).unwrap() + ); + + let mut mydst = dst.clone(); + for part in &remote_path[..remote_path.len() - 1] { + mydst.push(part); + } + cx.shared.ensure_dir(&mydst)?; + mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1])); + + let (mut all_implementors, _) = + try_err!(collect(&mydst, &krate.name.as_str(), "implementors"), &mydst); + all_implementors.push(implementors); + // Sort the implementors by crate so the file will be generated + // identically even with rustdoc running in parallel. + all_implementors.sort(); + + let mut v = String::from("(function() {var implementors = {};\n"); + for implementor in &all_implementors { + writeln!(v, "{}", *implementor).unwrap(); + } + v.push_str( + "if (window.register_implementors) {\ + window.register_implementors(implementors);\ + } else {\ + window.pending_implementors = implementors;\ + }", + ); + v.push_str("})()"); + cx.shared.fs.write(&mydst, &v)?; + } + Ok(()) +} + +fn write_minify( + fs: &DocFS, + dst: PathBuf, + contents: &str, + enable_minification: bool, +) -> Result<(), Error> { + if enable_minification { + if dst.extension() == Some(&OsStr::new("css")) { + let res = try_none!(minifier::css::minify(contents).ok(), &dst); + fs.write(dst, res.as_bytes()) + } else { + fs.write(dst, minifier::js::minify(contents).as_bytes()) + } + } else { + fs.write(dst, contents.as_bytes()) + } +} From afb8220d0af762189168a0f0b4f5c385f5a630e5 Mon Sep 17 00:00:00 2001 From: Nicholas-Baron Date: Sun, 14 Feb 2021 14:11:55 -0800 Subject: [PATCH 55/62] Corrected imports for render tests and mod files Due to a rebase, some edits were needed in the mod file. --- src/librustdoc/html/render/mod.rs | 2 -- src/librustdoc/html/render/tests.rs | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 1db081b181f9b..50cae50c2c3eb 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1,5 +1,3 @@ -// ignore-tidy-filelength - //! Rustdoc's HTML rendering module. //! //! This modules contains the bulk of the logic necessary for rendering a diff --git a/src/librustdoc/html/render/tests.rs b/src/librustdoc/html/render/tests.rs index 224c794fb3b4a..3175fbe5666d8 100644 --- a/src/librustdoc/html/render/tests.rs +++ b/src/librustdoc/html/render/tests.rs @@ -1,4 +1,7 @@ -use super::*; +use std::cmp::Ordering; + +use super::print_item::compare_names; +use super::{AllTypes, Buffer}; #[test] fn test_compare_names() { From dbdaa1248614790d8a2b6ea4ccd032456bfc6466 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 1 Mar 2021 20:07:56 -0500 Subject: [PATCH 56/62] Update rustdoc documentation - Remove most of the information about passes. Passes are deprecated. - Add `--document-private-items`; it was missing before. - Update `--output-format json`; it was very outdated. - Note that `--input-format` is deprecated. - Move deprecated options to the very end. - Move `passes.html` to the end of the table of contents. Ideally it would be removed altogether, but that causes mdbook not to generate the docs. --- src/doc/rustdoc/src/SUMMARY.md | 2 +- src/doc/rustdoc/src/command-line-arguments.md | 86 ++++++++---------- src/doc/rustdoc/src/passes.md | 87 ++----------------- src/doc/rustdoc/src/unstable-features.md | 24 +++++ 4 files changed, 67 insertions(+), 132 deletions(-) diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md index 943aa09f678d7..ae94527e2b4ca 100644 --- a/src/doc/rustdoc/src/SUMMARY.md +++ b/src/doc/rustdoc/src/SUMMARY.md @@ -8,7 +8,7 @@ - [Documentation tests](documentation-tests.md) - [Linking to items by name](linking-to-items-by-name.md) - [Lints](lints.md) -- [Passes](passes.md) - [Advanced features](advanced-features.md) - [Unstable features](unstable-features.md) +- [Passes](passes.md) - [References](references.md) diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index 0302fbecb6ed0..2e4016e24bc3f 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -57,23 +57,6 @@ release: 1.17.0 LLVM version: 3.9 ``` -## `-r`/`--input-format`: input format - -This flag is currently ignored; the idea is that `rustdoc` would support various -input formats, and you could specify them via this flag. - -Rustdoc only supports Rust source code and Markdown input formats. If the -file ends in `.md` or `.markdown`, `rustdoc` treats it as a Markdown file. -Otherwise, it assumes that the input file is Rust. - - -## `-w`/`--output-format`: output format - -This flag is currently ignored; the idea is that `rustdoc` would support -various output formats, and you could specify them via this flag. - -Rustdoc only supports HTML output, and so this flag is redundant today. - ## `-o`/`--output`: output path Using this flag looks like this: @@ -100,6 +83,25 @@ By default, `rustdoc` assumes that the name of your crate is the same name as the `.rs` file. `--crate-name` lets you override this assumption with whatever name you choose. +## `--document-private-items`: Show items that are not public + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs --document-private-items +``` + +By default, `rustdoc` only documents items that are publicly reachable. + +```rust +pub fn public() {} // this item is public and will documented +mod private { // this item is private and will not be documented + pub fn unreachable() {} // this item is public, but unreachable, so it will not be documented +} +``` + +`--document-private-items` documents all items, even if they're not public. + ## `-L`/`--library-path`: where to look for dependencies Using this flag looks like this: @@ -166,38 +168,6 @@ affect that. The arguments to this flag are the same as those for the `-C` flag on rustc. Run `rustc -C help` to get the full list. -## `--passes`: add more rustdoc passes - -Using this flag looks like this: - -```bash -$ rustdoc --passes list -$ rustdoc src/lib.rs --passes strip-priv-imports -``` - -An argument of "list" will print a list of possible "rustdoc passes", and other -arguments will be the name of which passes to run in addition to the defaults. - -For more details on passes, see [the chapter on them](passes.md). - -See also `--no-defaults`. - -## `--no-defaults`: don't run default passes - -Using this flag looks like this: - -```bash -$ rustdoc src/lib.rs --no-defaults -``` - -By default, `rustdoc` will run several passes over your code. This -removes those defaults, allowing you to use `--passes` to specify -exactly which passes you want. - -For more details on passes, see [the chapter on them](passes.md). - -See also `--passes`. - ## `--test`: run code examples as tests Using this flag looks like this: @@ -429,3 +399,21 @@ If you specify `@path` on the command-line, then it will open `path` and read command line options from it. These options are one per line; a blank line indicates an empty option. The file can use Unix or Windows style line endings, and must be encoded as UTF-8. + +## `--passes`: add more rustdoc passes + +This flag is **deprecated**. +For more details on passes, see [the chapter on them](passes.md). + +## `--no-defaults`: don't run default passes + +This flag is **deprecated**. +For more details on passes, see [the chapter on them](passes.md). + +## `-r`/`--input-format`: input format + +This flag is **deprecated** and **has no effect**. + +Rustdoc only supports Rust source code and Markdown input formats. If the +file ends in `.md` or `.markdown`, `rustdoc` treats it as a Markdown file. +Otherwise, it assumes that the input file is Rust. diff --git a/src/doc/rustdoc/src/passes.md b/src/doc/rustdoc/src/passes.md index 140b832f19a54..c3c3fd3068ec4 100644 --- a/src/doc/rustdoc/src/passes.md +++ b/src/doc/rustdoc/src/passes.md @@ -3,86 +3,9 @@ Rustdoc has a concept called "passes". These are transformations that `rustdoc` runs on your documentation before producing its final output. -In addition to the passes below, check out the docs for these flags: +Customizing passes is **deprecated**. The available passes are not considered stable and may +change in any release. -* [`--passes`](command-line-arguments.md#--passes-add-more-rustdoc-passes) -* [`--no-defaults`](command-line-arguments.md#--no-defaults-dont-run-default-passes) - -## Default passes - -By default, rustdoc will run some passes, namely: - -* `strip-hidden` -* `strip-private` -* `collapse-docs` -* `unindent-comments` - -However, `strip-private` implies `strip-priv-imports`, and so effectively, -all passes are run by default. - -## `strip-hidden` - -This pass implements the `#[doc(hidden)]` attribute. When this pass runs, it -checks each item, and if it is annotated with this attribute, it removes it -from `rustdoc`'s output. - -Without this pass, these items will remain in the output. - -## `unindent-comments` - -When you write a doc comment like this: - -```rust,no_run -/// This is a documentation comment. -# fn f() {} -``` - -There's a space between the `///` and that `T`. That spacing isn't intended -to be a part of the output; it's there for humans, to help separate the doc -comment syntax from the text of the comment. This pass is what removes that -space. - -The exact rules are left under-specified so that we can fix issues that we find. - -Without this pass, the exact number of spaces is preserved. - -## `collapse-docs` - -With this pass, multiple `#[doc]` attributes are converted into one single -documentation string. - -For example: - -```rust,no_run -#[doc = "This is the first line."] -#[doc = "This is the second line."] -# fn f() {} -``` - -Gets collapsed into a single doc string of - -```text -This is the first line. -This is the second line. -``` - -## `strip-private` - -This removes documentation for any non-public items, so for example: - -```rust,no_run -/// These are private docs. -struct Private; - -/// These are public docs. -pub struct Public; -``` - -This pass removes the docs for `Private`, since they're not public. - -This pass implies `strip-priv-imports`. - -## `strip-priv-imports` - -This is the same as `strip-private`, but for `extern crate` and `use` -statements instead of items. +In the past the most common use case for customizing passes was to omit the `strip-private` pass. +You can do this more easily, and without risk of the pass being changed, by passing +[`--document-private-items`](./unstable-features.md#--document-private-items). diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index b43070510413a..7d1845dc9578e 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -340,6 +340,30 @@ Some methodology notes about what rustdoc counts in this metric: Public items that are not documented can be seen with the built-in `missing_docs` lint. Private items that are not documented can be seen with Clippy's `missing_docs_in_private_items` lint. +## `-w`/`--output-format`: output format + +When using +[`--show-coverage`](https://doc.rust-lang.org/nightly/rustdoc/unstable-features.html#--show-coverage-get-statistics-about-code-documentation-coverage), +passing `--output-format json` will display the coverage information in JSON format. For example, +here is the JSON for a file with one documented item and one undocumented item: + +```rust +/// This item has documentation +pub fn foo() {} + +pub fn no_documentation() {} +``` + +```json +{"no_std.rs":{"total":3,"with_docs":1,"total_examples":3,"with_examples":0}} +``` + +Note that the third item is the crate root, which in this case is undocumented. + +When not using `--show-coverage`, `--output-format json` emits documentation in the experimental +[JSON format](https://github.com/rust-lang/rfcs/pull/2963). `--output-format html` has no effect, +and is also accepted on stable toolchains. + ### `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests Using this flag looks like this: From 4b30625094eec6b339c83bb41b92eb206df7280d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 13 Feb 2021 21:44:42 +0100 Subject: [PATCH 57/62] Don't warn for `missing_doc_examples` when item is #[doc(hidden)] --- src/librustdoc/passes/doc_test_lints.rs | 11 ++++++++++- src/librustdoc/visit_ast.rs | 26 ++++++++++++------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs index 81104236314d9..19815c7cd5f75 100644 --- a/src/librustdoc/passes/doc_test_lints.rs +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -9,8 +9,10 @@ use crate::clean::*; use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString}; +use crate::visit_ast::inherits_doc_hidden; use rustc_middle::lint::LintLevelSource; use rustc_session::lint; +use rustc_span::symbol::sym; crate const CHECK_PRIVATE_ITEMS_DOC_TESTS: Pass = Pass { name: "check-private-items-doc-tests", @@ -68,6 +70,11 @@ crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> boo return false; } let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_local()); + if cx.tcx.hir().attrs(hir_id).lists(sym::doc).has_word(sym::hidden) + || inherits_doc_hidden(cx, hir_id) + { + return false; + } let (level, source) = cx.tcx.lint_level_at_node(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id); level != lint::Level::Allow || matches!(source, LintLevelSource::Default) } @@ -86,7 +93,9 @@ crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None); if tests.found_tests == 0 && cx.tcx.sess.is_nightly_build() { - if should_have_doc_example(cx, &item) { + if cx.renderinfo.borrow().access_levels.is_public(item.def_id) + && should_have_doc_example(cx, &item) + { debug!("reporting error for {:?} (hir_id={:?})", item, hir_id); let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); cx.tcx.struct_span_lint_hir( diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 5da7d2f1e9b84..b9b2be1db802e 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -29,6 +29,19 @@ fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec { std::iter::once(crate_name).chain(relative).collect() } +crate fn inherits_doc_hidden(cx: &core::DocContext<'_>, mut node: hir::HirId) -> bool { + while let Some(id) = cx.tcx.hir().get_enclosing_scope(node) { + node = id; + if cx.tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) { + return true; + } + if node == hir::CRATE_HIR_ID { + break; + } + } + false +} + // Also, is there some reason that this doesn't use the 'visit' // framework from syntax?. @@ -158,19 +171,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { om: &mut Module<'tcx>, please_inline: bool, ) -> bool { - fn inherits_doc_hidden(cx: &core::DocContext<'_>, mut node: hir::HirId) -> bool { - while let Some(id) = cx.tcx.hir().get_enclosing_scope(node) { - node = id; - if cx.tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) { - return true; - } - if node == hir::CRATE_HIR_ID { - break; - } - } - false - } - debug!("maybe_inline_local res: {:?}", res); let tcx = self.cx.tcx; From 91095b1be5b1befb545dc3a303e029f408aba4ee Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 13 Feb 2021 21:45:15 +0100 Subject: [PATCH 58/62] Update missing code example test --- .../lint-missing-doc-code-example.rs | 21 +++++++++++++++---- .../lint-missing-doc-code-example.stderr | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs index 8d727b0d0b550..41e8847792694 100644 --- a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs +++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs @@ -12,16 +12,16 @@ /// ``` /// println!("hello"); /// ``` -fn test() { +pub fn test() { } #[allow(missing_docs)] -mod module1 { //~ ERROR +pub mod module1 { //~ ERROR } #[allow(rustdoc::missing_doc_code_examples)] /// doc -mod module2 { +pub mod module2 { /// doc pub fn test() {} @@ -63,9 +63,22 @@ pub enum Enum { /// Doc //~^ ERROR #[repr(C)] -union Union { +pub union Union { /// Doc, but no code example and it's fine! a: i32, /// Doc, but no code example and it's fine! b: f32, } + + +#[doc(hidden)] +pub mod foo { + pub fn bar() {} +} + +fn babar() {} + + +mod fofoo { + pub fn tadam() {} +} diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr index 370c577f85d8f..3715797854281 100644 --- a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr +++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr @@ -1,7 +1,7 @@ error: missing code example in this documentation --> $DIR/lint-missing-doc-code-example.rs:19:1 | -LL | / mod module1 { +LL | / pub mod module1 { LL | | } | |_^ | From 186f13914a825039081795d382eb75c6a9f981ae Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 15 Feb 2021 14:17:43 +0100 Subject: [PATCH 59/62] Move visibility check inside the should_have_doc_example function --- src/librustdoc/passes/doc_test_lints.rs | 34 ++++++++++++------------- src/librustdoc/visit_ast.rs | 3 --- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs index 19815c7cd5f75..cf8966fadf60b 100644 --- a/src/librustdoc/passes/doc_test_lints.rs +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -53,20 +53,22 @@ impl crate::doctest::Tester for Tests { } crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool { - if matches!( - *item.kind, - clean::StructFieldItem(_) - | clean::VariantItem(_) - | clean::AssocConstItem(_, _) - | clean::AssocTypeItem(_, _) - | clean::TypedefItem(_, _) - | clean::StaticItem(_) - | clean::ConstantItem(_) - | clean::ExternCrateItem(_, _) - | clean::ImportItem(_) - | clean::PrimitiveItem(_) - | clean::KeywordItem(_) - ) { + if !cx.renderinfo.borrow().access_levels.is_public(item.def_id) + || matches!( + *item.kind, + clean::StructFieldItem(_) + | clean::VariantItem(_) + | clean::AssocConstItem(_, _) + | clean::AssocTypeItem(_, _) + | clean::TypedefItem(_, _) + | clean::StaticItem(_) + | clean::ConstantItem(_) + | clean::ExternCrateItem(_, _) + | clean::ImportItem(_) + | clean::PrimitiveItem(_) + | clean::KeywordItem(_) + ) + { return false; } let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_local()); @@ -93,9 +95,7 @@ crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None); if tests.found_tests == 0 && cx.tcx.sess.is_nightly_build() { - if cx.renderinfo.borrow().access_levels.is_public(item.def_id) - && should_have_doc_example(cx, &item) - { + if should_have_doc_example(cx, &item) { debug!("reporting error for {:?} (hir_id={:?})", item, hir_id); let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); cx.tcx.struct_span_lint_hir( diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index b9b2be1db802e..875be028e848d 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -35,9 +35,6 @@ crate fn inherits_doc_hidden(cx: &core::DocContext<'_>, mut node: hir::HirId) -> if cx.tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) { return true; } - if node == hir::CRATE_HIR_ID { - break; - } } false } From ad30c39918ca4191cb4b0191cfb56b97d9bef330 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 23 Feb 2021 15:19:13 +0100 Subject: [PATCH 60/62] Pass TyCtxt directly instead of DocContext in librustdoc::visit_ast::inherits_doc_hidden --- src/librustdoc/passes/doc_test_lints.rs | 2 +- src/librustdoc/visit_ast.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs index cf8966fadf60b..2ce43dd9ff818 100644 --- a/src/librustdoc/passes/doc_test_lints.rs +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -73,7 +73,7 @@ crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> boo } let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_local()); if cx.tcx.hir().attrs(hir_id).lists(sym::doc).has_word(sym::hidden) - || inherits_doc_hidden(cx, hir_id) + || inherits_doc_hidden(cx.tcx, hir_id) { return false; } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 875be028e848d..ba6bb359b9135 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -29,10 +29,10 @@ fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec { std::iter::once(crate_name).chain(relative).collect() } -crate fn inherits_doc_hidden(cx: &core::DocContext<'_>, mut node: hir::HirId) -> bool { - while let Some(id) = cx.tcx.hir().get_enclosing_scope(node) { +crate fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: hir::HirId) -> bool { + while let Some(id) = tcx.hir().get_enclosing_scope(node) { node = id; - if cx.tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) { + if tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) { return true; } } @@ -209,7 +209,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }; let is_private = !self.cx.cache.access_levels.is_public(res_did); - let is_hidden = inherits_doc_hidden(self.cx, res_hir_id); + let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id); // Only inline if requested or if the item would otherwise be stripped. if (!please_inline && !is_private && !is_hidden) || is_no_inline { From e4287992a7b37ed0ae9a9158b1d7bfb4509d2164 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 23 Feb 2021 21:53:28 +0100 Subject: [PATCH 61/62] No more need for borrow call --- src/librustdoc/passes/doc_test_lints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs index 2ce43dd9ff818..c97684c7e01b9 100644 --- a/src/librustdoc/passes/doc_test_lints.rs +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -53,7 +53,7 @@ impl crate::doctest::Tester for Tests { } crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool { - if !cx.renderinfo.borrow().access_levels.is_public(item.def_id) + if !cx.renderinfo.access_levels.is_public(item.def_id) || matches!( *item.kind, clean::StructFieldItem(_) From 1683cb12e44e433bc75ab3ffa2cfc65c6d0bdd24 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 2 Mar 2021 11:59:37 +0100 Subject: [PATCH 62/62] Use cache access levels --- src/librustdoc/passes/doc_test_lints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs index c97684c7e01b9..ede9e18a511a8 100644 --- a/src/librustdoc/passes/doc_test_lints.rs +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -53,7 +53,7 @@ impl crate::doctest::Tester for Tests { } crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool { - if !cx.renderinfo.access_levels.is_public(item.def_id) + if !cx.cache.access_levels.is_public(item.def_id) || matches!( *item.kind, clean::StructFieldItem(_)