From 77f564a42cb3e72c296bb42bdd88bc64158af805 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 12 Jun 2022 14:10:18 -0700 Subject: [PATCH 01/14] Add `IsTerminal` trait to determine if a descriptor or handle is a terminal The UNIX and WASI implementations use `isatty`. The Windows implementation uses the same logic the `atty` crate uses, including the hack needed to detect msys terminals. Implement this trait for `File` and for `Stdin`/`Stdout`/`Stderr` and their locked counterparts on all platforms. On UNIX and WASI, implement it for `BorrowedFd`/`OwnedFd`. On Windows, implement it for `BorrowedHandle`/`OwnedHandle`. Based on https://github.com/rust-lang/rust/pull/91121 Co-authored-by: Matt Wilkinson --- library/std/src/io/mod.rs | 2 + library/std/src/io/stdio.rs | 29 ++++++++++++ library/std/src/lib.rs | 1 + library/std/src/os/fd/owned.rs | 17 +++++++ library/std/src/os/mod.rs | 2 +- library/std/src/os/windows/io/handle.rs | 17 +++++++ library/std/src/sys/unix/io.rs | 7 +++ library/std/src/sys/unsupported/io.rs | 4 ++ library/std/src/sys/wasi/io.rs | 7 +++ library/std/src/sys/windows/c.rs | 8 ++++ library/std/src/sys/windows/io.rs | 59 +++++++++++++++++++++++++ library/test/src/cli.rs | 4 +- library/test/src/helpers/isatty.rs | 32 -------------- library/test/src/helpers/mod.rs | 1 - library/test/src/lib.rs | 1 + 15 files changed, 155 insertions(+), 36 deletions(-) delete mode 100644 library/test/src/helpers/isatty.rs diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 01a3873c75cec..90251eb4b02e5 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -265,6 +265,8 @@ pub use self::buffered::WriterPanicked; #[unstable(feature = "internal_output_capture", issue = "none")] #[doc(no_inline, hidden)] pub use self::stdio::set_output_capture; +#[unstable(feature = "is_terminal", issue = "98070")] +pub use self::stdio::IsTerminal; #[unstable(feature = "print_internals", issue = "none")] pub use self::stdio::{_eprint, _print}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 2dc12a18a8a66..3c3fa9654bbea 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -7,6 +7,7 @@ use crate::io::prelude::*; use crate::cell::{Cell, RefCell}; use crate::fmt; +use crate::fs::File; use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines}; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{Arc, Mutex, MutexGuard, OnceLock}; @@ -1019,6 +1020,34 @@ where } } +/// Trait to determine if a descriptor/handle refers to a terminal/tty. +#[unstable(feature = "is_terminal", issue = "98070")] +pub trait IsTerminal: crate::sealed::Sealed { + /// Returns `true` if the descriptor/handle refers to a terminal/tty. + /// + /// On platforms where Rust does not know how to detect a terminal yet, this will return + /// `false`. This will also return `false` if an unexpected error occurred, such as from + /// passing an invalid file descriptor. + fn is_terminal(&self) -> bool; +} + +macro_rules! impl_is_terminal { + ($($t:ty),*$(,)?) => {$( + #[unstable(feature = "sealed", issue = "none")] + impl crate::sealed::Sealed for $t {} + + #[unstable(feature = "is_terminal", issue = "98070")] + impl IsTerminal for $t { + #[inline] + fn is_terminal(&self) -> bool { + crate::sys::io::is_terminal(self) + } + } + )*} +} + +impl_is_terminal!(File, Stdin, StdinLock<'_>, Stdout, StdoutLock<'_>, Stderr, StderrLock<'_>); + #[unstable( feature = "print_internals", reason = "implementation detail which may disappear or be replaced at any time", diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f13500de01431..d236c1214e8c4 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -252,6 +252,7 @@ #![feature(dropck_eyepatch)] #![feature(exhaustive_patterns)] #![feature(intra_doc_pointers)] +#![feature(is_terminal)] #![cfg_attr(bootstrap, feature(label_break_value))] #![feature(lang_items)] #![feature(let_chains)] diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 71e33fb9ed864..8c44415190748 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -192,6 +192,23 @@ impl fmt::Debug for OwnedFd { } } +macro_rules! impl_is_terminal { + ($($t:ty),*$(,)?) => {$( + #[unstable(feature = "sealed", issue = "none")] + impl crate::sealed::Sealed for $t {} + + #[unstable(feature = "is_terminal", issue = "98070")] + impl crate::io::IsTerminal for $t { + #[inline] + fn is_terminal(&self) -> bool { + crate::sys::io::is_terminal(self) + } + } + )*} +} + +impl_is_terminal!(BorrowedFd<'_>, OwnedFd); + /// A trait to borrow the file descriptor from an underlying object. /// /// This is only available on unix platforms and must be imported in order to diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 18c64b5100764..380950b0ac37f 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -147,7 +147,7 @@ pub mod solid; pub mod vxworks; #[cfg(any(unix, target_os = "wasi", doc))] -mod fd; +pub(crate) mod fd; #[cfg(any(target_os = "linux", target_os = "android", doc))] mod net; diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 16cc8fa2783ee..1dfecc57338a7 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -384,6 +384,23 @@ impl fmt::Debug for OwnedHandle { } } +macro_rules! impl_is_terminal { + ($($t:ty),*$(,)?) => {$( + #[unstable(feature = "sealed", issue = "none")] + impl crate::sealed::Sealed for $t {} + + #[unstable(feature = "is_terminal", issue = "98070")] + impl crate::io::IsTerminal for $t { + #[inline] + fn is_terminal(&self) -> bool { + crate::sys::io::is_terminal(self) + } + } + )*} +} + +impl_is_terminal!(BorrowedHandle<'_>, OwnedHandle); + /// A trait to borrow the handle from an underlying object. #[stable(feature = "io_safety", since = "1.63.0")] pub trait AsHandle { diff --git a/library/std/src/sys/unix/io.rs b/library/std/src/sys/unix/io.rs index deb5ee76bd035..8d189059d3641 100644 --- a/library/std/src/sys/unix/io.rs +++ b/library/std/src/sys/unix/io.rs @@ -1,4 +1,6 @@ use crate::marker::PhantomData; +use crate::os::fd::owned::AsFd; +use crate::os::fd::raw::AsRawFd; use crate::slice; use libc::{c_void, iovec}; @@ -74,3 +76,8 @@ impl<'a> IoSliceMut<'a> { unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + unsafe { libc::isatty(fd.as_raw_fd()) != 0 } +} diff --git a/library/std/src/sys/unsupported/io.rs b/library/std/src/sys/unsupported/io.rs index d5f475b4310fd..82610ffab7e1e 100644 --- a/library/std/src/sys/unsupported/io.rs +++ b/library/std/src/sys/unsupported/io.rs @@ -45,3 +45,7 @@ impl<'a> IoSliceMut<'a> { self.0 } } + +pub fn is_terminal(_: &T) -> bool { + false +} diff --git a/library/std/src/sys/wasi/io.rs b/library/std/src/sys/wasi/io.rs index ee017d13a4ca0..bd9f8872333e2 100644 --- a/library/std/src/sys/wasi/io.rs +++ b/library/std/src/sys/wasi/io.rs @@ -1,6 +1,8 @@ #![deny(unsafe_op_in_unsafe_fn)] use crate::marker::PhantomData; +use crate::os::fd::owned::AsFd; +use crate::os::fd::raw::AsRawFd; use crate::slice; #[derive(Copy, Clone)] @@ -71,3 +73,8 @@ impl<'a> IoSliceMut<'a> { unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } } } + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + unsafe { libc::isatty(fd.as_raw_fd()) != 0 } +} diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 89d0ab59be89f..ae54626b85c1c 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -126,6 +126,8 @@ pub const SECURITY_SQOS_PRESENT: DWORD = 0x00100000; pub const FIONBIO: c_ulong = 0x8004667e; +pub const MAX_PATH: usize = 260; + #[repr(C)] #[derive(Copy)] pub struct WIN32_FIND_DATAW { @@ -534,6 +536,12 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER { /// NB: Use carefully! In general using this as a reference is likely to get the /// provenance wrong for the `PathBuffer` field! +#[repr(C)] +pub struct FILE_NAME_INFO { + pub FileNameLength: DWORD, + pub FileName: [WCHAR; 1], +} + #[repr(C)] pub struct MOUNT_POINT_REPARSE_BUFFER { pub SubstituteNameOffset: c_ushort, diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs index fb06df1f80cda..489d66b06714f 100644 --- a/library/std/src/sys/windows/io.rs +++ b/library/std/src/sys/windows/io.rs @@ -1,6 +1,10 @@ use crate::marker::PhantomData; +use crate::mem::size_of; +use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; use crate::slice; use crate::sys::c; +use core; +use libc; #[derive(Copy, Clone)] #[repr(transparent)] @@ -78,3 +82,58 @@ impl<'a> IoSliceMut<'a> { unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.len as usize) } } } + +pub fn is_terminal(h: &impl AsHandle) -> bool { + unsafe { handle_is_console(h.as_handle()) } +} + +unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { + let handle = handle.as_raw_handle(); + + let mut out = 0; + if c::GetConsoleMode(handle, &mut out) != 0 { + // False positives aren't possible. If we got a console then we definitely have a console. + return true; + } + + // At this point, we *could* have a false negative. We can determine that this is a true + // negative if we can detect the presence of a console on any of the standard I/O streams. If + // another stream has a console, then we know we're in a Windows console and can therefore + // trust the negative. + for std_handle in [c::STD_INPUT_HANDLE, c::STD_OUTPUT_HANDLE, c::STD_ERROR_HANDLE] { + let std_handle = c::GetStdHandle(std_handle); + if std_handle != handle && c::GetConsoleMode(std_handle, &mut out) != 0 { + return false; + } + } + + // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty. + msys_tty_on(handle) +} + +unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { + let size = size_of::() + c::MAX_PATH * size_of::(); + let mut name_info_bytes = vec![0u8; size]; + let res = c::GetFileInformationByHandleEx( + handle, + c::FileNameInfo, + name_info_bytes.as_mut_ptr() as *mut libc::c_void, + size as u32, + ); + if res == 0 { + return false; + } + let name_info: &c::FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const c::FILE_NAME_INFO); + let s = core::slice::from_raw_parts( + name_info.FileName.as_ptr(), + name_info.FileNameLength as usize / 2, + ); + let name = String::from_utf16_lossy(s); + // This checks whether 'pty' exists in the file name, which indicates that + // a pseudo-terminal is attached. To mitigate against false positives + // (e.g., an actual file name that contains 'pty'), we also require that + // either the strings 'msys-' or 'cygwin-' are in the file name as well.) + let is_msys = name.contains("msys-") || name.contains("cygwin-"); + let is_pty = name.contains("-pty"); + is_msys && is_pty +} diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index f981b9c495476..8be32183fe780 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -3,9 +3,9 @@ use std::env; use std::path::PathBuf; -use super::helpers::isatty; use super::options::{ColorConfig, Options, OutputFormat, RunIgnored}; use super::time::TestTimeOptions; +use std::io::{self, IsTerminal}; #[derive(Debug)] pub struct TestOpts { @@ -32,7 +32,7 @@ pub struct TestOpts { impl TestOpts { pub fn use_color(&self) -> bool { match self.color { - ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(), + ColorConfig::AutoColor => !self.nocapture && io::stdout().is_terminal(), ColorConfig::AlwaysColor => true, ColorConfig::NeverColor => false, } diff --git a/library/test/src/helpers/isatty.rs b/library/test/src/helpers/isatty.rs deleted file mode 100644 index 874ecc3764572..0000000000000 --- a/library/test/src/helpers/isatty.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Helper module which provides a function to test -//! if stdout is a tty. - -cfg_if::cfg_if! { - if #[cfg(unix)] { - pub fn stdout_isatty() -> bool { - unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 } - } - } else if #[cfg(windows)] { - pub fn stdout_isatty() -> bool { - type DWORD = u32; - type BOOL = i32; - type HANDLE = *mut u8; - type LPDWORD = *mut u32; - const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD; - extern "system" { - fn GetStdHandle(which: DWORD) -> HANDLE; - fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL; - } - unsafe { - let handle = GetStdHandle(STD_OUTPUT_HANDLE); - let mut out = 0; - GetConsoleMode(handle, &mut out) != 0 - } - } - } else { - // FIXME: Implement isatty on SGX - pub fn stdout_isatty() -> bool { - false - } - } -} diff --git a/library/test/src/helpers/mod.rs b/library/test/src/helpers/mod.rs index 049cadf86a6d0..6f366a911e8cd 100644 --- a/library/test/src/helpers/mod.rs +++ b/library/test/src/helpers/mod.rs @@ -3,6 +3,5 @@ pub mod concurrency; pub mod exit_code; -pub mod isatty; pub mod metrics; pub mod shuffle; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 3b7193adcc758..f595e2a74d4ad 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -17,6 +17,7 @@ #![doc(test(attr(deny(warnings))))] #![feature(bench_black_box)] #![feature(internal_output_capture)] +#![feature(is_terminal)] #![feature(staged_api)] #![feature(process_exitcode_internals)] #![feature(test)] From 0fb1bf0bddbd776aafa9d98af8b46aedbafa8244 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 19 Jun 2022 18:11:39 -0700 Subject: [PATCH 02/14] Make is_terminal fail fast if a process has no console at all If a process has no console, it'll have NULL in place of a console handle, so return early with `false` in that case without making any OS calls. --- library/std/src/sys/windows/io.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs index 489d66b06714f..0ec619315633e 100644 --- a/library/std/src/sys/windows/io.rs +++ b/library/std/src/sys/windows/io.rs @@ -90,6 +90,11 @@ pub fn is_terminal(h: &impl AsHandle) -> bool { unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { let handle = handle.as_raw_handle(); + // A null handle means the process has no console. + if handle.is_null() { + return false; + } + let mut out = 0; if c::GetConsoleMode(handle, &mut out) != 0 { // False positives aren't possible. If we got a console then we definitely have a console. @@ -102,7 +107,10 @@ unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { // trust the negative. for std_handle in [c::STD_INPUT_HANDLE, c::STD_OUTPUT_HANDLE, c::STD_ERROR_HANDLE] { let std_handle = c::GetStdHandle(std_handle); - if std_handle != handle && c::GetConsoleMode(std_handle, &mut out) != 0 { + if !std_handle.is_null() + && std_handle != handle + && c::GetConsoleMode(std_handle, &mut out) != 0 + { return false; } } From aa42c4d86575fc33d41304ab90557f930d5868b2 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 24 Aug 2022 15:32:16 +0200 Subject: [PATCH 03/14] Rewrite FILE_NAME_INFO handling to avoid enlarging slice reference Rather than referencing a slice's pointer and then creating a new slice with a longer length, offset from the base structure pointer instead. This makes some choices of Rust semantics happier. --- library/std/src/sys/windows/io.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs index 0ec619315633e..7e7518b6d8905 100644 --- a/library/std/src/sys/windows/io.rs +++ b/library/std/src/sys/windows/io.rs @@ -132,10 +132,10 @@ unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { return false; } let name_info: &c::FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const c::FILE_NAME_INFO); - let s = core::slice::from_raw_parts( - name_info.FileName.as_ptr(), - name_info.FileNameLength as usize / 2, - ); + let name_len = name_info.FileNameLength as usize / 2; + // Offset to get the `FileName` field. + let name_ptr = name_info_bytes.as_ptr().offset(size_of::() as isize).cast::(); + let s = core::slice::from_raw_parts(name_ptr, name_len); let name = String::from_utf16_lossy(s); // This checks whether 'pty' exists in the file name, which indicates that // a pseudo-terminal is attached. To mitigate against false positives From 170a8de82b818c98f43e7311a489a938b1297bc7 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 9 Sep 2022 15:01:26 +0200 Subject: [PATCH 04/14] Use Align8 to avoid misalignment if the allocator or Vec doesn't align allocations --- library/std/src/sys/windows/io.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs index 7e7518b6d8905..0879ef199338e 100644 --- a/library/std/src/sys/windows/io.rs +++ b/library/std/src/sys/windows/io.rs @@ -2,7 +2,7 @@ use crate::marker::PhantomData; use crate::mem::size_of; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; use crate::slice; -use crate::sys::c; +use crate::sys::{c, Align8}; use core; use libc; @@ -120,21 +120,21 @@ unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { } unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { - let size = size_of::() + c::MAX_PATH * size_of::(); - let mut name_info_bytes = vec![0u8; size]; + const SIZE: usize = size_of::() + c::MAX_PATH * size_of::(); + let mut name_info_bytes = Align8([0u8; SIZE]); let res = c::GetFileInformationByHandleEx( handle, c::FileNameInfo, - name_info_bytes.as_mut_ptr() as *mut libc::c_void, - size as u32, + name_info_bytes.0.as_mut_ptr() as *mut libc::c_void, + SIZE as u32, ); if res == 0 { return false; } - let name_info: &c::FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const c::FILE_NAME_INFO); + let name_info: &c::FILE_NAME_INFO = &*(name_info_bytes.0.as_ptr() as *const c::FILE_NAME_INFO); let name_len = name_info.FileNameLength as usize / 2; // Offset to get the `FileName` field. - let name_ptr = name_info_bytes.as_ptr().offset(size_of::() as isize).cast::(); + let name_ptr = name_info_bytes.0.as_ptr().offset(size_of::() as isize).cast::(); let s = core::slice::from_raw_parts(name_ptr, name_len); let name = String::from_utf16_lossy(s); // This checks whether 'pty' exists in the file name, which indicates that From 8823634db8c4ee65b4a3d47ccb1d81891e33d0de Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 8 Aug 2022 00:05:20 +0200 Subject: [PATCH 05/14] Manually cleanup token stream when macro expansion aborts. --- .../rustc_parse/src/parser/attr_wrapper.rs | 45 ++++++++++++------- src/test/ui/macros/syntax-error-recovery.rs | 17 +++++++ .../ui/macros/syntax-error-recovery.stderr | 24 ++++++++++ 3 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 src/test/ui/macros/syntax-error-recovery.rs create mode 100644 src/test/ui/macros/syntax-error-recovery.stderr diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 5fdafd187c660..34021c40ba967 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -442,23 +442,38 @@ fn make_token_stream( } token_and_spacing = iter.next(); } - let mut final_buf = stack.pop().expect("Missing final buf!"); - if break_last_token { - let last_token = final_buf.inner.pop().unwrap(); - if let AttrTokenTree::Token(last_token, spacing) = last_token { - let unglued_first = last_token.kind.break_two_token_op().unwrap().0; - - // An 'unglued' token is always two ASCII characters - let mut first_span = last_token.span.shrink_to_lo(); - first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1)); - - final_buf + while let Some(FrameData { open_delim_sp, mut inner }) = stack.pop() { + // A former macro expansion could give us malformed tokens. + // In that case, manually close all open delimitors so downstream users + // don't ICE on them. + if let Some((delim, open_sp)) = open_delim_sp { + let dspan = DelimSpan::from_pair(open_sp, rustc_span::DUMMY_SP); + let stream = AttrTokenStream::new(inner); + let delimited = AttrTokenTree::Delimited(dspan, delim, stream); + stack + .last_mut() + .unwrap_or_else(|| panic!("Bottom token frame is missing for recovered token")) .inner - .push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing)); + .push(delimited); } else { - panic!("Unexpected last token {:?}", last_token) + if break_last_token { + let last_token = inner.pop().unwrap(); + if let AttrTokenTree::Token(last_token, spacing) = last_token { + let unglued_first = last_token.kind.break_two_token_op().unwrap().0; + + // An 'unglued' token is always two ASCII characters + let mut first_span = last_token.span.shrink_to_lo(); + first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1)); + + inner + .push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing)); + } else { + panic!("Unexpected last token {:?}", last_token) + } + } + assert!(stack.is_empty(), "Stack should be empty: stack={:?}", stack); + return AttrTokenStream::new(inner); } } - assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack); - AttrTokenStream::new(final_buf.inner) + panic!("Missing final buf!") } diff --git a/src/test/ui/macros/syntax-error-recovery.rs b/src/test/ui/macros/syntax-error-recovery.rs new file mode 100644 index 0000000000000..2b2eec047058f --- /dev/null +++ b/src/test/ui/macros/syntax-error-recovery.rs @@ -0,0 +1,17 @@ +macro_rules! values { + ($($token:ident($value:literal) $(as $inner:ty)? => $attr:meta,)*) => { + #[derive(Debug)] + pub enum TokenKind { + $( + #[$attr] + $token $($inner)? = $value, + )* + } + }; +} +//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)` +//~| ERROR macro expansion ignores token `(String)` and any following + +values!(STRING(1) as (String) => cfg(test),); + +fn main() {} diff --git a/src/test/ui/macros/syntax-error-recovery.stderr b/src/test/ui/macros/syntax-error-recovery.stderr new file mode 100644 index 0000000000000..1d9ce110d7b6e --- /dev/null +++ b/src/test/ui/macros/syntax-error-recovery.stderr @@ -0,0 +1,24 @@ +error: expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)` + --> $DIR/syntax-error-recovery.rs:7:26 + | +LL | $token $($inner)? = $value, + | ^^^^^^ expected one of `(`, `,`, `=`, `{`, or `}` +... +LL | values!(STRING(1) as (String) => cfg(test),); + | -------------------------------------------- in this macro invocation + | + = note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: macro expansion ignores token `(String)` and any following + --> $DIR/syntax-error-recovery.rs:7:26 + | +LL | $token $($inner)? = $value, + | ^^^^^^ +... +LL | values!(STRING(1) as (String) => cfg(test),); + | -------------------------------------------- caused by the macro expansion here + | + = note: the usage of `values!` is likely invalid in item context + +error: aborting due to 2 previous errors + From cb5ea8d0b67e4a46f9f30aa93107035d9a1dadf0 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 8 Aug 2022 19:17:37 +0200 Subject: [PATCH 06/14] Emit an error instead of reconstructing token stream. --- compiler/rustc_builtin_macros/src/cfg_eval.rs | 64 +++++++++++-------- .../rustc_parse/src/parser/attr_wrapper.rs | 44 ++++--------- src/test/ui/macros/syntax-error-recovery.rs | 1 + .../ui/macros/syntax-error-recovery.stderr | 8 ++- 4 files changed, 58 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index e673dff0dea8e..009f3c783d4c8 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -7,6 +7,7 @@ use rustc_ast::visit::Visitor; use rustc_ast::NodeId; use rustc_ast::{mut_visit, visit}; use rustc_ast::{Attribute, HasAttrs, HasTokens}; +use rustc_errors::PResult; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure; @@ -144,33 +145,34 @@ impl CfgEval<'_, '_> { // the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization // process is lossless, so this process is invisible to proc-macros. - let parse_annotatable_with: fn(&mut Parser<'_>) -> _ = match annotatable { - Annotatable::Item(_) => { - |parser| Annotatable::Item(parser.parse_item(ForceCollect::Yes).unwrap().unwrap()) - } - Annotatable::TraitItem(_) => |parser| { - Annotatable::TraitItem( - parser.parse_trait_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), - ) - }, - Annotatable::ImplItem(_) => |parser| { - Annotatable::ImplItem( - parser.parse_impl_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), - ) - }, - Annotatable::ForeignItem(_) => |parser| { - Annotatable::ForeignItem( - parser.parse_foreign_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), - ) - }, - Annotatable::Stmt(_) => |parser| { - Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes).unwrap().unwrap())) - }, - Annotatable::Expr(_) => { - |parser| Annotatable::Expr(parser.parse_expr_force_collect().unwrap()) - } - _ => unreachable!(), - }; + let parse_annotatable_with: for<'a> fn(&mut Parser<'a>) -> PResult<'a, _> = + match annotatable { + Annotatable::Item(_) => { + |parser| Ok(Annotatable::Item(parser.parse_item(ForceCollect::Yes)?.unwrap())) + } + Annotatable::TraitItem(_) => |parser| { + Ok(Annotatable::TraitItem( + parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap(), + )) + }, + Annotatable::ImplItem(_) => |parser| { + Ok(Annotatable::ImplItem( + parser.parse_impl_item(ForceCollect::Yes)?.unwrap().unwrap(), + )) + }, + Annotatable::ForeignItem(_) => |parser| { + Ok(Annotatable::ForeignItem( + parser.parse_foreign_item(ForceCollect::Yes)?.unwrap().unwrap(), + )) + }, + Annotatable::Stmt(_) => |parser| { + Ok(Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes)?.unwrap()))) + }, + Annotatable::Expr(_) => { + |parser| Ok(Annotatable::Expr(parser.parse_expr_force_collect()?)) + } + _ => unreachable!(), + }; // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`) // to `None`-delimited groups containing the corresponding tokens. This @@ -193,7 +195,13 @@ impl CfgEval<'_, '_> { let mut parser = rustc_parse::stream_to_parser(&self.cfg.sess.parse_sess, orig_tokens, None); parser.capture_cfg = true; - annotatable = parse_annotatable_with(&mut parser); + match parse_annotatable_with(&mut parser) { + Ok(a) => annotatable = a, + Err(mut err) => { + err.emit(); + return Some(annotatable); + } + } // Now that we have our re-parsed `AttrTokenStream`, recursively configuring // our attribute target will correctly the tokens as well. diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 34021c40ba967..86c386b94c834 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -442,38 +442,22 @@ fn make_token_stream( } token_and_spacing = iter.next(); } - while let Some(FrameData { open_delim_sp, mut inner }) = stack.pop() { - // A former macro expansion could give us malformed tokens. - // In that case, manually close all open delimitors so downstream users - // don't ICE on them. - if let Some((delim, open_sp)) = open_delim_sp { - let dspan = DelimSpan::from_pair(open_sp, rustc_span::DUMMY_SP); - let stream = AttrTokenStream::new(inner); - let delimited = AttrTokenTree::Delimited(dspan, delim, stream); - stack - .last_mut() - .unwrap_or_else(|| panic!("Bottom token frame is missing for recovered token")) + let mut final_buf = stack.pop().expect("Missing final buf!"); + if break_last_token { + let last_token = final_buf.inner.pop().unwrap(); + if let AttrTokenTree::Token(last_token, spacing) = last_token { + let unglued_first = last_token.kind.break_two_token_op().unwrap().0; + + // An 'unglued' token is always two ASCII characters + let mut first_span = last_token.span.shrink_to_lo(); + first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1)); + + final_buf .inner - .push(delimited); + .push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing)); } else { - if break_last_token { - let last_token = inner.pop().unwrap(); - if let AttrTokenTree::Token(last_token, spacing) = last_token { - let unglued_first = last_token.kind.break_two_token_op().unwrap().0; - - // An 'unglued' token is always two ASCII characters - let mut first_span = last_token.span.shrink_to_lo(); - first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1)); - - inner - .push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing)); - } else { - panic!("Unexpected last token {:?}", last_token) - } - } - assert!(stack.is_empty(), "Stack should be empty: stack={:?}", stack); - return AttrTokenStream::new(inner); + panic!("Unexpected last token {:?}", last_token) } } - panic!("Missing final buf!") + AttrTokenStream::new(final_buf.inner) } diff --git a/src/test/ui/macros/syntax-error-recovery.rs b/src/test/ui/macros/syntax-error-recovery.rs index 2b2eec047058f..ae6de3c5046cd 100644 --- a/src/test/ui/macros/syntax-error-recovery.rs +++ b/src/test/ui/macros/syntax-error-recovery.rs @@ -13,5 +13,6 @@ macro_rules! values { //~| ERROR macro expansion ignores token `(String)` and any following values!(STRING(1) as (String) => cfg(test),); +//~^ ERROR expected one of `!` or `::`, found `` fn main() {} diff --git a/src/test/ui/macros/syntax-error-recovery.stderr b/src/test/ui/macros/syntax-error-recovery.stderr index 1d9ce110d7b6e..c153b3b910bbe 100644 --- a/src/test/ui/macros/syntax-error-recovery.stderr +++ b/src/test/ui/macros/syntax-error-recovery.stderr @@ -20,5 +20,11 @@ LL | values!(STRING(1) as (String) => cfg(test),); | = note: the usage of `values!` is likely invalid in item context -error: aborting due to 2 previous errors +error: expected one of `!` or `::`, found `` + --> $DIR/syntax-error-recovery.rs:15:9 + | +LL | values!(STRING(1) as (String) => cfg(test),); + | ^^^^^^ expected one of `!` or `::` + +error: aborting due to 3 previous errors From dddfb7db24e66891fec9f2abf2ac1477985f36ad Mon Sep 17 00:00:00 2001 From: hanar3 Date: Wed, 14 Sep 2022 12:19:42 -0300 Subject: [PATCH 07/14] Improve error message for unsupported crate --- compiler/rustc_middle/src/ty/query.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index 5665bb866d46c..d69c28cfda294 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -275,10 +275,11 @@ macro_rules! define_callbacks { fn default() -> Self { Providers { $($name: |_, key| bug!( - "`tcx.{}({:?})` unsupported by its crate; \ - perhaps the `{}` query was never assigned a provider function", + "`tcx.{}({:?})` unsupported for {} crate; \ + perhaps the `{}` query was never assigned a provider function. Queries can be either made to the local crate, or the external crate. This error means you tried to use it for one that's not supported.", stringify!($name), key, + if key.query_crate_is_local() { "local" } : { "external" } , stringify!($name), ),)* } From 0405a5d4e9aca1f1f22d94abca84c281bf1a97ea Mon Sep 17 00:00:00 2001 From: hanar3 Date: Sat, 17 Sep 2022 20:25:37 -0300 Subject: [PATCH 08/14] improve error message for when a query isn't supported --- compiler/rustc_middle/src/ty/query.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index d69c28cfda294..69a54cbc928d5 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -275,11 +275,11 @@ macro_rules! define_callbacks { fn default() -> Self { Providers { $($name: |_, key| bug!( - "`tcx.{}({:?})` unsupported for {} crate; \ - perhaps the `{}` query was never assigned a provider function. Queries can be either made to the local crate, or the external crate. This error means you tried to use it for one that's not supported.", + "`tcx.{}({:?})` is not supported for external or local crate;\n + hint: Queries can be either made to the local crate, or the external crate. This error means you tried to use it for one that's not supported (likely the local crate).\n + If that's not the case, {} was likely never assigned to a provider function.\n", stringify!($name), key, - if key.query_crate_is_local() { "local" } : { "external" } , stringify!($name), ),)* } From 550bd0945fd6b5228206aaf5e8fbf613a77ab16d Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 19 Sep 2022 09:29:12 -0300 Subject: [PATCH 09/14] Simplify rpitit handling on lower_fn_decl --- compiler/rustc_ast_lowering/src/lib.rs | 60 +++++++------------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d2ce6bdb08e58..e9c05eb5f455f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -324,16 +324,10 @@ enum FnDeclKind { } impl FnDeclKind { - fn impl_trait_return_allowed(&self, tcx: TyCtxt<'_>) -> bool { + fn impl_trait_allowed(&self, tcx: TyCtxt<'_>) -> bool { match self { FnDeclKind::Fn | FnDeclKind::Inherent => true, FnDeclKind::Impl if tcx.features().return_position_impl_trait_in_trait => true, - _ => false, - } - } - - fn impl_trait_in_trait_allowed(&self, tcx: TyCtxt<'_>) -> bool { - match self { FnDeclKind::Trait if tcx.features().return_position_impl_trait_in_trait => true, _ => false, } @@ -1698,9 +1692,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { })); let output = if let Some((ret_id, span)) = make_ret_async { - match kind { - FnDeclKind::Trait => { - if !kind.impl_trait_in_trait_allowed(self.tcx) { + if !kind.impl_trait_allowed(self.tcx) { + match kind { + FnDeclKind::Trait | FnDeclKind::Impl => { self.tcx .sess .create_feature_err( @@ -1709,51 +1703,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) .emit(); } - self.lower_async_fn_ret_ty( - &decl.output, - fn_node_id.expect("`make_ret_async` but no `fn_def_id`"), - ret_id, - true, - ) - } - _ => { - if !kind.impl_trait_return_allowed(self.tcx) { - if kind == FnDeclKind::Impl { - self.tcx - .sess - .create_feature_err( - TraitFnAsync { fn_span, span }, - sym::return_position_impl_trait_in_trait, - ) - .emit(); - } else { - self.tcx.sess.emit_err(TraitFnAsync { fn_span, span }); - } + _ => { + self.tcx.sess.emit_err(TraitFnAsync { fn_span, span }); } - self.lower_async_fn_ret_ty( - &decl.output, - fn_node_id.expect("`make_ret_async` but no `fn_def_id`"), - ret_id, - false, - ) } } + + self.lower_async_fn_ret_ty( + &decl.output, + fn_node_id.expect("`make_ret_async` but no `fn_def_id`"), + ret_id, + matches!(kind, FnDeclKind::Trait), + ) } else { match decl.output { FnRetTy::Ty(ref ty) => { let mut context = match fn_node_id { - Some(fn_node_id) if kind.impl_trait_return_allowed(self.tcx) => { - let fn_def_id = self.local_def_id(fn_node_id); - ImplTraitContext::ReturnPositionOpaqueTy { - origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), - in_trait: false, - } - } - Some(fn_node_id) if kind.impl_trait_in_trait_allowed(self.tcx) => { + Some(fn_node_id) if kind.impl_trait_allowed(self.tcx) => { let fn_def_id = self.local_def_id(fn_node_id); ImplTraitContext::ReturnPositionOpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), - in_trait: true, + in_trait: matches!(kind, FnDeclKind::Trait), } } _ => ImplTraitContext::Disallowed(match kind { From 9457380ac902db3febf92077c5b645db55998ad4 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 19 Sep 2022 10:52:13 -0700 Subject: [PATCH 10/14] rustdoc: remove `docblock` class from `item-decl` This class was originally added in 73b97c7e7c9cfac4dfa4804654b1db6ab687b589 to support hiding and showing the item, because `main.js` went through all `docblock` elements in the DOM and added toggles to them. https://github.com/rust-lang/rust/blob/73b97c7e7c9cfac4dfa4804654b1db6ab687b589/src/librustdoc/html/static/main.js#L1856-L1867 The `item-decl` is no longer auto-hidden since c96f86de3026f864e78397aff9097e885f2f8fdf removed it. `item-decl` used to be called `type-decl`: that name was changed in 8b7a2dd4626acf164e1ce8397878b3f5af83d585. The `docblock` class is no longer used for implementing toggles, since rustdoc switched to using `
` elements. --- src/librustdoc/html/render/print_item.rs | 30 +++++++++++----------- src/librustdoc/html/static/css/rustdoc.css | 3 --- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index cfa4509428f10..3e324bbb069c6 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -514,7 +514,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle + name.as_str().len() + generics_len; - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "fn", |w| { render_attributes_in_pre(w, it, ""); w.reserve(header_len); @@ -553,7 +553,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: cx.tcx().trait_def(t.def_id).must_implement_one_of.clone(); // Output the trait definition - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "trait", |w| { render_attributes_in_pre(w, it, ""); write!( @@ -1033,7 +1033,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "trait-alias", |w| { render_attributes_in_pre(w, it, ""); write!( @@ -1057,7 +1057,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & } fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "opaque", |w| { render_attributes_in_pre(w, it, ""); write!( @@ -1096,7 +1096,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea }); } - wrap_into_docblock(w, |w| write_content(w, cx, it, t)); + wrap_into_item_decl(w, |w| write_content(w, cx, it, t)); document(w, cx, it, None, HeadingOffset::H2); @@ -1110,7 +1110,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea } fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "union", |w| { render_attributes_in_pre(w, it, ""); render_union(w, it, Some(&s.generics), &s.fields, "", cx); @@ -1174,7 +1174,7 @@ fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item] fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) { let count_variants = e.variants().count(); - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "enum", |w| { render_attributes_in_pre(w, it, ""); write!( @@ -1333,14 +1333,14 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: } fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { highlight::render_macro_with_highlighting(&t.source, w); }); document(w, cx, it, None, HeadingOffset::H2) } fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { @@ -1387,7 +1387,7 @@ fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { } fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "const", |w| { render_attributes_in_code(w, it); @@ -1436,7 +1436,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle } fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "struct", |w| { render_attributes_in_code(w, it); render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx); @@ -1489,7 +1489,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean } fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "static", |w| { render_attributes_in_code(w, it); write!( @@ -1506,7 +1506,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean } fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { - wrap_into_docblock(w, |w| { + wrap_into_item_decl(w, |w| { wrap_item(w, "foreigntype", |w| { w.write_str("extern {\n"); render_attributes_in_code(w, it); @@ -1595,11 +1595,11 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) bounds } -fn wrap_into_docblock(w: &mut Buffer, f: F) +fn wrap_into_item_decl(w: &mut Buffer, f: F) where F: FnOnce(&mut Buffer), { - w.write_str("
"); + w.write_str("
"); f(w); w.write_str("
") } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index f458a4f59ae82..dc2a67c69d725 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -372,9 +372,6 @@ code, pre, a.test-arrow, .code-header { pre { padding: 14px; } -.docblock.item-decl { - margin-left: 0; -} .item-decl pre { overflow-x: auto; } From 8e6cf7d84089862aad9711792ccbbe58cab28395 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 19 Sep 2022 12:17:05 -0700 Subject: [PATCH 11/14] rustdoc: update test cases for changed item-decl HTML --- .../rustdoc-gui/check-code-blocks-margin.goml | 4 +-- src/test/rustdoc-gui/font-weight.goml | 8 ++--- src/test/rustdoc-gui/item-info-overflow.goml | 2 +- src/test/rustdoc/attribute-rendering.rs | 2 +- src/test/rustdoc/attributes.rs | 2 +- src/test/rustdoc/const-value-display.rs | 4 +-- src/test/rustdoc/decl-trailing-whitespace.rs | 2 +- .../rustdoc/macro-higher-kinded-function.rs | 4 +-- src/test/rustdoc/reexport-dep-foreign-fn.rs | 2 +- src/test/rustdoc/reexports-priv.rs | 32 +++++++++---------- src/test/rustdoc/reexports.rs | 16 +++++----- src/test/rustdoc/toggle-item-contents.rs | 2 +- src/test/rustdoc/trait_alias.rs | 6 ++-- .../rustdoc/where.SWhere_Simd_item-decl.html | 2 +- .../where.SWhere_TraitWhere_item-decl.html | 4 +-- src/test/rustdoc/where.rs | 4 +-- .../whitespace-after-where-clause.enum.html | 4 +-- .../whitespace-after-where-clause.enum2.html | 4 +-- .../rustdoc/whitespace-after-where-clause.rs | 16 +++++----- .../whitespace-after-where-clause.struct.html | 4 +-- ...whitespace-after-where-clause.struct2.html | 4 +-- .../whitespace-after-where-clause.trait.html | 4 +-- .../whitespace-after-where-clause.trait2.html | 4 +-- .../whitespace-after-where-clause.union.html | 4 +-- .../whitespace-after-where-clause.union2.html | 4 +-- 25 files changed, 72 insertions(+), 72 deletions(-) diff --git a/src/test/rustdoc-gui/check-code-blocks-margin.goml b/src/test/rustdoc-gui/check-code-blocks-margin.goml index f6266eba75de7..f2fc3e9afc2a2 100644 --- a/src/test/rustdoc-gui/check-code-blocks-margin.goml +++ b/src/test/rustdoc-gui/check-code-blocks-margin.goml @@ -1,6 +1,6 @@ // This test ensures that the docblock elements have the appropriate left margin. goto: file://|DOC_PATH|/test_docs/fn.foo.html // The top docblock elements shouldn't have left margin... -assert-css: ("#main-content .docblock.item-decl", {"margin-left": "0px"}) +assert-css: ("#main-content .item-decl", {"margin-left": "0px"}) // ... but all the others should! -assert-css: ("#main-content .docblock:not(.item-decl)", {"margin-left": "24px"}) +assert-css: ("#main-content .docblock", {"margin-left": "24px"}) diff --git a/src/test/rustdoc-gui/font-weight.goml b/src/test/rustdoc-gui/font-weight.goml index 5f29fde6689bf..13e8ec9fb16a7 100644 --- a/src/test/rustdoc-gui/font-weight.goml +++ b/src/test/rustdoc-gui/font-weight.goml @@ -1,6 +1,6 @@ // This test checks that the font weight is correctly applied. goto: file://|DOC_PATH|/lib2/struct.Foo.html -assert-css: ("//*[@class='docblock item-decl']//a[text()='Alias']", {"font-weight": "400"}) +assert-css: ("//*[@class='item-decl']//a[text()='Alias']", {"font-weight": "400"}) assert-css: ( "//*[@class='structfield small-section-header']//a[text()='Alias']", {"font-weight": "400"}, @@ -19,7 +19,7 @@ goto: file://|DOC_PATH|/lib2/trait.Trait.html // This is a complex selector, so here's how it works: // -// * //*[@class='docblock item-decl'] — selects element of any tag with classes docblock and item-decl +// * //*[@class='item-decl'] — selects element of any tag with classes docblock and item-decl // * /pre[@class='rust trait'] — selects immediate child with tag pre and classes rust and trait // * /code — selects immediate child with tag code // * /a[@class='constant'] — selects immediate child with tag a and class constant @@ -29,11 +29,11 @@ goto: file://|DOC_PATH|/lib2/trait.Trait.html // This uses '/parent::*' as a proxy for the style of the text node. // We can't just select the '' because intermediate tags could be added. assert-count: ( - "//*[@class='docblock item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*", + "//*[@class='item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*", 1, ) assert-css: ( - "//*[@class='docblock item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*", + "//*[@class='item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*", {"font-weight": "400"}, ) diff --git a/src/test/rustdoc-gui/item-info-overflow.goml b/src/test/rustdoc-gui/item-info-overflow.goml index b7095a3c5324d..17478da4fea29 100644 --- a/src/test/rustdoc-gui/item-info-overflow.goml +++ b/src/test/rustdoc-gui/item-info-overflow.goml @@ -3,7 +3,7 @@ goto: file://|DOC_PATH|/lib2/struct.LongItemInfo.html // We set a fixed size so there is no chance of "random" resize. size: (1200, 870) // Logically, the "item-decl" and the "item-info" should have the same scroll width. -compare-elements-property: (".docblock.item-decl", ".item-info", ["scrollWidth"]) +compare-elements-property: (".item-decl", ".item-info", ["scrollWidth"]) assert-property: (".item-info", {"scrollWidth": "890"}) // Just to be sure we're comparing the correct "item-info": assert-text: ( diff --git a/src/test/rustdoc/attribute-rendering.rs b/src/test/rustdoc/attribute-rendering.rs index 6777871846e2d..36e10923c8535 100644 --- a/src/test/rustdoc/attribute-rendering.rs +++ b/src/test/rustdoc/attribute-rendering.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] // @has 'foo/fn.f.html' -// @has - //*[@'class="docblock item-decl"]' '#[export_name = "f"] pub fn f()' +// @has - //*[@'class="item-decl"]' '#[export_name = "f"] pub fn f()' #[export_name = "\ f"] pub fn f() {} diff --git a/src/test/rustdoc/attributes.rs b/src/test/rustdoc/attributes.rs index 1c7f4b7241893..a36dadced87d7 100644 --- a/src/test/rustdoc/attributes.rs +++ b/src/test/rustdoc/attributes.rs @@ -8,6 +8,6 @@ pub extern "C" fn f() {} #[export_name = "bar"] pub extern "C" fn g() {} -// @has foo/struct.Repr.html '//*[@class="docblock item-decl"]' '#[repr(C, align(8))]' +// @has foo/struct.Repr.html '//*[@class="item-decl"]' '#[repr(C, align(8))]' #[repr(C, align(8))] pub struct Repr; diff --git a/src/test/rustdoc/const-value-display.rs b/src/test/rustdoc/const-value-display.rs index 5b2f3c48d57fa..8d95f0de9d098 100644 --- a/src/test/rustdoc/const-value-display.rs +++ b/src/test/rustdoc/const-value-display.rs @@ -1,9 +1,9 @@ #![crate_name = "foo"] // @has 'foo/constant.HOUR_IN_SECONDS.html' -// @has - '//*[@class="docblock item-decl"]//code' 'pub const HOUR_IN_SECONDS: u64 = _; // 3_600u64' +// @has - '//*[@class="item-decl"]//code' 'pub const HOUR_IN_SECONDS: u64 = _; // 3_600u64' pub const HOUR_IN_SECONDS: u64 = 60 * 60; // @has 'foo/constant.NEGATIVE.html' -// @has - '//*[@class="docblock item-decl"]//code' 'pub const NEGATIVE: i64 = _; // -3_600i64' +// @has - '//*[@class="item-decl"]//code' 'pub const NEGATIVE: i64 = _; // -3_600i64' pub const NEGATIVE: i64 = -60 * 60; diff --git a/src/test/rustdoc/decl-trailing-whitespace.rs b/src/test/rustdoc/decl-trailing-whitespace.rs index 46a2307abef02..e47edc1321851 100644 --- a/src/test/rustdoc/decl-trailing-whitespace.rs +++ b/src/test/rustdoc/decl-trailing-whitespace.rs @@ -7,7 +7,7 @@ pub struct Error; // @has 'foo/trait.Write.html' pub trait Write { - // @snapshot 'declaration' - '//*[@class="docblock item-decl"]//code' + // @snapshot 'declaration' - '//*[@class="item-decl"]//code' fn poll_write( self: Option, cx: &mut Option, diff --git a/src/test/rustdoc/macro-higher-kinded-function.rs b/src/test/rustdoc/macro-higher-kinded-function.rs index 02a4305644e7d..b8c52b7b791d6 100644 --- a/src/test/rustdoc/macro-higher-kinded-function.rs +++ b/src/test/rustdoc/macro-higher-kinded-function.rs @@ -11,8 +11,8 @@ macro_rules! gen { } // @has 'foo/struct.Providers.html' -// @has - '//*[@class="docblock item-decl"]//code' "pub a: for<'tcx> fn(_: TyCtxt<'tcx>, _: u8) -> i8," -// @has - '//*[@class="docblock item-decl"]//code' "pub b: for<'tcx> fn(_: TyCtxt<'tcx>, _: u16) -> i16," +// @has - '//*[@class="item-decl"]//code' "pub a: for<'tcx> fn(_: TyCtxt<'tcx>, _: u8) -> i8," +// @has - '//*[@class="item-decl"]//code' "pub b: for<'tcx> fn(_: TyCtxt<'tcx>, _: u16) -> i16," // @has - '//*[@id="structfield.a"]/code' "a: for<'tcx> fn(_: TyCtxt<'tcx>, _: u8) -> i8" // @has - '//*[@id="structfield.b"]/code' "b: for<'tcx> fn(_: TyCtxt<'tcx>, _: u16) -> i16" gen! { diff --git a/src/test/rustdoc/reexport-dep-foreign-fn.rs b/src/test/rustdoc/reexport-dep-foreign-fn.rs index 6e1dc453982d9..6694c91d10442 100644 --- a/src/test/rustdoc/reexport-dep-foreign-fn.rs +++ b/src/test/rustdoc/reexport-dep-foreign-fn.rs @@ -8,5 +8,5 @@ extern crate all_item_types; // @has 'foo/fn.foo_ffn.html' -// @has - '//*[@class="docblock item-decl"]//code' 'pub unsafe extern "C" fn foo_ffn()' +// @has - '//*[@class="item-decl"]//code' 'pub unsafe extern "C" fn foo_ffn()' pub use all_item_types::foo_ffn; diff --git a/src/test/rustdoc/reexports-priv.rs b/src/test/rustdoc/reexports-priv.rs index aea9b9f2b395d..11364e7f707ef 100644 --- a/src/test/rustdoc/reexports-priv.rs +++ b/src/test/rustdoc/reexports-priv.rs @@ -5,7 +5,7 @@ extern crate reexports; -// @has 'foo/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place:expr) {' +// @has 'foo/macro.addr_of.html' '//*[@class="item-decl"]' 'pub macro addr_of($place:expr) {' pub use reexports::addr_of; // @!has 'foo/macro.addr_of_crate.html' pub(crate) use reexports::addr_of_crate; @@ -14,7 +14,7 @@ pub(self) use reexports::addr_of_self; // @!has 'foo/macro.addr_of_local.html' use reexports::addr_of_local; -// @has 'foo/struct.Foo.html' '//*[@class="docblock item-decl"]' 'pub struct Foo;' +// @has 'foo/struct.Foo.html' '//*[@class="item-decl"]' 'pub struct Foo;' pub use reexports::Foo; // @!has 'foo/struct.FooCrate.html' pub(crate) use reexports::FooCrate; @@ -23,7 +23,7 @@ pub(self) use reexports::FooSelf; // @!has 'foo/struct.FooLocal.html' use reexports::FooLocal; -// @has 'foo/enum.Bar.html' '//*[@class="docblock item-decl"]' 'pub enum Bar {' +// @has 'foo/enum.Bar.html' '//*[@class="item-decl"]' 'pub enum Bar {' pub use reexports::Bar; // @!has 'foo/enum.BarCrate.html' pub(crate) use reexports::BarCrate; @@ -50,7 +50,7 @@ pub(self) use reexports::TypeSelf; // @!has 'foo/type.TypeLocal.html' use reexports::TypeLocal; -// @has 'foo/union.Union.html' '//*[@class="docblock item-decl"]' 'pub union Union {' +// @has 'foo/union.Union.html' '//*[@class="item-decl"]' 'pub union Union {' pub use reexports::Union; // @!has 'foo/union.UnionCrate.html' pub(crate) use reexports::UnionCrate; @@ -61,33 +61,33 @@ use reexports::UnionLocal; pub mod outer { pub mod inner { - // @has 'foo/outer/inner/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place:expr) {' + // @has 'foo/outer/inner/macro.addr_of.html' '//*[@class="item-decl"]' 'pub macro addr_of($place:expr) {' pub use reexports::addr_of; - // @has 'foo/outer/inner/macro.addr_of_crate.html' '//*[@class="docblock item-decl"]' 'pub(crate) macro addr_of_crate($place:expr) {' + // @has 'foo/outer/inner/macro.addr_of_crate.html' '//*[@class="item-decl"]' 'pub(crate) macro addr_of_crate($place:expr) {' pub(crate) use reexports::addr_of_crate; - // @has 'foo/outer/inner/macro.addr_of_super.html' '//*[@class="docblock item-decl"]' 'pub(in outer) macro addr_of_super($place:expr) {' + // @has 'foo/outer/inner/macro.addr_of_super.html' '//*[@class="item-decl"]' 'pub(in outer) macro addr_of_super($place:expr) {' pub(super) use reexports::addr_of_super; // @!has 'foo/outer/inner/macro.addr_of_self.html' pub(self) use reexports::addr_of_self; // @!has 'foo/outer/inner/macro.addr_of_local.html' use reexports::addr_of_local; - // @has 'foo/outer/inner/struct.Foo.html' '//*[@class="docblock item-decl"]' 'pub struct Foo;' + // @has 'foo/outer/inner/struct.Foo.html' '//*[@class="item-decl"]' 'pub struct Foo;' pub use reexports::Foo; - // @has 'foo/outer/inner/struct.FooCrate.html' '//*[@class="docblock item-decl"]' 'pub(crate) struct FooCrate;' + // @has 'foo/outer/inner/struct.FooCrate.html' '//*[@class="item-decl"]' 'pub(crate) struct FooCrate;' pub(crate) use reexports::FooCrate; - // @has 'foo/outer/inner/struct.FooSuper.html' '//*[@class="docblock item-decl"]' 'pub(in outer) struct FooSuper;' + // @has 'foo/outer/inner/struct.FooSuper.html' '//*[@class="item-decl"]' 'pub(in outer) struct FooSuper;' pub(super) use reexports::FooSuper; // @!has 'foo/outer/inner/struct.FooSelf.html' pub(self) use reexports::FooSelf; // @!has 'foo/outer/inner/struct.FooLocal.html' use reexports::FooLocal; - // @has 'foo/outer/inner/enum.Bar.html' '//*[@class="docblock item-decl"]' 'pub enum Bar {' + // @has 'foo/outer/inner/enum.Bar.html' '//*[@class="item-decl"]' 'pub enum Bar {' pub use reexports::Bar; - // @has 'foo/outer/inner/enum.BarCrate.html' '//*[@class="docblock item-decl"]' 'pub(crate) enum BarCrate {' + // @has 'foo/outer/inner/enum.BarCrate.html' '//*[@class="item-decl"]' 'pub(crate) enum BarCrate {' pub(crate) use reexports::BarCrate; - // @has 'foo/outer/inner/enum.BarSuper.html' '//*[@class="docblock item-decl"]' 'pub(in outer) enum BarSuper {' + // @has 'foo/outer/inner/enum.BarSuper.html' '//*[@class="item-decl"]' 'pub(in outer) enum BarSuper {' pub(super) use reexports::BarSuper; // @!has 'foo/outer/inner/enum.BarSelf.html' pub(self) use reexports::BarSelf; @@ -116,11 +116,11 @@ pub mod outer { // @!has 'foo/outer/inner/type.TypeLocal.html' use reexports::TypeLocal; - // @has 'foo/outer/inner/union.Union.html' '//*[@class="docblock item-decl"]' 'pub union Union {' + // @has 'foo/outer/inner/union.Union.html' '//*[@class="item-decl"]' 'pub union Union {' pub use reexports::Union; - // @has 'foo/outer/inner/union.UnionCrate.html' '//*[@class="docblock item-decl"]' 'pub(crate) union UnionCrate {' + // @has 'foo/outer/inner/union.UnionCrate.html' '//*[@class="item-decl"]' 'pub(crate) union UnionCrate {' pub(crate) use reexports::UnionCrate; - // @has 'foo/outer/inner/union.UnionSuper.html' '//*[@class="docblock item-decl"]' 'pub(in outer) union UnionSuper {' + // @has 'foo/outer/inner/union.UnionSuper.html' '//*[@class="item-decl"]' 'pub(in outer) union UnionSuper {' pub(super) use reexports::UnionSuper; // @!has 'foo/outer/inner/union.UnionSelf.html' pub(self) use reexports::UnionSelf; diff --git a/src/test/rustdoc/reexports.rs b/src/test/rustdoc/reexports.rs index 7abcbfb618122..9aa6d7224baca 100644 --- a/src/test/rustdoc/reexports.rs +++ b/src/test/rustdoc/reexports.rs @@ -4,7 +4,7 @@ extern crate reexports; -// @has 'foo/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place:expr) {' +// @has 'foo/macro.addr_of.html' '//*[@class="item-decl"]' 'pub macro addr_of($place:expr) {' pub use reexports::addr_of; // @!has 'foo/macro.addr_of_crate.html' pub(crate) use reexports::addr_of_crate; @@ -13,7 +13,7 @@ pub(self) use reexports::addr_of_self; // @!has 'foo/macro.addr_of_local.html' use reexports::addr_of_local; -// @has 'foo/struct.Foo.html' '//*[@class="docblock item-decl"]' 'pub struct Foo;' +// @has 'foo/struct.Foo.html' '//*[@class="item-decl"]' 'pub struct Foo;' pub use reexports::Foo; // @!has 'foo/struct.FooCrate.html' pub(crate) use reexports::FooCrate; @@ -22,7 +22,7 @@ pub(self) use reexports::FooSelf; // @!has 'foo/struct.FooLocal.html' use reexports::FooLocal; -// @has 'foo/enum.Bar.html' '//*[@class="docblock item-decl"]' 'pub enum Bar {' +// @has 'foo/enum.Bar.html' '//*[@class="item-decl"]' 'pub enum Bar {' pub use reexports::Bar; // @!has 'foo/enum.BarCrate.html' pub(crate) use reexports::BarCrate; @@ -49,7 +49,7 @@ pub(self) use reexports::TypeSelf; // @!has 'foo/type.TypeLocal.html' use reexports::TypeLocal; -// @has 'foo/union.Union.html' '//*[@class="docblock item-decl"]' 'pub union Union {' +// @has 'foo/union.Union.html' '//*[@class="item-decl"]' 'pub union Union {' pub use reexports::Union; // @!has 'foo/union.UnionCrate.html' pub(crate) use reexports::UnionCrate; @@ -60,7 +60,7 @@ use reexports::UnionLocal; pub mod outer { pub mod inner { - // @has 'foo/outer/inner/macro.addr_of.html' '//*[@class="docblock item-decl"]' 'pub macro addr_of($place:expr) {' + // @has 'foo/outer/inner/macro.addr_of.html' '//*[@class="item-decl"]' 'pub macro addr_of($place:expr) {' pub use reexports::addr_of; // @!has 'foo/outer/inner/macro.addr_of_crate.html' pub(crate) use reexports::addr_of_crate; @@ -71,7 +71,7 @@ pub mod outer { // @!has 'foo/outer/inner/macro.addr_of_local.html' use reexports::addr_of_local; - // @has 'foo/outer/inner/struct.Foo.html' '//*[@class="docblock item-decl"]' 'pub struct Foo;' + // @has 'foo/outer/inner/struct.Foo.html' '//*[@class="item-decl"]' 'pub struct Foo;' pub use reexports::Foo; // @!has 'foo/outer/inner/struct.FooCrate.html' pub(crate) use reexports::FooCrate; @@ -82,7 +82,7 @@ pub mod outer { // @!has 'foo/outer/inner/struct.FooLocal.html' use reexports::FooLocal; - // @has 'foo/outer/inner/enum.Bar.html' '//*[@class="docblock item-decl"]' 'pub enum Bar {' + // @has 'foo/outer/inner/enum.Bar.html' '//*[@class="item-decl"]' 'pub enum Bar {' pub use reexports::Bar; // @!has 'foo/outer/inner/enum.BarCrate.html' pub(crate) use reexports::BarCrate; @@ -115,7 +115,7 @@ pub mod outer { // @!has 'foo/outer/inner/type.TypeLocal.html' use reexports::TypeLocal; - // @has 'foo/outer/inner/union.Union.html' '//*[@class="docblock item-decl"]' 'pub union Union {' + // @has 'foo/outer/inner/union.Union.html' '//*[@class="item-decl"]' 'pub union Union {' pub use reexports::Union; // @!has 'foo/outer/inner/union.UnionCrate.html' pub(crate) use reexports::UnionCrate; diff --git a/src/test/rustdoc/toggle-item-contents.rs b/src/test/rustdoc/toggle-item-contents.rs index dbaf195e1a963..47a1d62f5a7a3 100644 --- a/src/test/rustdoc/toggle-item-contents.rs +++ b/src/test/rustdoc/toggle-item-contents.rs @@ -55,7 +55,7 @@ pub union Union { // @has 'toggle_item_contents/struct.PrivStruct.html' // @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 -// @has - '//div[@class="docblock item-decl"]' '/* private fields */' +// @has - '//div[@class="item-decl"]' '/* private fields */' pub struct PrivStruct { a: usize, b: usize, diff --git a/src/test/rustdoc/trait_alias.rs b/src/test/rustdoc/trait_alias.rs index a0c657d9a054d..791c099cc5233 100644 --- a/src/test/rustdoc/trait_alias.rs +++ b/src/test/rustdoc/trait_alias.rs @@ -14,13 +14,13 @@ use std::fmt::Debug; // @has foo/index.html '//a[@class="traitalias"]' 'Foo' // @has foo/traitalias.CopyAlias.html -// @has - '//section[@id="main-content"]/div[@class="docblock item-decl"]/pre' 'trait CopyAlias = Copy;' +// @has - '//section[@id="main-content"]/div[@class="item-decl"]/pre' 'trait CopyAlias = Copy;' pub trait CopyAlias = Copy; // @has foo/traitalias.Alias2.html -// @has - '//section[@id="main-content"]/div[@class="docblock item-decl"]/pre' 'trait Alias2 = Copy + Debug;' +// @has - '//section[@id="main-content"]/div[@class="item-decl"]/pre' 'trait Alias2 = Copy + Debug;' pub trait Alias2 = Copy + Debug; // @has foo/traitalias.Foo.html -// @has - '//section[@id="main-content"]/div[@class="docblock item-decl"]/pre' 'trait Foo = Into + Debug;' +// @has - '//section[@id="main-content"]/div[@class="item-decl"]/pre' 'trait Foo = Into + Debug;' pub trait Foo = Into + Debug; // @has foo/fn.bar.html '//a[@href="traitalias.Alias2.html"]' 'Alias2' pub fn bar() where T: Alias2 {} diff --git a/src/test/rustdoc/where.SWhere_Simd_item-decl.html b/src/test/rustdoc/where.SWhere_Simd_item-decl.html index 0133bcaeb6673..6c1b5d3151352 100644 --- a/src/test/rustdoc/where.SWhere_Simd_item-decl.html +++ b/src/test/rustdoc/where.SWhere_Simd_item-decl.html @@ -1 +1 @@ - \ No newline at end of file +
pub struct Simd<T>(_)
where
    T: MyTrait
;
\ No newline at end of file diff --git a/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html b/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html index 54026ff034e00..0fbdc0c9cd1e8 100644 --- a/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html +++ b/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html @@ -1,3 +1,3 @@ -
pub trait TraitWhere {
-    type Item<'a>
    where
        Self: 'a
; +
pub trait TraitWhere {
+    type Item<'a>
   where
        Self: 'a
; }
\ No newline at end of file diff --git a/src/test/rustdoc/where.rs b/src/test/rustdoc/where.rs index b1034c707f55a..8818d74ddd087 100644 --- a/src/test/rustdoc/where.rs +++ b/src/test/rustdoc/where.rs @@ -20,13 +20,13 @@ impl Delta where D: MyTrait { pub struct Echo(E); // @has 'foo/struct.Simd.html' -// @snapshot SWhere_Simd_item-decl - '//div[@class="docblock item-decl"]' +// @snapshot SWhere_Simd_item-decl - '//div[@class="item-decl"]' pub struct Simd([T; 1]) where T: MyTrait; // @has 'foo/trait.TraitWhere.html' -// @snapshot SWhere_TraitWhere_item-decl - '//div[@class="docblock item-decl"]' +// @snapshot SWhere_TraitWhere_item-decl - '//div[@class="item-decl"]' pub trait TraitWhere { type Item<'a> where Self: 'a; } diff --git a/src/test/rustdoc/whitespace-after-where-clause.enum.html b/src/test/rustdoc/whitespace-after-where-clause.enum.html index 9e5bf45ae7d2f..c74866f4a10b8 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.enum.html +++ b/src/test/rustdoc/whitespace-after-where-clause.enum.html @@ -1,4 +1,4 @@ -
pub enum Cow<'a, B: ?Sized + 'a> where
    B: ToOwned<dyn Clone>, 
{ +
pub enum Cow<'a, B: ?Sized + 'a>where
    B: ToOwned<dyn Clone>,
{ Borrowed(&'a B), Whatever(u32), -}
+}
\ No newline at end of file diff --git a/src/test/rustdoc/whitespace-after-where-clause.enum2.html b/src/test/rustdoc/whitespace-after-where-clause.enum2.html index 6bc47beaed125..ac7d775982110 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.enum2.html +++ b/src/test/rustdoc/whitespace-after-where-clause.enum2.html @@ -1,4 +1,4 @@ -
pub enum Cow2<'a, B: ?Sized + ToOwned<dyn Clone> + 'a> {
+
pub enum Cow2<'a, B: ?Sized + ToOwned<dyn Clone> + 'a> {
     Borrowed(&'a B),
     Whatever(u32),
-}
+}
\ No newline at end of file diff --git a/src/test/rustdoc/whitespace-after-where-clause.rs b/src/test/rustdoc/whitespace-after-where-clause.rs index c36386a2aa2b5..4b740b970fc20 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.rs +++ b/src/test/rustdoc/whitespace-after-where-clause.rs @@ -4,7 +4,7 @@ #![crate_name = "foo"] // @has 'foo/trait.ToOwned.html' -// @snapshot trait - '//*[@class="docblock item-decl"]' +// @snapshot trait - '//*[@class="item-decl"]' pub trait ToOwned where T: Clone { @@ -14,7 +14,7 @@ where T: Clone } // @has 'foo/trait.ToOwned2.html' -// @snapshot trait2 - '//*[@class="docblock item-decl"]' +// @snapshot trait2 - '//*[@class="item-decl"]' // There should be a whitespace before `{` in this case! pub trait ToOwned2 { type Owned; @@ -23,7 +23,7 @@ pub trait ToOwned2 { } // @has 'foo/enum.Cow.html' -// @snapshot enum - '//*[@class="docblock item-decl"]' +// @snapshot enum - '//*[@class="item-decl"]' pub enum Cow<'a, B: ?Sized + 'a> where B: ToOwned, @@ -33,7 +33,7 @@ where } // @has 'foo/enum.Cow2.html' -// @snapshot enum2 - '//*[@class="docblock item-decl"]' +// @snapshot enum2 - '//*[@class="item-decl"]' // There should be a whitespace before `{` in this case! pub enum Cow2<'a, B: ?Sized + ToOwned + 'a> { Borrowed(&'a B), @@ -41,7 +41,7 @@ pub enum Cow2<'a, B: ?Sized + ToOwned + 'a> { } // @has 'foo/struct.Struct.html' -// @snapshot struct - '//*[@class="docblock item-decl"]' +// @snapshot struct - '//*[@class="item-decl"]' pub struct Struct<'a, B: ?Sized + 'a> where B: ToOwned, @@ -51,7 +51,7 @@ where } // @has 'foo/struct.Struct2.html' -// @snapshot struct2 - '//*[@class="docblock item-decl"]' +// @snapshot struct2 - '//*[@class="item-decl"]' // There should be a whitespace before `{` in this case! pub struct Struct2<'a, B: ?Sized + ToOwned + 'a> { pub a: &'a B, @@ -59,7 +59,7 @@ pub struct Struct2<'a, B: ?Sized + ToOwned + 'a> { } // @has 'foo/union.Union.html' -// @snapshot union - '//*[@class="docblock item-decl"]' +// @snapshot union - '//*[@class="item-decl"]' pub union Union<'a, B: ?Sized + 'a> where B: ToOwned, @@ -69,7 +69,7 @@ where } // @has 'foo/union.Union2.html' -// @snapshot union2 - '//*[@class="docblock item-decl"]' +// @snapshot union2 - '//*[@class="item-decl"]' // There should be a whitespace before `{` in this case! pub union Union2<'a, B: ?Sized + ToOwned + 'a> { a: &'a B, diff --git a/src/test/rustdoc/whitespace-after-where-clause.struct.html b/src/test/rustdoc/whitespace-after-where-clause.struct.html index 236cc3b30d088..1ba1367d20f72 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.struct.html +++ b/src/test/rustdoc/whitespace-after-where-clause.struct.html @@ -1,4 +1,4 @@ -
pub struct Struct<'a, B: ?Sized + 'a> where
    B: ToOwned<dyn Clone>, 
{ +
pub struct Struct<'a, B: ?Sized + 'a>where
    B: ToOwned<dyn Clone>,
{ pub a: &'a B, pub b: u32, -}
+}
\ No newline at end of file diff --git a/src/test/rustdoc/whitespace-after-where-clause.struct2.html b/src/test/rustdoc/whitespace-after-where-clause.struct2.html index 47f5c6ba9c846..fb06b0f77c5ce 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.struct2.html +++ b/src/test/rustdoc/whitespace-after-where-clause.struct2.html @@ -1,4 +1,4 @@ -
pub struct Struct2<'a, B: ?Sized + ToOwned<dyn Clone> + 'a> {
+
pub struct Struct2<'a, B: ?Sized + ToOwned<dyn Clone> + 'a> {
     pub a: &'a B,
     pub b: u32,
-}
+}
\ No newline at end of file diff --git a/src/test/rustdoc/whitespace-after-where-clause.trait.html b/src/test/rustdoc/whitespace-after-where-clause.trait.html index 98f03b837b528..16b5582370353 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.trait.html +++ b/src/test/rustdoc/whitespace-after-where-clause.trait.html @@ -1,6 +1,6 @@ -
pub trait ToOwned<T> where
    T: Clone
{ +
pub trait ToOwned<T>where
    T: Clone,
{ type Owned; fn to_owned(&self) -> Self::Owned; fn whatever(&self) -> T; -}
+}
\ No newline at end of file diff --git a/src/test/rustdoc/whitespace-after-where-clause.trait2.html b/src/test/rustdoc/whitespace-after-where-clause.trait2.html index 35052869e762d..eeca6e1f50081 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.trait2.html +++ b/src/test/rustdoc/whitespace-after-where-clause.trait2.html @@ -1,6 +1,6 @@ -
pub trait ToOwned2<T: Clone> {
+
pub trait ToOwned2<T: Clone> {
     type Owned;
 
     fn to_owned(&self) -> Self::Owned;
     fn whatever(&self) -> T;
-}
+}
\ No newline at end of file diff --git a/src/test/rustdoc/whitespace-after-where-clause.union.html b/src/test/rustdoc/whitespace-after-where-clause.union.html index 97e1bbcf339f8..0dfb6407d45f1 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.union.html +++ b/src/test/rustdoc/whitespace-after-where-clause.union.html @@ -1,3 +1,3 @@ -
pub union Union<'a, B: ?Sized + 'a> where
    B: ToOwned<dyn Clone>, 
{ +
pub union Union<'a, B: ?Sized + 'a>where
    B: ToOwned<dyn Clone>,
{ /* private fields */ -}
+}
\ No newline at end of file diff --git a/src/test/rustdoc/whitespace-after-where-clause.union2.html b/src/test/rustdoc/whitespace-after-where-clause.union2.html index 6c752a8b4c5ea..0d237df53c7f4 100644 --- a/src/test/rustdoc/whitespace-after-where-clause.union2.html +++ b/src/test/rustdoc/whitespace-after-where-clause.union2.html @@ -1,3 +1,3 @@ -
pub union Union2<'a, B: ?Sized + ToOwned<dyn Clone> + 'a> {
+
pub union Union2<'a, B: ?Sized + ToOwned<dyn Clone> + 'a> {
     /* private fields */
-}
+}
\ No newline at end of file From 5922d6cf60169ad0fd792581c9da11bd633533da Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 19 Sep 2022 18:43:15 -0500 Subject: [PATCH 12/14] slightly cleanup building SelectionContext --- .../src/traits/select/mod.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 8b15e10ba9cbf..6bdd613be5aa7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -226,13 +226,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener_keep_static(), - intercrate: true, - intercrate_ambiguity_causes: None, - query_mode: TraitQueryMode::Standard, - } + SelectionContext { intercrate: true, ..SelectionContext::new(infcx) } } pub fn with_query_mode( @@ -240,13 +234,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { query_mode: TraitQueryMode, ) -> SelectionContext<'cx, 'tcx> { debug!(?query_mode, "with_query_mode"); - SelectionContext { - infcx, - freshener: infcx.freshener_keep_static(), - intercrate: false, - intercrate_ambiguity_causes: None, - query_mode, - } + SelectionContext { query_mode, ..SelectionContext::new(infcx) } } /// Enables tracking of intercrate ambiguity causes. See From 749dec64519b5bdfe688cb945eeee5afd6ab68d0 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 19 Sep 2022 20:57:37 -0500 Subject: [PATCH 13/14] Make `OUT` an associated type instead of a generic parameter This avoids toil when changing other functions in `ObligationForest` to take an `OUT` parameter. --- compiler/rustc_data_structures/src/obligation_forest/mod.rs | 4 ++++ compiler/rustc_data_structures/src/obligation_forest/tests.rs | 1 + compiler/rustc_trait_selection/src/traits/fulfill.rs | 1 + 3 files changed, 6 insertions(+) diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index e351b650a16c1..f2d72647a6686 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -95,6 +95,10 @@ pub trait ForestObligation: Clone + Debug { pub trait ObligationProcessor { type Obligation: ForestObligation; type Error: Debug; + type OUT: OutcomeTrait< + Obligation = Self::Obligation, + Error = Error, + >; fn needs_process_obligation(&self, obligation: &Self::Obligation) -> bool; diff --git a/compiler/rustc_data_structures/src/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs index e2991aae1742c..f2a04796691ac 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/tests.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/tests.rs @@ -64,6 +64,7 @@ where { type Obligation = O; type Error = E; + type OUT = TestOutcome; fn needs_process_obligation(&self, _obligation: &Self::Obligation) -> bool { true diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 3763a98c488b7..0ea2b6ce885fd 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -224,6 +224,7 @@ fn mk_pending(os: Vec>) -> Vec ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { type Obligation = PendingPredicateObligation<'tcx>; type Error = FulfillmentErrorCode<'tcx>; + type OUT = Outcome; /// Identifies whether a predicate obligation needs processing. /// From 1512ce5925eeafc01f011c46216c157e4e5644cb Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 19 Sep 2022 20:45:00 -0500 Subject: [PATCH 14/14] Make cycle errors recoverable In particular, this allows rustdoc to recover from cycle errors when normalizing associated types for documentation. In the past, `@jackh726` has said we need to be careful about overflow errors: > Off the top of my head, we definitely should be careful about treating overflow errors the same as "not implemented for some reason" errors. Otherwise, you could end up with behavior that is different depending on recursion depth. But, that might be context-dependent. But cycle errors should be safe to unconditionally report; they don't depend on the recursion depth, they will always be an error whenever they're encountered. --- .../src/obligation_forest/mod.rs | 33 ++++++++++++------- .../src/obligation_forest/tests.rs | 7 +++- compiler/rustc_infer/src/traits/mod.rs | 2 ++ .../src/traits/structural_impls.rs | 1 + .../src/traits/codegen.rs | 10 ++++++ .../src/traits/error_reporting/mod.rs | 3 ++ .../src/traits/fulfill.rs | 9 ++--- src/test/rustdoc-ui/normalize-cycle.rs | 1 + 8 files changed, 50 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index f2d72647a6686..10e673cd9297b 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -115,7 +115,11 @@ pub trait ObligationProcessor { /// In other words, if we had O1 which required O2 which required /// O3 which required O1, we would give an iterator yielding O1, /// O2, O3 (O1 is not yielded twice). - fn process_backedge<'c, I>(&mut self, cycle: I, _marker: PhantomData<&'c Self::Obligation>) + fn process_backedge<'c, I>( + &mut self, + cycle: I, + _marker: PhantomData<&'c Self::Obligation>, + ) -> Result<(), Self::Error> where I: Clone + Iterator; } @@ -406,12 +410,11 @@ impl ObligationForest { /// Performs a fixpoint computation over the obligation list. #[inline(never)] - pub fn process_obligations(&mut self, processor: &mut P) -> OUT + pub fn process_obligations

(&mut self, processor: &mut P) -> P::OUT where P: ObligationProcessor, - OUT: OutcomeTrait>, { - let mut outcome = OUT::new(); + let mut outcome = P::OUT::new(); // Fixpoint computation: we repeat until the inner loop stalls. loop { @@ -477,7 +480,7 @@ impl ObligationForest { } self.mark_successes(); - self.process_cycles(processor); + self.process_cycles(processor, &mut outcome); self.compress(|obl| outcome.record_completed(obl)); } @@ -562,7 +565,7 @@ impl ObligationForest { /// Report cycles between all `Success` nodes, and convert all `Success` /// nodes to `Done`. This must be called after `mark_successes`. - fn process_cycles

(&mut self, processor: &mut P) + fn process_cycles

(&mut self, processor: &mut P, outcome: &mut P::OUT) where P: ObligationProcessor, { @@ -572,7 +575,7 @@ impl ObligationForest { // to handle the no-op cases immediately to avoid the cost of the // function call. if node.state.get() == NodeState::Success { - self.find_cycles_from_node(&mut stack, processor, index); + self.find_cycles_from_node(&mut stack, processor, index, outcome); } } @@ -580,8 +583,13 @@ impl ObligationForest { self.reused_node_vec = stack; } - fn find_cycles_from_node

(&self, stack: &mut Vec, processor: &mut P, index: usize) - where + fn find_cycles_from_node

( + &self, + stack: &mut Vec, + processor: &mut P, + index: usize, + outcome: &mut P::OUT, + ) where P: ObligationProcessor, { let node = &self.nodes[index]; @@ -590,17 +598,20 @@ impl ObligationForest { None => { stack.push(index); for &dep_index in node.dependents.iter() { - self.find_cycles_from_node(stack, processor, dep_index); + self.find_cycles_from_node(stack, processor, dep_index, outcome); } stack.pop(); node.state.set(NodeState::Done); } Some(rpos) => { // Cycle detected. - processor.process_backedge( + let result = processor.process_backedge( stack[rpos..].iter().map(|&i| &self.nodes[i].obligation), PhantomData, ); + if let Err(err) = result { + outcome.record_error(Error { error: err, backtrace: self.error_at(index) }); + } } } } diff --git a/compiler/rustc_data_structures/src/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs index f2a04796691ac..bc252f772a168 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/tests.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/tests.rs @@ -77,10 +77,15 @@ where (self.process_obligation)(obligation) } - fn process_backedge<'c, I>(&mut self, _cycle: I, _marker: PhantomData<&'c Self::Obligation>) + fn process_backedge<'c, I>( + &mut self, + _cycle: I, + _marker: PhantomData<&'c Self::Obligation>, + ) -> Result<(), Self::Error> where I: Clone + Iterator, { + Ok(()) } } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 4df4de21a0f07..ed2beefc6e773 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -105,6 +105,8 @@ pub struct FulfillmentError<'tcx> { #[derive(Clone)] pub enum FulfillmentErrorCode<'tcx> { + /// Inherently impossible to fulfill; this trait is implemented if and only if it is already implemented. + CodeCycle(Vec>>), CodeSelectionError(SelectionError<'tcx>), CodeProjectionError(MismatchedProjectionTypes<'tcx>), CodeSubtypeError(ExpectedFound>, TypeError<'tcx>), // always comes from a SubtypePredicate diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index 573d2d1e33016..1c6ab6a082b99 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -47,6 +47,7 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { write!(f, "CodeConstEquateError({:?}, {:?})", a, b) } super::CodeAmbiguity => write!(f, "Ambiguity"), + super::CodeCycle(ref cycle) => write!(f, "Cycle({:?})", cycle), } } } diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index 08adbcbd410c6..0155798c8b6d7 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -4,10 +4,12 @@ // general routines. use crate::infer::{DefiningAnchor, TyCtxtInferExt}; +use crate::traits::error_reporting::InferCtxtExt; use crate::traits::{ ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, TraitEngineExt, Unimplemented, }; +use rustc_infer::traits::FulfillmentErrorCode; use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt}; @@ -62,6 +64,14 @@ pub fn codegen_select_candidate<'tcx>( // optimization to stop iterating early. let errors = fulfill_cx.select_all_or_error(&infcx); if !errors.is_empty() { + // `rustc_monomorphize::collector` assumes there are no type errors. + // Cycle errors are the only post-monomorphization errors possible; emit them now so + // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization. + for err in errors { + if let FulfillmentErrorCode::CodeCycle(cycle) = err.code { + infcx.report_overflow_error_cycle(&cycle); + } + } return Err(CodegenObligationError::FulfillmentError); } 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 efdb1ace13992..d62b399c1b562 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1540,6 +1540,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } diag.emit(); } + FulfillmentErrorCode::CodeCycle(ref cycle) => { + self.report_overflow_error_cycle(cycle); + } } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 0ea2b6ce885fd..1cd8e1c21236f 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -25,10 +25,9 @@ use super::Unimplemented; use super::{FulfillmentError, FulfillmentErrorCode}; use super::{ObligationCause, PredicateObligation}; -use crate::traits::error_reporting::InferCtxtExt as _; use crate::traits::project::PolyProjectionObligation; use crate::traits::project::ProjectionCacheKeyExt as _; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::query::evaluate_obligation::InferCtxtExt; impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { /// Note that we include both the `ParamEnv` and the `Predicate`, @@ -603,14 +602,16 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { &mut self, cycle: I, _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, - ) where + ) -> Result<(), FulfillmentErrorCode<'tcx>> + where I: Clone + Iterator>, { if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { debug!("process_child_obligations: coinductive match"); + Ok(()) } else { let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); - self.selcx.infcx().report_overflow_error_cycle(&cycle); + Err(FulfillmentErrorCode::CodeCycle(cycle)) } } } diff --git a/src/test/rustdoc-ui/normalize-cycle.rs b/src/test/rustdoc-ui/normalize-cycle.rs index 14ffac1e1dce6..1ed9ac6bc34ac 100644 --- a/src/test/rustdoc-ui/normalize-cycle.rs +++ b/src/test/rustdoc-ui/normalize-cycle.rs @@ -1,4 +1,5 @@ // check-pass +// compile-flags: -Znormalize-docs // Regression test for . pub trait Query {}