Auto merge of #136747 - matthiaskrgr:rollup-qfiiv4n, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #135696 (std: move `io` module out of `pal`, get rid of `sys_common::io`) - #136099 (Optimize `Rc::<str>::default()` implementation) - #136200 (Generate correct terminate block under Wasm EH) - #136626 (create `initial_rustdoc` field in `Build`) - #136657 (Make empty-line-after an early clippy lint) - #136679 (ci: Use largedisk for loongarch) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
43ca9d18e3
64 changed files with 857 additions and 802 deletions
|
@ -1708,15 +1708,32 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let mut cs_bx = Bx::build(self.cx, llbb);
|
||||
let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);
|
||||
|
||||
// The "null" here is actually a RTTI type descriptor for the
|
||||
// C++ personality function, but `catch (...)` has no type so
|
||||
// it's null. The 64 here is actually a bitfield which
|
||||
// represents that this is a catch-all block.
|
||||
bx = Bx::build(self.cx, cp_llbb);
|
||||
let null =
|
||||
bx.const_null(bx.type_ptr_ext(bx.cx().data_layout().instruction_address_space));
|
||||
let sixty_four = bx.const_i32(64);
|
||||
funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null]));
|
||||
|
||||
// The `null` in first argument here is actually a RTTI type
|
||||
// descriptor for the C++ personality function, but `catch (...)`
|
||||
// has no type so it's null.
|
||||
let args = if base::wants_msvc_seh(self.cx.sess()) {
|
||||
// This bitmask is a single `HT_IsStdDotDot` flag, which
|
||||
// represents that this is a C++-style `catch (...)` block that
|
||||
// only captures programmatic exceptions, not all SEH
|
||||
// exceptions. The second `null` points to a non-existent
|
||||
// `alloca` instruction, which an LLVM pass would inline into
|
||||
// the initial SEH frame allocation.
|
||||
let adjectives = bx.const_i32(0x40);
|
||||
&[null, adjectives, null] as &[_]
|
||||
} else {
|
||||
// Specifying more arguments than necessary usually doesn't
|
||||
// hurt, but the `WasmEHPrepare` LLVM pass does not recognize
|
||||
// anything other than a single `null` as a `catch (...)` block,
|
||||
// leading to problems down the line during instruction
|
||||
// selection.
|
||||
&[null] as &[_]
|
||||
};
|
||||
|
||||
funclet = Some(bx.catch_pad(cs, args));
|
||||
} else {
|
||||
llbb = Bx::append_block(self.cx, self.llfn, "terminate");
|
||||
bx = Bx::build(self.cx, llbb);
|
||||
|
|
|
@ -246,6 +246,14 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast>
|
|||
}
|
||||
}
|
||||
ast_visit::walk_assoc_item(cx, item, ctxt);
|
||||
match ctxt {
|
||||
ast_visit::AssocCtxt::Trait => {
|
||||
lint_callback!(cx, check_trait_item_post, item);
|
||||
}
|
||||
ast_visit::AssocCtxt::Impl => {
|
||||
lint_callback!(cx, check_impl_item_post, item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -162,7 +162,9 @@ macro_rules! early_lint_methods {
|
|||
c: rustc_span::Span,
|
||||
d_: rustc_ast::NodeId);
|
||||
fn check_trait_item(a: &rustc_ast::AssocItem);
|
||||
fn check_trait_item_post(a: &rustc_ast::AssocItem);
|
||||
fn check_impl_item(a: &rustc_ast::AssocItem);
|
||||
fn check_impl_item_post(a: &rustc_ast::AssocItem);
|
||||
fn check_variant(a: &rustc_ast::Variant);
|
||||
fn check_attribute(a: &rustc_ast::Attribute);
|
||||
fn check_attributes(a: &[rustc_ast::Attribute]);
|
||||
|
|
|
@ -965,8 +965,9 @@ impl Default for Rc<CStr> {
|
|||
/// This may or may not share an allocation with other Rcs on the same thread.
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
let c_str: &CStr = Default::default();
|
||||
Rc::from(c_str)
|
||||
let rc = Rc::<[u8]>::from(*b"\0");
|
||||
// `[u8]` has the same layout as `CStr`, and it is `NUL` terminated.
|
||||
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2369,7 +2369,9 @@ impl Default for Rc<str> {
|
|||
/// This may or may not share an allocation with other Rcs on the same thread.
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Rc::from("")
|
||||
let rc = Rc::<[u8]>::default();
|
||||
// `[u8]` has the same layout as `str`.
|
||||
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const str) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::os::unix::fs::symlink as junction_point;
|
|||
use crate::os::windows::fs::{OpenOptionsExt, junction_point, symlink_dir, symlink_file};
|
||||
use crate::path::Path;
|
||||
use crate::sync::Arc;
|
||||
use crate::sys_common::io::test::{TempDir, tmpdir};
|
||||
use crate::test_helpers::{TempDir, tmpdir};
|
||||
use crate::time::{Duration, Instant, SystemTime};
|
||||
use crate::{env, str, thread};
|
||||
|
||||
|
|
|
@ -344,7 +344,7 @@ pub mod prelude;
|
|||
mod stdio;
|
||||
mod util;
|
||||
|
||||
const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
|
||||
const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
|
||||
|
||||
pub(crate) use stdio::cleanup;
|
||||
|
||||
|
|
|
@ -739,27 +739,4 @@ mod sealed {
|
|||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)] // Not used in all configurations.
|
||||
pub(crate) mod test_helpers {
|
||||
/// Test-only replacement for `rand::thread_rng()`, which is unusable for
|
||||
/// us, as we want to allow running stdlib tests on tier-3 targets which may
|
||||
/// not have `getrandom` support.
|
||||
///
|
||||
/// Does a bit of a song and dance to ensure that the seed is different on
|
||||
/// each call (as some tests sadly rely on this), but doesn't try that hard.
|
||||
///
|
||||
/// This is duplicated in the `core`, `alloc` test suites (as well as
|
||||
/// `std`'s integration tests), but figuring out a mechanism to share these
|
||||
/// seems far more painful than copy-pasting a 7 line function a couple
|
||||
/// times, given that even under a perma-unstable feature, I don't think we
|
||||
/// want to expose types from `rand` from `std`.
|
||||
#[track_caller]
|
||||
pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng {
|
||||
use core::hash::{BuildHasher, Hash, Hasher};
|
||||
let mut hasher = crate::hash::RandomState::new().build_hasher();
|
||||
core::panic::Location::caller().hash(&mut hasher);
|
||||
let hc64 = hasher.finish();
|
||||
let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>();
|
||||
let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap();
|
||||
rand::SeedableRng::from_seed(seed)
|
||||
}
|
||||
}
|
||||
pub(crate) mod test_helpers;
|
||||
|
|
|
@ -3,7 +3,7 @@ use super::*;
|
|||
#[test]
|
||||
fn read_vectored_at() {
|
||||
let msg = b"preadv is working!";
|
||||
let dir = crate::sys_common::io::test::tmpdir();
|
||||
let dir = crate::test_helpers::tmpdir();
|
||||
|
||||
let filename = dir.join("preadv.txt");
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ fn read_vectored_at() {
|
|||
#[test]
|
||||
fn write_vectored_at() {
|
||||
let msg = b"pwritev is not working!";
|
||||
let dir = crate::sys_common::io::test::tmpdir();
|
||||
let dir = crate::test_helpers::tmpdir();
|
||||
|
||||
let filename = dir.join("preadv.txt");
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::os::android::net::{SocketAddrExt, UnixSocketExt};
|
|||
use crate::os::linux::net::{SocketAddrExt, UnixSocketExt};
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
use crate::os::unix::io::AsRawFd;
|
||||
use crate::sys_common::io::test::tmpdir;
|
||||
use crate::test_helpers::tmpdir;
|
||||
use crate::thread;
|
||||
use crate::time::Duration;
|
||||
|
||||
|
|
|
@ -549,7 +549,7 @@ fn debug_print() {
|
|||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn run_bat_script() {
|
||||
let tempdir = crate::sys_common::io::test::tmpdir();
|
||||
let tempdir = crate::test_helpers::tmpdir();
|
||||
let script_path = tempdir.join("hello.cmd");
|
||||
|
||||
crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
|
||||
|
@ -568,7 +568,7 @@ fn run_bat_script() {
|
|||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn run_canonical_bat_script() {
|
||||
let tempdir = crate::sys_common::io::test::tmpdir();
|
||||
let tempdir = crate::test_helpers::tmpdir();
|
||||
let script_path = tempdir.join("hello.cmd");
|
||||
|
||||
crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
use hermit_abi::{c_void, iovec};
|
||||
#[cfg(target_os = "hermit")]
|
||||
use hermit_abi::iovec;
|
||||
#[cfg(target_family = "unix")]
|
||||
use libc::iovec;
|
||||
|
||||
use crate::ffi::c_void;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::os::hermit::io::{AsFd, AsRawFd};
|
||||
use crate::slice;
|
||||
#[cfg(target_os = "solid_asp3")]
|
||||
use crate::sys::pal::abi::sockets::iovec;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
|
@ -80,8 +85,3 @@ 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();
|
||||
hermit_abi::isatty(fd.as_raw_fd())
|
||||
}
|
|
@ -50,7 +50,3 @@ impl<'a> IoSliceMut<'a> {
|
|||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_terminal<T>(_: &T) -> bool {
|
||||
false
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use crate::marker::PhantomData;
|
||||
use crate::os::fd::{AsFd, AsRawFd};
|
||||
use crate::slice;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -77,8 +74,3 @@ 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 }
|
||||
}
|
|
@ -1,86 +1,82 @@
|
|||
use libc::c_void;
|
||||
|
||||
use super::abi::sockets::iovec;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::slice;
|
||||
use crate::sys::c;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct IoSlice<'a> {
|
||||
vec: iovec,
|
||||
vec: c::WSABUF,
|
||||
_p: PhantomData<&'a [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> IoSlice<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
|
||||
assert!(buf.len() <= u32::MAX as usize);
|
||||
IoSlice {
|
||||
vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() },
|
||||
vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 },
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
if self.vec.iov_len < n {
|
||||
if (self.vec.len as usize) < n {
|
||||
panic!("advancing IoSlice beyond its length");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.vec.iov_len -= n;
|
||||
self.vec.iov_base = self.vec.iov_base.add(n);
|
||||
self.vec.len -= n as u32;
|
||||
self.vec.buf = self.vec.buf.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn as_slice(&self) -> &'a [u8] {
|
||||
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct IoSliceMut<'a> {
|
||||
vec: iovec,
|
||||
vec: c::WSABUF,
|
||||
_p: PhantomData<&'a mut [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> IoSliceMut<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
|
||||
assert!(buf.len() <= u32::MAX as usize);
|
||||
IoSliceMut {
|
||||
vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() },
|
||||
vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() },
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
if self.vec.iov_len < n {
|
||||
if (self.vec.len as usize) < n {
|
||||
panic!("advancing IoSliceMut beyond its length");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.vec.iov_len -= n;
|
||||
self.vec.iov_base = self.vec.iov_base.add(n);
|
||||
self.vec.len -= n as u32;
|
||||
self.vec.buf = self.vec.buf.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn into_slice(self) -> &'a mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_terminal<T>(_: &T) -> bool {
|
||||
false
|
||||
}
|
6
library/std/src/sys/io/is_terminal/hermit.rs
Normal file
6
library/std/src/sys/io/is_terminal/hermit.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use crate::os::fd::{AsFd, AsRawFd};
|
||||
|
||||
pub fn is_terminal(fd: &impl AsFd) -> bool {
|
||||
let fd = fd.as_fd();
|
||||
hermit_abi::isatty(fd.as_raw_fd())
|
||||
}
|
6
library/std/src/sys/io/is_terminal/isatty.rs
Normal file
6
library/std/src/sys/io/is_terminal/isatty.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use crate::os::fd::{AsFd, AsRawFd};
|
||||
|
||||
pub fn is_terminal(fd: &impl AsFd) -> bool {
|
||||
let fd = fd.as_fd();
|
||||
unsafe { libc::isatty(fd.as_raw_fd()) != 0 }
|
||||
}
|
3
library/std/src/sys/io/is_terminal/unsupported.rs
Normal file
3
library/std/src/sys/io/is_terminal/unsupported.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub fn is_terminal<T>(_: &T) -> bool {
|
||||
false
|
||||
}
|
|
@ -1,90 +1,8 @@
|
|||
use core::ffi::c_void;
|
||||
|
||||
use crate::marker::PhantomData;
|
||||
use crate::ffi::c_void;
|
||||
use crate::mem::size_of;
|
||||
use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle};
|
||||
use crate::slice;
|
||||
use crate::sys::c;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct IoSlice<'a> {
|
||||
vec: c::WSABUF,
|
||||
_p: PhantomData<&'a [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> IoSlice<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
|
||||
assert!(buf.len() <= u32::MAX as usize);
|
||||
IoSlice {
|
||||
vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 },
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
if (self.vec.len as usize) < n {
|
||||
panic!("advancing IoSlice beyond its length");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.vec.len -= n as u32;
|
||||
self.vec.buf = self.vec.buf.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn as_slice(&self) -> &'a [u8] {
|
||||
unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct IoSliceMut<'a> {
|
||||
vec: c::WSABUF,
|
||||
_p: PhantomData<&'a mut [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> IoSliceMut<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
|
||||
assert!(buf.len() <= u32::MAX as usize);
|
||||
IoSliceMut {
|
||||
vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() },
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
if (self.vec.len as usize) < n {
|
||||
panic!("advancing IoSliceMut beyond its length");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.vec.len -= n as u32;
|
||||
self.vec.buf = self.vec.buf.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn into_slice(self) -> &'a mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_terminal(h: &impl AsHandle) -> bool {
|
||||
handle_is_console(h.as_handle())
|
||||
}
|
44
library/std/src/sys/io/mod.rs
Normal file
44
library/std/src/sys/io/mod.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
mod io_slice {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3"))] {
|
||||
mod iovec;
|
||||
pub use iovec::*;
|
||||
} else if #[cfg(target_os = "windows")] {
|
||||
mod windows;
|
||||
pub use windows::*;
|
||||
} else if #[cfg(target_os = "wasi")] {
|
||||
mod wasi;
|
||||
pub use wasi::*;
|
||||
} else {
|
||||
mod unsupported;
|
||||
pub use unsupported::*;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod is_terminal {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(target_family = "unix", target_os = "wasi"))] {
|
||||
mod isatty;
|
||||
pub use isatty::*;
|
||||
} else if #[cfg(target_os = "windows")] {
|
||||
mod windows;
|
||||
pub use windows::*;
|
||||
} else if #[cfg(target_os = "hermit")] {
|
||||
mod hermit;
|
||||
pub use hermit::*;
|
||||
} else {
|
||||
mod unsupported;
|
||||
pub use unsupported::*;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use io_slice::{IoSlice, IoSliceMut};
|
||||
pub use is_terminal::is_terminal;
|
||||
|
||||
// Bare metal platforms usually have very small amounts of RAM
|
||||
// (in the order of hundreds of KB)
|
||||
pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 };
|
|
@ -12,6 +12,7 @@ pub mod anonymous_pipe;
|
|||
pub mod backtrace;
|
||||
pub mod cmath;
|
||||
pub mod exit_guard;
|
||||
pub mod io;
|
||||
pub mod net;
|
||||
pub mod os_str;
|
||||
pub mod path;
|
||||
|
|
|
@ -23,7 +23,6 @@ pub mod env;
|
|||
pub mod fd;
|
||||
pub mod fs;
|
||||
pub mod futex;
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
|
|
@ -14,8 +14,6 @@ pub mod env;
|
|||
pub mod fd;
|
||||
#[path = "../unsupported/fs.rs"]
|
||||
pub mod fs;
|
||||
#[path = "../unsupported/io.rs"]
|
||||
pub mod io;
|
||||
mod libunwind_integration;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
|
|
|
@ -62,7 +62,7 @@ impl io::Write for Stderr {
|
|||
}
|
||||
}
|
||||
|
||||
pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
|
||||
pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
|
||||
|
||||
pub fn is_ebadf(err: &io::Error) -> bool {
|
||||
// FIXME: Rust normally maps Unix EBADF to `Uncategorized`
|
||||
|
|
|
@ -23,7 +23,6 @@ pub mod env;
|
|||
// `crate::sys::error`
|
||||
pub(crate) mod error;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
|
|
@ -13,8 +13,6 @@ pub mod env;
|
|||
//pub mod fd;
|
||||
#[path = "../unsupported/fs.rs"]
|
||||
pub mod fs;
|
||||
#[path = "../unsupported/io.rs"]
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
|
|
@ -17,8 +17,6 @@ pub mod args;
|
|||
pub mod env;
|
||||
pub mod fs;
|
||||
pub mod helpers;
|
||||
#[path = "../unsupported/io.rs"]
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
use libc::{c_void, iovec};
|
||||
|
||||
use crate::marker::PhantomData;
|
||||
use crate::os::fd::{AsFd, AsRawFd};
|
||||
use crate::slice;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct IoSlice<'a> {
|
||||
vec: iovec,
|
||||
_p: PhantomData<&'a [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> IoSlice<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
|
||||
IoSlice {
|
||||
vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() },
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
if self.vec.iov_len < n {
|
||||
panic!("advancing IoSlice beyond its length");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.vec.iov_len -= n;
|
||||
self.vec.iov_base = self.vec.iov_base.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn as_slice(&self) -> &'a [u8] {
|
||||
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct IoSliceMut<'a> {
|
||||
vec: iovec,
|
||||
_p: PhantomData<&'a mut [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> IoSliceMut<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
|
||||
IoSliceMut {
|
||||
vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() },
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
if self.vec.iov_len < n {
|
||||
panic!("advancing IoSliceMut beyond its length");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.vec.iov_len -= n;
|
||||
self.vec.iov_base = self.vec.iov_base.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn into_slice(self) -> &'a mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
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 }
|
||||
}
|
|
@ -2,7 +2,7 @@ use crate::fs::OpenOptions;
|
|||
use crate::io;
|
||||
use crate::io::{BufRead, Read, Result, Seek, SeekFrom, Write};
|
||||
use crate::os::unix::io::AsRawFd;
|
||||
use crate::sys_common::io::test::tmpdir;
|
||||
use crate::test_helpers::tmpdir;
|
||||
|
||||
#[test]
|
||||
fn copy_specialization() -> Result<()> {
|
||||
|
|
|
@ -11,7 +11,6 @@ pub mod env;
|
|||
pub mod fd;
|
||||
pub mod fs;
|
||||
pub mod futex;
|
||||
pub mod io;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub mod kernel_copy;
|
||||
#[cfg(target_os = "linux")]
|
||||
|
|
|
@ -92,7 +92,7 @@ pub fn is_ebadf(err: &io::Error) -> bool {
|
|||
err.raw_os_error() == Some(libc::EBADF as i32)
|
||||
}
|
||||
|
||||
pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
|
||||
pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
|
||||
|
||||
pub fn panic_output() -> Option<impl io::Write> {
|
||||
Some(Stderr::new())
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
pub mod args;
|
||||
pub mod env;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
pub mod pipe;
|
||||
pub mod process;
|
||||
|
|
|
@ -20,7 +20,6 @@ pub mod fs;
|
|||
#[allow(unused)]
|
||||
#[path = "../wasm/atomics/futex.rs"]
|
||||
pub mod futex;
|
||||
pub mod io;
|
||||
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
|
|
|
@ -101,7 +101,7 @@ impl io::Write for Stderr {
|
|||
}
|
||||
}
|
||||
|
||||
pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
|
||||
pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
|
||||
|
||||
pub fn is_ebadf(err: &io::Error) -> bool {
|
||||
err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into())
|
||||
|
|
|
@ -17,8 +17,6 @@ pub mod fs;
|
|||
#[allow(unused)]
|
||||
#[path = "../wasm/atomics/futex.rs"]
|
||||
pub mod futex;
|
||||
#[path = "../wasi/io.rs"]
|
||||
pub mod io;
|
||||
|
||||
#[path = "../wasi/os.rs"]
|
||||
pub mod os;
|
||||
|
|
|
@ -21,8 +21,6 @@ pub mod args;
|
|||
pub mod env;
|
||||
#[path = "../unsupported/fs.rs"]
|
||||
pub mod fs;
|
||||
#[path = "../unsupported/io.rs"]
|
||||
pub mod io;
|
||||
#[path = "../unsupported/os.rs"]
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
|
|
|
@ -21,7 +21,6 @@ pub mod fs;
|
|||
#[cfg(not(target_vendor = "win7"))]
|
||||
pub mod futex;
|
||||
pub mod handle;
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
pub mod pipe;
|
||||
pub mod process;
|
||||
|
|
|
@ -158,7 +158,7 @@ fn windows_exe_resolver() {
|
|||
use super::resolve_exe;
|
||||
use crate::io;
|
||||
use crate::sys::fs::symlink;
|
||||
use crate::sys_common::io::test::tmpdir;
|
||||
use crate::test_helpers::tmpdir;
|
||||
|
||||
let env_paths = || env::var_os("PATH");
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ pub mod args;
|
|||
pub mod env;
|
||||
#[path = "../unsupported/fs.rs"]
|
||||
pub mod fs;
|
||||
#[path = "../unsupported/io.rs"]
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
|
|
@ -16,8 +16,6 @@ pub mod args;
|
|||
pub mod env;
|
||||
#[path = "../unsupported/fs.rs"]
|
||||
pub mod fs;
|
||||
#[path = "../unsupported/io.rs"]
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
|
|
@ -54,7 +54,7 @@ impl io::Write for Stderr {
|
|||
}
|
||||
}
|
||||
|
||||
pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
|
||||
pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
|
||||
|
||||
pub fn is_ebadf(_err: &io::Error) -> bool {
|
||||
true
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
// Bare metal platforms usually have very small amounts of RAM
|
||||
// (in the order of hundreds of KB)
|
||||
pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 };
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)] // not used on emscripten and wasi
|
||||
pub mod test {
|
||||
use rand::RngCore;
|
||||
|
||||
use crate::path::{Path, PathBuf};
|
||||
use crate::{env, fs, thread};
|
||||
|
||||
pub struct TempDir(PathBuf);
|
||||
|
||||
impl TempDir {
|
||||
pub fn join(&self, path: &str) -> PathBuf {
|
||||
let TempDir(ref p) = *self;
|
||||
p.join(path)
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &Path {
|
||||
let TempDir(ref p) = *self;
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TempDir {
|
||||
fn drop(&mut self) {
|
||||
// Gee, seeing how we're testing the fs module I sure hope that we
|
||||
// at least implement this correctly!
|
||||
let TempDir(ref p) = *self;
|
||||
let result = fs::remove_dir_all(p);
|
||||
// Avoid panicking while panicking as this causes the process to
|
||||
// immediately abort, without displaying test results.
|
||||
if !thread::panicking() {
|
||||
result.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller] // for `test_rng`
|
||||
pub fn tmpdir() -> TempDir {
|
||||
let p = env::temp_dir();
|
||||
let mut r = crate::test_helpers::test_rng();
|
||||
let ret = p.join(&format!("rust-{}", r.next_u32()));
|
||||
fs::create_dir(&ret).unwrap();
|
||||
TempDir(ret)
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@
|
|||
mod tests;
|
||||
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod process;
|
||||
pub mod wstr;
|
||||
pub mod wtf8;
|
||||
|
|
65
library/std/src/test_helpers.rs
Normal file
65
library/std/src/test_helpers.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use rand::{RngCore, SeedableRng};
|
||||
|
||||
use crate::hash::{BuildHasher, Hash, Hasher, RandomState};
|
||||
use crate::panic::Location;
|
||||
use crate::path::{Path, PathBuf};
|
||||
use crate::{env, fs, thread};
|
||||
|
||||
/// Test-only replacement for `rand::thread_rng()`, which is unusable for
|
||||
/// us, as we want to allow running stdlib tests on tier-3 targets which may
|
||||
/// not have `getrandom` support.
|
||||
///
|
||||
/// Does a bit of a song and dance to ensure that the seed is different on
|
||||
/// each call (as some tests sadly rely on this), but doesn't try that hard.
|
||||
///
|
||||
/// This is duplicated in the `core`, `alloc` test suites (as well as
|
||||
/// `std`'s integration tests), but figuring out a mechanism to share these
|
||||
/// seems far more painful than copy-pasting a 7 line function a couple
|
||||
/// times, given that even under a perma-unstable feature, I don't think we
|
||||
/// want to expose types from `rand` from `std`.
|
||||
#[track_caller]
|
||||
pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng {
|
||||
let mut hasher = RandomState::new().build_hasher();
|
||||
Location::caller().hash(&mut hasher);
|
||||
let hc64 = hasher.finish();
|
||||
let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>();
|
||||
let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap();
|
||||
SeedableRng::from_seed(seed)
|
||||
}
|
||||
|
||||
pub struct TempDir(PathBuf);
|
||||
|
||||
impl TempDir {
|
||||
pub fn join(&self, path: &str) -> PathBuf {
|
||||
let TempDir(ref p) = *self;
|
||||
p.join(path)
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &Path {
|
||||
let TempDir(ref p) = *self;
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TempDir {
|
||||
fn drop(&mut self) {
|
||||
// Gee, seeing how we're testing the fs module I sure hope that we
|
||||
// at least implement this correctly!
|
||||
let TempDir(ref p) = *self;
|
||||
let result = fs::remove_dir_all(p);
|
||||
// Avoid panicking while panicking as this causes the process to
|
||||
// immediately abort, without displaying test results.
|
||||
if !thread::panicking() {
|
||||
result.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller] // for `test_rng`
|
||||
pub fn tmpdir() -> TempDir {
|
||||
let p = env::temp_dir();
|
||||
let mut r = test_rng();
|
||||
let ret = p.join(&format!("rust-{}", r.next_u32()));
|
||||
fs::create_dir(&ret).unwrap();
|
||||
TempDir(ret)
|
||||
}
|
|
@ -18,7 +18,7 @@ pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng {
|
|||
rand::SeedableRng::from_seed(seed)
|
||||
}
|
||||
|
||||
// Copied from std::sys_common::io
|
||||
// Copied from std::test_helpers
|
||||
pub(crate) struct TempDir(PathBuf);
|
||||
|
||||
impl TempDir {
|
||||
|
|
|
@ -582,7 +582,7 @@ impl Step for Rustdoc {
|
|||
if !target_compiler.is_snapshot(builder) {
|
||||
panic!("rustdoc in stage 0 must be snapshot rustdoc");
|
||||
}
|
||||
return builder.initial_rustc.with_file_name(exe("rustdoc", target_compiler.host));
|
||||
return builder.initial_rustdoc.clone();
|
||||
}
|
||||
let target = target_compiler.host;
|
||||
|
||||
|
|
|
@ -154,6 +154,7 @@ pub struct Build {
|
|||
targets: Vec<TargetSelection>,
|
||||
|
||||
initial_rustc: PathBuf,
|
||||
initial_rustdoc: PathBuf,
|
||||
initial_cargo: PathBuf,
|
||||
initial_lld: PathBuf,
|
||||
initial_relative_libdir: PathBuf,
|
||||
|
@ -354,6 +355,7 @@ impl Build {
|
|||
initial_lld,
|
||||
initial_relative_libdir,
|
||||
initial_rustc: config.initial_rustc.clone(),
|
||||
initial_rustdoc: config.initial_rustc.with_file_name(exe("rustdoc", config.build)),
|
||||
initial_cargo: config.initial_cargo.clone(),
|
||||
initial_sysroot: config.initial_sysroot.clone(),
|
||||
local_rebuild: config.local_rebuild,
|
||||
|
|
|
@ -167,10 +167,10 @@ auto:
|
|||
<<: *job-linux-4c
|
||||
|
||||
- name: dist-loongarch64-linux
|
||||
<<: *job-linux-4c
|
||||
<<: *job-linux-4c-largedisk
|
||||
|
||||
- name: dist-loongarch64-musl
|
||||
<<: *job-linux-4c
|
||||
<<: *job-linux-4c-largedisk
|
||||
|
||||
- name: dist-ohos
|
||||
<<: *job-linux-4c
|
||||
|
|
|
@ -144,8 +144,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::doc::DOC_NESTED_REFDEFS_INFO,
|
||||
crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO,
|
||||
crate::doc::EMPTY_DOCS_INFO,
|
||||
crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||
crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
||||
crate::doc::MISSING_ERRORS_DOC_INFO,
|
||||
crate::doc::MISSING_PANICS_DOC_INFO,
|
||||
crate::doc::MISSING_SAFETY_DOC_INFO,
|
||||
|
@ -162,6 +160,8 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
|
||||
crate::empty_drop::EMPTY_DROP_INFO,
|
||||
crate::empty_enum::EMPTY_ENUM_INFO,
|
||||
crate::empty_line_after::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||
crate::empty_line_after::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
||||
crate::empty_with_brackets::EMPTY_ENUM_VARIANTS_WITH_BRACKETS_INFO,
|
||||
crate::empty_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
|
||||
crate::endian_bytes::BIG_ENDIAN_BYTES_INFO,
|
||||
|
|
|
@ -1,345 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{SpanRangeExt, snippet_indent};
|
||||
use clippy_utils::tokenize_with_text;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::AttrStyle;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_errors::{Applicability, Diag, SuggestionStyle};
|
||||
use rustc_hir::{AttrKind, Attribute, ItemKind, Node};
|
||||
use rustc_lexer::TokenKind;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{BytePos, ExpnKind, InnerSpan, Span, SpanData};
|
||||
|
||||
use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
enum StopKind {
|
||||
Attr,
|
||||
Doc(CommentKind),
|
||||
}
|
||||
|
||||
impl StopKind {
|
||||
fn is_doc(self) -> bool {
|
||||
matches!(self, StopKind::Doc(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Stop {
|
||||
span: Span,
|
||||
kind: StopKind,
|
||||
first: usize,
|
||||
last: usize,
|
||||
}
|
||||
|
||||
impl Stop {
|
||||
fn convert_to_inner(&self) -> (Span, String) {
|
||||
let inner = match self.kind {
|
||||
// #|[...]
|
||||
StopKind::Attr => InnerSpan::new(1, 1),
|
||||
// /// or /**
|
||||
// ^ ^
|
||||
StopKind::Doc(_) => InnerSpan::new(2, 3),
|
||||
};
|
||||
(self.span.from_inner(inner), "!".into())
|
||||
}
|
||||
|
||||
fn comment_out(&self, cx: &LateContext<'_>, suggestions: &mut Vec<(Span, String)>) {
|
||||
match self.kind {
|
||||
StopKind::Attr => {
|
||||
if cx.tcx.sess.source_map().is_multiline(self.span) {
|
||||
suggestions.extend([
|
||||
(self.span.shrink_to_lo(), "/* ".into()),
|
||||
(self.span.shrink_to_hi(), " */".into()),
|
||||
]);
|
||||
} else {
|
||||
suggestions.push((self.span.shrink_to_lo(), "// ".into()));
|
||||
}
|
||||
},
|
||||
StopKind::Doc(CommentKind::Line) => suggestions.push((self.span.shrink_to_lo(), "// ".into())),
|
||||
StopKind::Doc(CommentKind::Block) => {
|
||||
// /** outer */ /*! inner */
|
||||
// ^ ^
|
||||
let asterisk = self.span.from_inner(InnerSpan::new(1, 2));
|
||||
suggestions.push((asterisk, String::new()));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_attr(cx: &LateContext<'_>, attr: &Attribute) -> Option<Self> {
|
||||
let SpanData { lo, hi, .. } = attr.span.data();
|
||||
let file = cx.tcx.sess.source_map().lookup_source_file(lo);
|
||||
|
||||
Some(Self {
|
||||
span: attr.span,
|
||||
kind: match attr.kind {
|
||||
AttrKind::Normal(_) => StopKind::Attr,
|
||||
AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind),
|
||||
},
|
||||
first: file.lookup_line(file.relative_position(lo))?,
|
||||
last: file.lookup_line(file.relative_position(hi))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a set of attrs/doc comments separated by 1 or more empty lines
|
||||
///
|
||||
/// ```ignore
|
||||
/// /// chunk 1 docs
|
||||
/// // not an empty line so also part of chunk 1
|
||||
/// #[chunk_1_attrs] // <-- prev_stop
|
||||
///
|
||||
/// /* gap */
|
||||
///
|
||||
/// /// chunk 2 docs // <-- next_stop
|
||||
/// #[chunk_2_attrs]
|
||||
/// ```
|
||||
struct Gap<'a> {
|
||||
/// The span of individual empty lines including the newline at the end of the line
|
||||
empty_lines: Vec<Span>,
|
||||
has_comment: bool,
|
||||
next_stop: &'a Stop,
|
||||
prev_stop: &'a Stop,
|
||||
/// The chunk that includes [`prev_stop`](Self::prev_stop)
|
||||
prev_chunk: &'a [Stop],
|
||||
}
|
||||
|
||||
impl<'a> Gap<'a> {
|
||||
fn new(cx: &LateContext<'_>, prev_chunk: &'a [Stop], next_chunk: &'a [Stop]) -> Option<Self> {
|
||||
let prev_stop = prev_chunk.last()?;
|
||||
let next_stop = next_chunk.first()?;
|
||||
let gap_span = prev_stop.span.between(next_stop.span);
|
||||
let gap_snippet = gap_span.get_source_text(cx)?;
|
||||
|
||||
let mut has_comment = false;
|
||||
let mut empty_lines = Vec::new();
|
||||
|
||||
for (token, source, inner_span) in tokenize_with_text(&gap_snippet) {
|
||||
match token {
|
||||
TokenKind::BlockComment {
|
||||
doc_style: None,
|
||||
terminated: true,
|
||||
}
|
||||
| TokenKind::LineComment { doc_style: None } => has_comment = true,
|
||||
TokenKind::Whitespace => {
|
||||
let newlines = source.bytes().positions(|b| b == b'\n');
|
||||
empty_lines.extend(
|
||||
newlines
|
||||
.tuple_windows()
|
||||
.map(|(a, b)| InnerSpan::new(inner_span.start + a + 1, inner_span.start + b))
|
||||
.map(|inner_span| gap_span.from_inner(inner_span)),
|
||||
);
|
||||
},
|
||||
// Ignore cfg_attr'd out attributes as they may contain empty lines, could also be from macro
|
||||
// shenanigans
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
(!empty_lines.is_empty()).then_some(Self {
|
||||
empty_lines,
|
||||
has_comment,
|
||||
next_stop,
|
||||
prev_stop,
|
||||
prev_chunk,
|
||||
})
|
||||
}
|
||||
|
||||
fn contiguous_empty_lines(&self) -> impl Iterator<Item = Span> + '_ {
|
||||
self.empty_lines
|
||||
// The `+ BytePos(1)` means "next line", because each empty line span is "N:1-N:1".
|
||||
.chunk_by(|a, b| a.hi() + BytePos(1) == b.lo())
|
||||
.map(|chunk| {
|
||||
let first = chunk.first().expect("at least one empty line");
|
||||
let last = chunk.last().expect("at least one empty line");
|
||||
// The BytePos subtraction here is safe, as before an empty line, there must be at least one
|
||||
// attribute/comment. The span needs to start at the end of the previous line.
|
||||
first.with_lo(first.lo() - BytePos(1)).with_hi(last.hi())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// If the node the attributes/docs apply to is the first in the module/crate suggest converting
|
||||
/// them to inner attributes/docs
|
||||
fn suggest_inner(cx: &LateContext<'_>, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>]) {
|
||||
let Some(owner) = cx.last_node_with_lint_attrs.as_owner() else {
|
||||
return;
|
||||
};
|
||||
let parent_desc = match cx.tcx.parent_hir_node(owner.into()) {
|
||||
Node::Item(item)
|
||||
if let ItemKind::Mod(parent_mod) = item.kind
|
||||
&& let [first, ..] = parent_mod.item_ids
|
||||
&& first.owner_id == owner =>
|
||||
{
|
||||
"parent module"
|
||||
},
|
||||
Node::Crate(crate_mod)
|
||||
if let Some(first) = crate_mod
|
||||
.item_ids
|
||||
.iter()
|
||||
.map(|&id| cx.tcx.hir().item(id))
|
||||
// skip prelude imports
|
||||
.find(|item| !matches!(item.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_)))
|
||||
&& first.owner_id == owner =>
|
||||
{
|
||||
"crate"
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
diag.multipart_suggestion_verbose(
|
||||
match kind {
|
||||
StopKind::Attr => format!("if the attribute should apply to the {parent_desc} use an inner attribute"),
|
||||
StopKind::Doc(_) => format!("if the comment should document the {parent_desc} use an inner doc comment"),
|
||||
},
|
||||
gaps.iter()
|
||||
.flat_map(|gap| gap.prev_chunk)
|
||||
.map(Stop::convert_to_inner)
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
||||
fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool {
|
||||
let Some(first_gap) = gaps.first() else {
|
||||
return false;
|
||||
};
|
||||
let empty_lines = || gaps.iter().flat_map(|gap| gap.empty_lines.iter().copied());
|
||||
let contiguous_empty_lines = || gaps.iter().flat_map(Gap::contiguous_empty_lines);
|
||||
let mut has_comment = false;
|
||||
let mut has_attr = false;
|
||||
for gap in gaps {
|
||||
has_comment |= gap.has_comment;
|
||||
if !has_attr {
|
||||
has_attr = gap.prev_chunk.iter().any(|stop| stop.kind == StopKind::Attr);
|
||||
}
|
||||
}
|
||||
let kind = first_gap.prev_stop.kind;
|
||||
let (lint, kind_desc) = match kind {
|
||||
StopKind::Attr => (EMPTY_LINE_AFTER_OUTER_ATTR, "outer attribute"),
|
||||
StopKind::Doc(_) => (EMPTY_LINE_AFTER_DOC_COMMENTS, "doc comment"),
|
||||
};
|
||||
let (lines, are, them) = if empty_lines().nth(1).is_some() {
|
||||
("lines", "are", "them")
|
||||
} else {
|
||||
("line", "is", "it")
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
first_gap.prev_stop.span.to(empty_lines().last().unwrap()),
|
||||
format!("empty {lines} after {kind_desc}"),
|
||||
|diag| {
|
||||
if let Some(owner) = cx.last_node_with_lint_attrs.as_owner() {
|
||||
let def_id = owner.to_def_id();
|
||||
let def_descr = cx.tcx.def_descr(def_id);
|
||||
diag.span_label(
|
||||
cx.tcx.def_span(def_id),
|
||||
match kind {
|
||||
StopKind::Attr => format!("the attribute applies to this {def_descr}"),
|
||||
StopKind::Doc(_) => format!("the comment documents this {def_descr}"),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
diag.multipart_suggestion_with_style(
|
||||
format!("if the empty {lines} {are} unintentional remove {them}"),
|
||||
contiguous_empty_lines()
|
||||
.map(|empty_lines| (empty_lines, String::new()))
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
SuggestionStyle::HideCodeAlways,
|
||||
);
|
||||
|
||||
if has_comment && kind.is_doc() {
|
||||
// Likely doc comments that applied to some now commented out code
|
||||
//
|
||||
// /// Old docs for Foo
|
||||
// // struct Foo;
|
||||
|
||||
let mut suggestions = Vec::new();
|
||||
for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) {
|
||||
stop.comment_out(cx, &mut suggestions);
|
||||
}
|
||||
let name = match cx.tcx.hir().opt_name(cx.last_node_with_lint_attrs) {
|
||||
Some(name) => format!("`{name}`"),
|
||||
None => "this".into(),
|
||||
};
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("if the doc comment should not document {name} comment it out"),
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
suggest_inner(cx, diag, kind, gaps);
|
||||
}
|
||||
|
||||
if kind == StopKind::Doc(CommentKind::Line)
|
||||
&& gaps
|
||||
.iter()
|
||||
.all(|gap| !gap.has_comment && gap.next_stop.kind == StopKind::Doc(CommentKind::Line))
|
||||
{
|
||||
// Commentless empty gaps between line doc comments, possibly intended to be part of the markdown
|
||||
|
||||
let indent = snippet_indent(cx, first_gap.prev_stop.span).unwrap_or_default();
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("if the documentation should include the empty {lines} include {them} in the comment"),
|
||||
empty_lines()
|
||||
.map(|empty_line| (empty_line, format!("{indent}///")))
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
kind.is_doc()
|
||||
}
|
||||
|
||||
/// Returns `true` if [`EMPTY_LINE_AFTER_DOC_COMMENTS`] triggered, used to skip other doc comment
|
||||
/// lints where they would be confusing
|
||||
///
|
||||
/// [`EMPTY_LINE_AFTER_OUTER_ATTR`] is also here to share an implementation but does not return
|
||||
/// `true` if it triggers
|
||||
pub(super) fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool {
|
||||
let mut outer = attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.style == AttrStyle::Outer && !attr.span.from_expansion())
|
||||
.map(|attr| Stop::from_attr(cx, attr))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
|
||||
if outer.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Push a fake attribute Stop for the item itself so we check for gaps between the last outer
|
||||
// attr/doc comment and the item they apply to
|
||||
let span = cx.tcx.hir().span(cx.last_node_with_lint_attrs);
|
||||
if !span.from_expansion()
|
||||
&& let Ok(line) = cx.tcx.sess.source_map().lookup_line(span.lo())
|
||||
{
|
||||
outer.push(Stop {
|
||||
span,
|
||||
kind: StopKind::Attr,
|
||||
first: line.line,
|
||||
// last doesn't need to be accurate here, we don't compare it with anything
|
||||
last: line.line,
|
||||
});
|
||||
}
|
||||
|
||||
let mut gaps = Vec::new();
|
||||
let mut last = 0;
|
||||
for pos in outer
|
||||
.array_windows()
|
||||
.positions(|[a, b]| b.first.saturating_sub(a.last) > 1)
|
||||
{
|
||||
// we want to be after the first stop in the window
|
||||
let pos = pos + 1;
|
||||
if let Some(gap) = Gap::new(cx, &outer[last..pos], &outer[pos..]) {
|
||||
last = pos;
|
||||
gaps.push(gap);
|
||||
}
|
||||
}
|
||||
|
||||
check_gaps(cx, &gaps)
|
||||
}
|
|
@ -33,7 +33,6 @@ use rustc_span::{Span, sym};
|
|||
use std::ops::Range;
|
||||
use url::Url;
|
||||
|
||||
mod empty_line_after;
|
||||
mod include_in_doc_without_cfg;
|
||||
mod link_with_quotes;
|
||||
mod markdown;
|
||||
|
@ -491,82 +490,6 @@ declare_clippy_lint! {
|
|||
"ensure the first documentation paragraph is short"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after outer attributes
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute may have meant to be an inner attribute (`#![attr]`). If
|
||||
/// it was meant to be an outer attribute (`#[attr]`) then the empty line
|
||||
/// should be removed
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[allow(dead_code)]
|
||||
///
|
||||
/// fn not_quite_good_code() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Good (as inner attribute)
|
||||
/// #![allow(dead_code)]
|
||||
///
|
||||
/// fn this_is_fine() {}
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[allow(dead_code)]
|
||||
/// fn this_is_fine_too() {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
suspicious,
|
||||
"empty line after outer attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after doc comments.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The doc comment may have meant to be an inner doc comment, regular
|
||||
/// comment or applied to some old code that is now commented out. If it was
|
||||
/// intended to be a doc comment, then the empty line should be removed.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// Some doc comment with a blank line after it.
|
||||
///
|
||||
/// fn f() {}
|
||||
///
|
||||
/// /// Docs for `old_code`
|
||||
/// // fn old_code() {}
|
||||
///
|
||||
/// fn new_code() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// //! Convert it to an inner doc comment
|
||||
///
|
||||
/// // Or a regular comment
|
||||
///
|
||||
/// /// Or remove the empty line
|
||||
/// fn f() {}
|
||||
///
|
||||
/// // /// Docs for `old_code`
|
||||
/// // fn old_code() {}
|
||||
///
|
||||
/// fn new_code() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
suspicious,
|
||||
"empty line after doc comments"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if included files in doc comments are included only for `cfg(doc)`.
|
||||
|
@ -650,8 +573,6 @@ impl_lint_pass!(Documentation => [
|
|||
EMPTY_DOCS,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
DOC_INCLUDE_WITHOUT_CFG,
|
||||
]);
|
||||
|
@ -784,7 +705,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
|||
}
|
||||
|
||||
include_in_doc_without_cfg::check(cx, attrs);
|
||||
if suspicious_doc_comments::check(cx, attrs) || empty_line_after::check(cx, attrs) || is_doc_hidden(attrs) {
|
||||
if suspicious_doc_comments::check(cx, attrs) || is_doc_hidden(attrs) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
492
src/tools/clippy/clippy_lints/src/empty_line_after.rs
Normal file
492
src/tools/clippy/clippy_lints/src/empty_line_after.rs
Normal file
|
@ -0,0 +1,492 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{SpanRangeExt, snippet_indent};
|
||||
use clippy_utils::tokenize_with_text;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::token::CommentKind;
|
||||
use rustc_ast::{AssocItemKind, AttrKind, AttrStyle, Attribute, Crate, Item, ItemKind, ModKind, NodeId};
|
||||
use rustc_errors::{Applicability, Diag, SuggestionStyle};
|
||||
use rustc_lexer::TokenKind;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after outer attributes
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute may have meant to be an inner attribute (`#![attr]`). If
|
||||
/// it was meant to be an outer attribute (`#[attr]`) then the empty line
|
||||
/// should be removed
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[allow(dead_code)]
|
||||
///
|
||||
/// fn not_quite_good_code() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Good (as inner attribute)
|
||||
/// #![allow(dead_code)]
|
||||
///
|
||||
/// fn this_is_fine() {}
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[allow(dead_code)]
|
||||
/// fn this_is_fine_too() {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
suspicious,
|
||||
"empty line after outer attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after doc comments.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The doc comment may have meant to be an inner doc comment, regular
|
||||
/// comment or applied to some old code that is now commented out. If it was
|
||||
/// intended to be a doc comment, then the empty line should be removed.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// Some doc comment with a blank line after it.
|
||||
///
|
||||
/// fn f() {}
|
||||
///
|
||||
/// /// Docs for `old_code`
|
||||
/// // fn old_code() {}
|
||||
///
|
||||
/// fn new_code() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// //! Convert it to an inner doc comment
|
||||
///
|
||||
/// // Or a regular comment
|
||||
///
|
||||
/// /// Or remove the empty line
|
||||
/// fn f() {}
|
||||
///
|
||||
/// // /// Docs for `old_code`
|
||||
/// // fn old_code() {}
|
||||
///
|
||||
/// fn new_code() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
suspicious,
|
||||
"empty line after doc comments"
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ItemInfo {
|
||||
kind: &'static str,
|
||||
name: Symbol,
|
||||
span: Span,
|
||||
mod_items: Option<NodeId>,
|
||||
}
|
||||
|
||||
pub struct EmptyLineAfter {
|
||||
items: Vec<ItemInfo>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyLineAfter => [
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
]);
|
||||
|
||||
impl EmptyLineAfter {
|
||||
pub fn new() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
enum StopKind {
|
||||
Attr,
|
||||
Doc(CommentKind),
|
||||
}
|
||||
|
||||
impl StopKind {
|
||||
fn is_doc(self) -> bool {
|
||||
matches!(self, StopKind::Doc(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Stop {
|
||||
span: Span,
|
||||
kind: StopKind,
|
||||
first: usize,
|
||||
last: usize,
|
||||
}
|
||||
|
||||
impl Stop {
|
||||
fn convert_to_inner(&self) -> (Span, String) {
|
||||
let inner = match self.kind {
|
||||
// #![...]
|
||||
StopKind::Attr => InnerSpan::new(1, 1),
|
||||
// /// or /**
|
||||
// ^ ^
|
||||
StopKind::Doc(_) => InnerSpan::new(2, 3),
|
||||
};
|
||||
(self.span.from_inner(inner), "!".into())
|
||||
}
|
||||
|
||||
fn comment_out(&self, cx: &EarlyContext<'_>, suggestions: &mut Vec<(Span, String)>) {
|
||||
match self.kind {
|
||||
StopKind::Attr => {
|
||||
if cx.sess().source_map().is_multiline(self.span) {
|
||||
suggestions.extend([
|
||||
(self.span.shrink_to_lo(), "/* ".into()),
|
||||
(self.span.shrink_to_hi(), " */".into()),
|
||||
]);
|
||||
} else {
|
||||
suggestions.push((self.span.shrink_to_lo(), "// ".into()));
|
||||
}
|
||||
},
|
||||
StopKind::Doc(CommentKind::Line) => suggestions.push((self.span.shrink_to_lo(), "// ".into())),
|
||||
StopKind::Doc(CommentKind::Block) => {
|
||||
// /** outer */ /*! inner */
|
||||
// ^ ^
|
||||
let asterisk = self.span.from_inner(InnerSpan::new(1, 2));
|
||||
suggestions.push((asterisk, String::new()));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_attr(cx: &EarlyContext<'_>, attr: &Attribute) -> Option<Self> {
|
||||
let SpanData { lo, hi, .. } = attr.span.data();
|
||||
let file = cx.sess().source_map().lookup_source_file(lo);
|
||||
|
||||
Some(Self {
|
||||
span: attr.span,
|
||||
kind: match attr.kind {
|
||||
AttrKind::Normal(_) => StopKind::Attr,
|
||||
AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind),
|
||||
},
|
||||
first: file.lookup_line(file.relative_position(lo))?,
|
||||
last: file.lookup_line(file.relative_position(hi))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a set of attrs/doc comments separated by 1 or more empty lines
|
||||
///
|
||||
/// ```ignore
|
||||
/// /// chunk 1 docs
|
||||
/// // not an empty line so also part of chunk 1
|
||||
/// #[chunk_1_attrs] // <-- prev_stop
|
||||
///
|
||||
/// /* gap */
|
||||
///
|
||||
/// /// chunk 2 docs // <-- next_stop
|
||||
/// #[chunk_2_attrs]
|
||||
/// ```
|
||||
struct Gap<'a> {
|
||||
/// The span of individual empty lines including the newline at the end of the line
|
||||
empty_lines: Vec<Span>,
|
||||
has_comment: bool,
|
||||
next_stop: &'a Stop,
|
||||
prev_stop: &'a Stop,
|
||||
/// The chunk that includes [`prev_stop`](Self::prev_stop)
|
||||
prev_chunk: &'a [Stop],
|
||||
}
|
||||
|
||||
impl<'a> Gap<'a> {
|
||||
fn new(cx: &EarlyContext<'_>, prev_chunk: &'a [Stop], next_chunk: &'a [Stop]) -> Option<Self> {
|
||||
let prev_stop = prev_chunk.last()?;
|
||||
let next_stop = next_chunk.first()?;
|
||||
let gap_span = prev_stop.span.between(next_stop.span);
|
||||
let gap_snippet = gap_span.get_source_text(cx)?;
|
||||
|
||||
let mut has_comment = false;
|
||||
let mut empty_lines = Vec::new();
|
||||
|
||||
for (token, source, inner_span) in tokenize_with_text(&gap_snippet) {
|
||||
match token {
|
||||
TokenKind::BlockComment {
|
||||
doc_style: None,
|
||||
terminated: true,
|
||||
}
|
||||
| TokenKind::LineComment { doc_style: None } => has_comment = true,
|
||||
TokenKind::Whitespace => {
|
||||
let newlines = source.bytes().positions(|b| b == b'\n');
|
||||
empty_lines.extend(
|
||||
newlines
|
||||
.tuple_windows()
|
||||
.map(|(a, b)| InnerSpan::new(inner_span.start + a + 1, inner_span.start + b))
|
||||
.map(|inner_span| gap_span.from_inner(inner_span)),
|
||||
);
|
||||
},
|
||||
// Ignore cfg_attr'd out attributes as they may contain empty lines, could also be from macro
|
||||
// shenanigans
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
(!empty_lines.is_empty()).then_some(Self {
|
||||
empty_lines,
|
||||
has_comment,
|
||||
next_stop,
|
||||
prev_stop,
|
||||
prev_chunk,
|
||||
})
|
||||
}
|
||||
|
||||
fn contiguous_empty_lines(&self) -> impl Iterator<Item = Span> + '_ {
|
||||
self.empty_lines
|
||||
// The `+ BytePos(1)` means "next line", because each empty line span is "N:1-N:1".
|
||||
.chunk_by(|a, b| a.hi() + BytePos(1) == b.lo())
|
||||
.map(|chunk| {
|
||||
let first = chunk.first().expect("at least one empty line");
|
||||
let last = chunk.last().expect("at least one empty line");
|
||||
// The BytePos subtraction here is safe, as before an empty line, there must be at least one
|
||||
// attribute/comment. The span needs to start at the end of the previous line.
|
||||
first.with_lo(first.lo() - BytePos(1)).with_hi(last.hi())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl EmptyLineAfter {
|
||||
fn check_gaps(&self, cx: &EarlyContext<'_>, gaps: &[Gap<'_>], id: NodeId) {
|
||||
let Some(first_gap) = gaps.first() else {
|
||||
return;
|
||||
};
|
||||
let empty_lines = || gaps.iter().flat_map(|gap| gap.empty_lines.iter().copied());
|
||||
let contiguous_empty_lines = || gaps.iter().flat_map(Gap::contiguous_empty_lines);
|
||||
let mut has_comment = false;
|
||||
let mut has_attr = false;
|
||||
for gap in gaps {
|
||||
has_comment |= gap.has_comment;
|
||||
if !has_attr {
|
||||
has_attr = gap.prev_chunk.iter().any(|stop| stop.kind == StopKind::Attr);
|
||||
}
|
||||
}
|
||||
let kind = first_gap.prev_stop.kind;
|
||||
let (lint, kind_desc) = match kind {
|
||||
StopKind::Attr => (EMPTY_LINE_AFTER_OUTER_ATTR, "outer attribute"),
|
||||
StopKind::Doc(_) => (EMPTY_LINE_AFTER_DOC_COMMENTS, "doc comment"),
|
||||
};
|
||||
let (lines, are, them) = if empty_lines().nth(1).is_some() {
|
||||
("lines", "are", "them")
|
||||
} else {
|
||||
("line", "is", "it")
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
first_gap.prev_stop.span.to(empty_lines().last().unwrap()),
|
||||
format!("empty {lines} after {kind_desc}"),
|
||||
|diag| {
|
||||
let info = self.items.last().unwrap();
|
||||
diag.span_label(info.span, match kind {
|
||||
StopKind::Attr => format!("the attribute applies to this {}", info.kind),
|
||||
StopKind::Doc(_) => format!("the comment documents this {}", info.kind),
|
||||
});
|
||||
|
||||
diag.multipart_suggestion_with_style(
|
||||
format!("if the empty {lines} {are} unintentional, remove {them}"),
|
||||
contiguous_empty_lines()
|
||||
.map(|empty_lines| (empty_lines, String::new()))
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
SuggestionStyle::HideCodeAlways,
|
||||
);
|
||||
|
||||
if has_comment && kind.is_doc() {
|
||||
// Likely doc comments that applied to some now commented out code
|
||||
//
|
||||
// /// Old docs for Foo
|
||||
// // struct Foo;
|
||||
|
||||
let mut suggestions = Vec::new();
|
||||
for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) {
|
||||
stop.comment_out(cx, &mut suggestions);
|
||||
}
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("if the doc comment should not document `{}` comment it out", info.name),
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
self.suggest_inner(diag, kind, gaps, id);
|
||||
}
|
||||
|
||||
if kind == StopKind::Doc(CommentKind::Line)
|
||||
&& gaps
|
||||
.iter()
|
||||
.all(|gap| !gap.has_comment && gap.next_stop.kind == StopKind::Doc(CommentKind::Line))
|
||||
{
|
||||
// Commentless empty gaps between line doc comments, possibly intended to be part of the markdown
|
||||
|
||||
let indent = snippet_indent(cx, first_gap.prev_stop.span).unwrap_or_default();
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("if the documentation should include the empty {lines} include {them} in the comment"),
|
||||
empty_lines()
|
||||
.map(|empty_line| (empty_line, format!("{indent}///")))
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// If the node the attributes/docs apply to is the first in the module/crate suggest converting
|
||||
/// them to inner attributes/docs
|
||||
fn suggest_inner(&self, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>], id: NodeId) {
|
||||
if let Some(parent) = self.items.iter().rev().nth(1)
|
||||
&& (parent.kind == "module" || parent.kind == "crate")
|
||||
&& parent.mod_items == Some(id)
|
||||
{
|
||||
let desc = if parent.kind == "module" {
|
||||
"parent module"
|
||||
} else {
|
||||
parent.kind
|
||||
};
|
||||
diag.multipart_suggestion_verbose(
|
||||
match kind {
|
||||
StopKind::Attr => format!("if the attribute should apply to the {desc} use an inner attribute"),
|
||||
StopKind::Doc(_) => format!("if the comment should document the {desc} use an inner doc comment"),
|
||||
},
|
||||
gaps.iter()
|
||||
.flat_map(|gap| gap.prev_chunk)
|
||||
.map(Stop::convert_to_inner)
|
||||
.collect(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_kind(
|
||||
&mut self,
|
||||
cx: &EarlyContext<'_>,
|
||||
kind: &ItemKind,
|
||||
ident: &Ident,
|
||||
span: Span,
|
||||
attrs: &[Attribute],
|
||||
id: NodeId,
|
||||
) {
|
||||
self.items.push(ItemInfo {
|
||||
kind: kind.descr(),
|
||||
name: ident.name,
|
||||
span: if span.contains(ident.span) {
|
||||
span.with_hi(ident.span.hi())
|
||||
} else {
|
||||
span.with_hi(span.lo())
|
||||
},
|
||||
mod_items: match kind {
|
||||
ItemKind::Mod(_, ModKind::Loaded(items, _, _, _)) => items
|
||||
.iter()
|
||||
.filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_)))
|
||||
.map(|i| i.id)
|
||||
.next(),
|
||||
_ => None,
|
||||
},
|
||||
});
|
||||
|
||||
let mut outer = attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.style == AttrStyle::Outer && !attr.span.from_expansion())
|
||||
.map(|attr| Stop::from_attr(cx, attr))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
|
||||
if outer.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Push a fake attribute Stop for the item itself so we check for gaps between the last outer
|
||||
// attr/doc comment and the item they apply to
|
||||
let span = self.items.last().unwrap().span;
|
||||
if !span.from_expansion()
|
||||
&& let Ok(line) = cx.sess().source_map().lookup_line(span.lo())
|
||||
{
|
||||
outer.push(Stop {
|
||||
span,
|
||||
kind: StopKind::Attr,
|
||||
first: line.line,
|
||||
// last doesn't need to be accurate here, we don't compare it with anything
|
||||
last: line.line,
|
||||
});
|
||||
}
|
||||
|
||||
let mut gaps = Vec::new();
|
||||
let mut last = 0;
|
||||
for pos in outer
|
||||
.array_windows()
|
||||
.positions(|[a, b]| b.first.saturating_sub(a.last) > 1)
|
||||
{
|
||||
// we want to be after the first stop in the window
|
||||
let pos = pos + 1;
|
||||
if let Some(gap) = Gap::new(cx, &outer[last..pos], &outer[pos..]) {
|
||||
last = pos;
|
||||
gaps.push(gap);
|
||||
}
|
||||
}
|
||||
|
||||
self.check_gaps(cx, &gaps, id);
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for EmptyLineAfter {
|
||||
fn check_crate(&mut self, _: &EarlyContext<'_>, krate: &Crate) {
|
||||
self.items.push(ItemInfo {
|
||||
kind: "crate",
|
||||
name: kw::Crate,
|
||||
span: krate.spans.inner_span.with_hi(krate.spans.inner_span.lo()),
|
||||
mod_items: krate
|
||||
.items
|
||||
.iter()
|
||||
.filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_)))
|
||||
.map(|i| i.id)
|
||||
.next(),
|
||||
});
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) {
|
||||
self.items.pop();
|
||||
}
|
||||
fn check_impl_item_post(&mut self, _: &EarlyContext<'_>, _: &Item<AssocItemKind>) {
|
||||
self.items.pop();
|
||||
}
|
||||
fn check_trait_item_post(&mut self, _: &EarlyContext<'_>, _: &Item<AssocItemKind>) {
|
||||
self.items.pop();
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &Item<AssocItemKind>) {
|
||||
self.check_item_kind(
|
||||
cx,
|
||||
&item.kind.clone().into(),
|
||||
&item.ident,
|
||||
item.span,
|
||||
&item.attrs,
|
||||
item.id,
|
||||
);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &EarlyContext<'_>, item: &Item<AssocItemKind>) {
|
||||
self.check_item_kind(
|
||||
cx,
|
||||
&item.kind.clone().into(),
|
||||
&item.ident,
|
||||
item.span,
|
||||
&item.attrs,
|
||||
item.id,
|
||||
);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
self.check_item_kind(cx, &item.kind, &item.ident, item.span, &item.attrs, item.id);
|
||||
}
|
||||
}
|
|
@ -126,6 +126,7 @@ mod duplicate_mod;
|
|||
mod else_if_without_else;
|
||||
mod empty_drop;
|
||||
mod empty_enum;
|
||||
mod empty_line_after;
|
||||
mod empty_with_brackets;
|
||||
mod endian_bytes;
|
||||
mod entry;
|
||||
|
@ -973,6 +974,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
|
||||
store.register_early_pass(|| Box::new(empty_line_after::EmptyLineAfter::new()));
|
||||
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
|
||||
store.register_late_pass(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default());
|
||||
|
|
|
@ -66,3 +66,19 @@ fn escape_3() {}
|
|||
|
||||
/// Backslashes ` \` within code blocks don't count.
|
||||
fn escape_4() {}
|
||||
|
||||
trait Foo {
|
||||
fn bar();
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
impl Foo for Bar {
|
||||
// NOTE: false positive
|
||||
/// Returns an `Option<Month>` from a i64, assuming a 1-index, January = 1.
|
||||
///
|
||||
/// `Month::from_i64(n: i64)`: | `1` | `2` | ... | `12`
|
||||
/// ---------------------------| -------------------- | --------------------- | ... | -----
|
||||
/// ``: | Some(Month::January) | Some(Month::February) | ... |
|
||||
/// Some(Month::December)
|
||||
fn bar() {}
|
||||
}
|
||||
|
|
|
@ -94,5 +94,17 @@ LL | /// Escaped \` ` backticks don't count, but unescaped backticks do.
|
|||
|
|
||||
= help: a backtick may be missing a pair
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
error: backticks are unbalanced
|
||||
--> tests/ui/doc/unbalanced_ticks.rs:79:9
|
||||
|
|
||||
LL | /// `Month::from_i64(n: i64)`: | `1` | `2` | ... | `12`
|
||||
| _________^
|
||||
LL | | /// ---------------------------| -------------------- | --------------------- | ... | -----
|
||||
LL | | /// ``: | Some(Month::January) | Some(Month::February) | ... |
|
||||
LL | | /// Some(Month::December)
|
||||
| |_____________________________^
|
||||
|
|
||||
= help: a backtick may be missing a pair
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
|
|
|
@ -132,4 +132,13 @@ pub struct BlockComment;
|
|||
))]
|
||||
fn empty_line_in_cfg_attr() {}
|
||||
|
||||
trait Foo {
|
||||
fn bar();
|
||||
}
|
||||
|
||||
impl Foo for LineComment {
|
||||
/// comment on assoc item
|
||||
fn bar() {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -141,4 +141,13 @@ pub struct BlockComment;
|
|||
))]
|
||||
fn empty_line_in_cfg_attr() {}
|
||||
|
||||
trait Foo {
|
||||
fn bar();
|
||||
}
|
||||
|
||||
impl Foo for LineComment {
|
||||
/// comment on assoc item
|
||||
fn bar() {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -144,4 +144,14 @@ pub struct BlockComment;
|
|||
))]
|
||||
fn empty_line_in_cfg_attr() {}
|
||||
|
||||
trait Foo {
|
||||
fn bar();
|
||||
}
|
||||
|
||||
impl Foo for LineComment {
|
||||
/// comment on assoc item
|
||||
|
||||
fn bar() {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -5,11 +5,11 @@ LL | / /// for the crate
|
|||
LL | |
|
||||
| |_^
|
||||
LL | fn first_in_crate() {}
|
||||
| ------------------- the comment documents this function
|
||||
| ----------------- the comment documents this function
|
||||
|
|
||||
= note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
help: if the comment should document the crate use an inner doc comment
|
||||
|
|
||||
LL ~ //! Meant to be an
|
||||
|
@ -24,9 +24,9 @@ LL | / /// for the module
|
|||
LL | |
|
||||
| |_^
|
||||
LL | fn first_in_module() {}
|
||||
| -------------------- the comment documents this function
|
||||
| ------------------ the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
help: if the comment should document the parent module use an inner doc comment
|
||||
|
|
||||
LL ~ //! Meant to be an
|
||||
|
@ -42,9 +42,9 @@ LL | |
|
|||
| |_^
|
||||
LL | /// Blank line
|
||||
LL | fn indented() {}
|
||||
| ------------- the comment documents this function
|
||||
| ----------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
help: if the documentation should include the empty line include it in the comment
|
||||
|
|
||||
LL | ///
|
||||
|
@ -57,9 +57,9 @@ LL | / /// This should produce a warning
|
|||
LL | |
|
||||
| |_^
|
||||
LL | fn with_doc_and_newline() {}
|
||||
| ------------------------- the comment documents this function
|
||||
| ----------------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
|
||||
error: empty lines after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:44:1
|
||||
|
@ -72,9 +72,9 @@ LL | |
|
|||
| |_^
|
||||
...
|
||||
LL | fn three_attributes() {}
|
||||
| --------------------- the comment documents this function
|
||||
| ------------------- the comment documents this function
|
||||
|
|
||||
= help: if the empty lines are unintentional remove them
|
||||
= help: if the empty lines are unintentional, remove them
|
||||
|
||||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:56:5
|
||||
|
@ -84,9 +84,9 @@ LL | | // fn old_code() {}
|
|||
LL | |
|
||||
| |_^
|
||||
LL | fn new_code() {}
|
||||
| ------------- the comment documents this function
|
||||
| ----------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
help: if the doc comment should not document `new_code` comment it out
|
||||
|
|
||||
LL | // /// docs for `old_code`
|
||||
|
@ -106,7 +106,7 @@ LL | |
|
|||
LL | struct Multiple;
|
||||
| --------------- the comment documents this struct
|
||||
|
|
||||
= help: if the empty lines are unintentional remove them
|
||||
= help: if the empty lines are unintentional, remove them
|
||||
help: if the doc comment should not document `Multiple` comment it out
|
||||
|
|
||||
LL ~ // /// Docs
|
||||
|
@ -126,9 +126,9 @@ LL | | */
|
|||
LL | |
|
||||
| |_^
|
||||
LL | fn first_in_module() {}
|
||||
| -------------------- the comment documents this function
|
||||
| ------------------ the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
help: if the comment should document the parent module use an inner doc comment
|
||||
|
|
||||
LL | /*!
|
||||
|
@ -145,9 +145,9 @@ LL | |
|
|||
| |_^
|
||||
...
|
||||
LL | fn new_code() {}
|
||||
| ------------- the comment documents this function
|
||||
| ----------- the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
help: if the doc comment should not document `new_code` comment it out
|
||||
|
|
||||
LL - /**
|
||||
|
@ -163,13 +163,24 @@ LL | |
|
|||
| |_^
|
||||
LL | /// Docs for `new_code2`
|
||||
LL | fn new_code2() {}
|
||||
| -------------- the comment documents this function
|
||||
| ------------ the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
help: if the doc comment should not document `new_code2` comment it out
|
||||
|
|
||||
LL | // /// Docs for `old_code2`
|
||||
| ++
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
error: empty line after doc comment
|
||||
--> tests/ui/empty_line_after/doc_comments.rs:152:5
|
||||
|
|
||||
LL | / /// comment on assoc item
|
||||
LL | |
|
||||
| |_^
|
||||
LL | fn bar() {}
|
||||
| ------ the comment documents this function
|
||||
|
|
||||
= help: if the empty line is unintentional, remove it
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ LL | / #[crate_type = "lib"]
|
|||
LL | |
|
||||
| |_^
|
||||
LL | fn first_in_crate() {}
|
||||
| ------------------- the attribute applies to this function
|
||||
| ----------------- the attribute applies to this function
|
||||
|
|
||||
= note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]`
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
help: if the attribute should apply to the crate use an inner attribute
|
||||
|
|
||||
LL | #![crate_type = "lib"]
|
||||
|
@ -23,9 +23,9 @@ LL | |
|
|||
| |_^
|
||||
LL | /// some comment
|
||||
LL | fn with_one_newline_and_comment() {}
|
||||
| --------------------------------- the attribute applies to this function
|
||||
| ------------------------------- the attribute applies to this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:23:1
|
||||
|
@ -34,9 +34,9 @@ LL | / #[inline]
|
|||
LL | |
|
||||
| |_^
|
||||
LL | fn with_one_newline() {}
|
||||
| --------------------- the attribute applies to this function
|
||||
| ------------------- the attribute applies to this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
|
||||
error: empty lines after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:30:5
|
||||
|
@ -46,9 +46,9 @@ LL | |
|
|||
LL | |
|
||||
| |_^
|
||||
LL | fn with_two_newlines() {}
|
||||
| ---------------------- the attribute applies to this function
|
||||
| -------------------- the attribute applies to this function
|
||||
|
|
||||
= help: if the empty lines are unintentional remove them
|
||||
= help: if the empty lines are unintentional, remove them
|
||||
help: if the attribute should apply to the parent module use an inner attribute
|
||||
|
|
||||
LL | #![crate_type = "lib"]
|
||||
|
@ -63,7 +63,7 @@ LL | |
|
|||
LL | enum Baz {
|
||||
| -------- the attribute applies to this enum
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:45:1
|
||||
|
@ -74,7 +74,7 @@ LL | |
|
|||
LL | struct Foo {
|
||||
| ---------- the attribute applies to this struct
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:53:1
|
||||
|
@ -85,7 +85,7 @@ LL | |
|
|||
LL | mod foo {}
|
||||
| ------- the attribute applies to this module
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:58:1
|
||||
|
@ -95,9 +95,9 @@ LL | | // Still lint cases where the empty line does not immediately follow the
|
|||
LL | |
|
||||
| |_^
|
||||
LL | fn comment_before_empty_line() {}
|
||||
| ------------------------------ the attribute applies to this function
|
||||
| ---------------------------- the attribute applies to this function
|
||||
|
|
||||
= help: if the empty line is unintentional remove it
|
||||
= help: if the empty line is unintentional, remove it
|
||||
|
||||
error: empty lines after outer attribute
|
||||
--> tests/ui/empty_line_after/outer_attribute.rs:64:1
|
||||
|
@ -107,9 +107,9 @@ LL | / #[allow(unused)]
|
|||
LL | |
|
||||
| |_^
|
||||
LL | pub fn isolated_comment() {}
|
||||
| ------------------------- the attribute applies to this function
|
||||
| ----------------------- the attribute applies to this function
|
||||
|
|
||||
= help: if the empty lines are unintentional remove them
|
||||
= help: if the empty lines are unintentional, remove them
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(unused)]
|
||||
#![warn(clippy::suspicious_doc_comments)]
|
||||
#![allow(clippy::empty_line_after_doc_comments)]
|
||||
|
||||
//! Real module documentation.
|
||||
//! Fake module documentation.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(unused)]
|
||||
#![warn(clippy::suspicious_doc_comments)]
|
||||
#![allow(clippy::empty_line_after_doc_comments)]
|
||||
|
||||
//! Real module documentation.
|
||||
///! Fake module documentation.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: this is an outer doc comment and does not apply to the parent module or crate
|
||||
--> tests/ui/suspicious_doc_comments.rs:5:1
|
||||
--> tests/ui/suspicious_doc_comments.rs:6:1
|
||||
|
|
||||
LL | ///! Fake module documentation.
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -12,7 +12,7 @@ LL | //! Fake module documentation.
|
|||
|
|
||||
|
||||
error: this is an outer doc comment and does not apply to the parent module or crate
|
||||
--> tests/ui/suspicious_doc_comments.rs:9:5
|
||||
--> tests/ui/suspicious_doc_comments.rs:10:5
|
||||
|
|
||||
LL | ///! This module contains useful functions.
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -23,7 +23,7 @@ LL | //! This module contains useful functions.
|
|||
|
|
||||
|
||||
error: this is an outer doc comment and does not apply to the parent module or crate
|
||||
--> tests/ui/suspicious_doc_comments.rs:21:5
|
||||
--> tests/ui/suspicious_doc_comments.rs:22:5
|
||||
|
|
||||
LL | / /**! This module contains useful functions.
|
||||
LL | | */
|
||||
|
@ -36,7 +36,7 @@ LL + */
|
|||
|
|
||||
|
||||
error: this is an outer doc comment and does not apply to the parent module or crate
|
||||
--> tests/ui/suspicious_doc_comments.rs:35:5
|
||||
--> tests/ui/suspicious_doc_comments.rs:36:5
|
||||
|
|
||||
LL | / ///! This module
|
||||
LL | | ///! contains
|
||||
|
@ -51,7 +51,7 @@ LL ~ //! useful functions.
|
|||
|
|
||||
|
||||
error: this is an outer doc comment and does not apply to the parent module or crate
|
||||
--> tests/ui/suspicious_doc_comments.rs:43:5
|
||||
--> tests/ui/suspicious_doc_comments.rs:44:5
|
||||
|
|
||||
LL | / ///! a
|
||||
LL | | ///! b
|
||||
|
@ -64,7 +64,7 @@ LL ~ //! b
|
|||
|
|
||||
|
||||
error: this is an outer doc comment and does not apply to the parent module or crate
|
||||
--> tests/ui/suspicious_doc_comments.rs:51:5
|
||||
--> tests/ui/suspicious_doc_comments.rs:52:5
|
||||
|
|
||||
LL | ///! a
|
||||
| ^^^^^^
|
||||
|
@ -75,7 +75,7 @@ LL | //! a
|
|||
|
|
||||
|
||||
error: this is an outer doc comment and does not apply to the parent module or crate
|
||||
--> tests/ui/suspicious_doc_comments.rs:57:5
|
||||
--> tests/ui/suspicious_doc_comments.rs:58:5
|
||||
|
|
||||
LL | / ///! a
|
||||
LL | |
|
||||
|
@ -90,7 +90,7 @@ LL ~ //! b
|
|||
|
|
||||
|
||||
error: this is an outer doc comment and does not apply to the parent module or crate
|
||||
--> tests/ui/suspicious_doc_comments.rs:69:5
|
||||
--> tests/ui/suspicious_doc_comments.rs:70:5
|
||||
|
|
||||
LL | ///! Very cool macro
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -101,7 +101,7 @@ LL | //! Very cool macro
|
|||
|
|
||||
|
||||
error: this is an outer doc comment and does not apply to the parent module or crate
|
||||
--> tests/ui/suspicious_doc_comments.rs:76:5
|
||||
--> tests/ui/suspicious_doc_comments.rs:77:5
|
||||
|
|
||||
LL | ///! Huh.
|
||||
| ^^^^^^^^^
|
||||
|
|
37
tests/codegen/terminating-catchpad.rs
Normal file
37
tests/codegen/terminating-catchpad.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
//@ revisions: emscripten wasi seh
|
||||
//@[emscripten] compile-flags: --target wasm32-unknown-emscripten -Z emscripten-wasm-eh
|
||||
//@[wasi] compile-flags: --target wasm32-wasip1 -C panic=unwind
|
||||
//@[seh] compile-flags: --target x86_64-pc-windows-msvc
|
||||
//@[emscripten] needs-llvm-components: webassembly
|
||||
//@[wasi] needs-llvm-components: webassembly
|
||||
//@[seh] needs-llvm-components: x86
|
||||
|
||||
// Ensure a catch-all generates:
|
||||
// - `catchpad ... [ptr null]` on Wasm (otherwise LLVM gets confused)
|
||||
// - `catchpad ... [ptr null, i32 64, ptr null]` on Windows (otherwise we catch SEH exceptions)
|
||||
|
||||
#![feature(no_core, lang_items, rustc_attrs)]
|
||||
#![crate_type = "lib"]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
|
||||
#[lang = "sized"]
|
||||
trait Sized {}
|
||||
|
||||
unsafe extern "C-unwind" {
|
||||
safe fn unwinds();
|
||||
}
|
||||
|
||||
#[lang = "panic_cannot_unwind"]
|
||||
fn panic_cannot_unwind() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[rustc_nounwind]
|
||||
pub fn doesnt_unwind() {
|
||||
// emscripten: %catchpad = catchpad within %catchswitch [ptr null]
|
||||
// wasi: %catchpad = catchpad within %catchswitch [ptr null]
|
||||
// seh: %catchpad = catchpad within %catchswitch [ptr null, i32 64, ptr null]
|
||||
unwinds();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue